From patchwork Sat Nov 25 00:03:28 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Pedro Alves X-Patchwork-Id: 24520 Received: (qmail 96859 invoked by alias); 25 Nov 2017 00:03:41 -0000 Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org Delivered-To: mailing list gdb-patches@sourceware.org Received: (qmail 96839 invoked by uid 89); 25 Nov 2017 00:03:41 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-24.2 required=5.0 tests=AWL, BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, KB_WAM_FROM_NAME_SINGLEWORD, RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 spammy=completeness, abel, sk:global- X-HELO: mail-wr0-f174.google.com Received: from mail-wr0-f174.google.com (HELO mail-wr0-f174.google.com) (209.85.128.174) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Sat, 25 Nov 2017 00:03:34 +0000 Received: by mail-wr0-f174.google.com with SMTP id l22so21556154wrc.11 for ; Fri, 24 Nov 2017 16:03:33 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:to:from:subject:message-id:date:user-agent :mime-version:content-transfer-encoding; bh=ScN/H2LSjnVlWTV1HJDZN4OfGfV1dWC9RpjR0F110lQ=; b=IAGDA1gTWVKOQ5QsbJR51N+vIwdxt6fmrGwcjcpzepFagqdXZdacrj1pd5uz8ZqvXH D6mJhTriIX4sbboftsW+RNvY1dhDz1287vwPaJE6+fygYdRdfrzI/o/u92dULMPtKhOq cIsPriDXAM7RNY7VpW1b1JJsOPd/LQkg1FQRkrpVopX9Aa1LysAO21X7E1Tod/fIRUUo PxHo60a841IJqx7k3S1gpyN3WMmjoxppw/wsLKM1BkBh+KDa0jPxTsoDoOuOrTiB68/g TXruqXFxb1HzB4XgT2fP2Xq8cv+VEVB4+HnXNGpi6FEQ15Y+gDnJaLa1W2JuuuwnIPUD 1nww== X-Gm-Message-State: AJaThX4+yvv74hSgTW0/Busg0lp2x4BwpbP34eIXsembVUxmUpw7LWJ2 1H8JGslHq8b2cq9Bgo9SI1p/JHNAXy8= X-Google-Smtp-Source: AGs4zMYGC4C15V4kcb+ZW8fky1M36GSwPGXBHsg7q2Kua8IQXR3vNpryIXU5baHe0JGuRz6L2teCmQ== X-Received: by 10.223.178.130 with SMTP id g2mr25074765wrd.129.1511568210952; Fri, 24 Nov 2017 16:03:30 -0800 (PST) Received: from [192.168.0.101] (bl20-178-16.dsl.telepac.pt. [2.81.178.16]) by smtp.gmail.com with ESMTPSA id r15sm15031137wrc.36.2017.11.24.16.03.28 for (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 24 Nov 2017 16:03:29 -0800 (PST) To: GDB Patches From: Pedro Alves Subject: [pushed] Comprehensive C++ linespec/completer tests Message-ID: <1a54f645-10b9-f4a6-abbc-151e840a09a1@redhat.com> Date: Sat, 25 Nov 2017 00:03:28 +0000 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Thunderbird/45.4.0 MIME-Version: 1.0 Now that the "Make strcmp_iw NOT ignore whitespace in the middle of tokens" patch is in, most of the tests that I had in the "C++ debugging improvements: breakpoints, TAB completion, more" series, in this patch here: https://sourceware.org/ml/gdb-patches/2017-06/msg00027.html started working. I removed the ones that don't yet (the ones that rely on C++ wildmatching), and after addressing Keith's review comments, at I went ahead and checked it in, as below. From 8955eb2da31d78690c762748fab3a16832ef1f81 Mon Sep 17 00:00:00 2001 From: Pedro Alves Date: Fri, 24 Nov 2017 23:41:12 +0000 Subject: [PATCH] Comprehensive C++ linespec/completer tests Exercises all sorts of aspects fixed by previous patches, going back a few months. - Exercises label completion, linespecs and explicit locations. - Exercises both quoting vs non-quoting, source filenames, function names, labels, with both linespecs and explicit locations. - Tests corner cases around not-quoting function names, and whitespace and/and completing inside a parameter or template argument list, anonymous namespace awareness, etc. E.g., "break foo<[TAB]" -> "break foo()" "break bar ( int[TAB]" -> "break bar ( int) "break ( anon" -> "break ( anonymous namespace)::func()" "b cfunc() [tab]" -> "b cfunc() const" "b rettype templfunc[tab]" -> "b rettype templfunc()" ... and others. - Tests the "b source.c[TAB] -> b source.cc:" feature. I.e., colon auto-appending. - Exercises corner cases around C++ "operator<" / "operator<<". (Much more extensive C++ operator completion/linespec handling in a separate patch.) - Exercises both tab completion and "complete" command completion, using routines that handle it automatically, to ensure no test forgets either mode. - Many of the completion tests test completion at at prefix of a given tricky name, to make sure all corner cases are covered. E.g., completing before, at and after ":", "(", "<". - Exercises "keyword" completion. I.e., "b function() [TAB]" displaying "if task thread" as completion match list. Likewise for display explicit location options matches at the appropriate points. - Ensures that the completer finds the same breakpoint locations that setting a breakpoint finds. - Tests that linespec/location completion doesn't find data symbols. - Tests that expression completion still kicks in after a linespec/location keyword. I.e., this: "b function () if global1 + global[TAB]" knows that after "if", you're completing on an expression, and thus breaks words after "if" as an expression and matches on "global" as a data symbol. - Adds common routines to help with all the above, to be used by multiple completion and linespec/location test cases. - More... Grows the gdb.linespec/ tests like this: -# of expected passes 573 +# of expected passes 3464 gdb/testsuite/ChangeLog: 2017-11-24 Pedro Alves * gdb.linespec/cpcompletion.exp: New file. * gdb.linespec/cpls-hyphen.cc: New file. * gdb.linespec/cpls.cc: New file. * gdb.linespec/cpls2.cc: New file. * gdb.linespec/explicit.exp: Load completion-support.exp. Adjust test to use test_gdb_complete_unique. Add label completion, keyword completion and explicit location completion tests. * lib/completion-support.exp: New file. --- gdb/testsuite/ChangeLog | 11 + gdb/testsuite/gdb.linespec/cpcompletion.exp | 534 ++++++++++++++++++++++++++++ gdb/testsuite/gdb.linespec/cpls-hyphen.cc | 33 ++ gdb/testsuite/gdb.linespec/cpls.cc | 386 ++++++++++++++++++++ gdb/testsuite/gdb.linespec/cpls2.cc | 46 +++ gdb/testsuite/gdb.linespec/explicit.exp | 208 ++++++++++- gdb/testsuite/lib/completion-support.exp | 471 ++++++++++++++++++++++++ 7 files changed, 1682 insertions(+), 7 deletions(-) create mode 100644 gdb/testsuite/gdb.linespec/cpcompletion.exp create mode 100644 gdb/testsuite/gdb.linespec/cpls-hyphen.cc create mode 100644 gdb/testsuite/gdb.linespec/cpls.cc create mode 100644 gdb/testsuite/gdb.linespec/cpls2.cc create mode 100644 gdb/testsuite/lib/completion-support.exp diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 8f194c4..469dde1 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,14 @@ +2017-11-24 Pedro Alves + + * gdb.linespec/cpcompletion.exp: New file. + * gdb.linespec/cpls-hyphen.cc: New file. + * gdb.linespec/cpls.cc: New file. + * gdb.linespec/cpls2.cc: New file. + * gdb.linespec/explicit.exp: Load completion-support.exp. Adjust + test to use test_gdb_complete_unique. Add label completion, + keyword completion and explicit location completion tests. + * lib/completion-support.exp: New file. + 2017-11-24 Joel Brobecker * gdb.ada/catch_ex.exp, gdb.ada/mi_catch_ex.exp, diff --git a/gdb/testsuite/gdb.linespec/cpcompletion.exp b/gdb/testsuite/gdb.linespec/cpcompletion.exp new file mode 100644 index 0000000..873dc78 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpcompletion.exp @@ -0,0 +1,534 @@ +# Copyright 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 . + +# This file is part of the gdb testsuite. + +load_lib completion-support.exp + +standard_testfile cpls.cc cpls2.cc cpls-hyphen.cc + +if {[prepare_for_testing "failed to prepare" $testfile \ + [list $srcfile $srcfile2 $srcfile3] {debug}]} { + return -1 +} + +# 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 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 + } +} + +# 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 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/task/thread) matches in linespec mode, and also the explicit +# location options in explicit locations mode. + +proc_with_prefix keywords-after-function {} { + set explicit_list \ + [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 \ + [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 + 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 + operator< + function-labels + keywords-after-function + keywords-after-label + keywords-after-unknown-file + no-data-symbols + if-expression +} + +test_driver diff --git a/gdb/testsuite/gdb.linespec/cpls-hyphen.cc b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc new file mode 100644 index 0000000..4bd2d9f --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpls-hyphen.cc @@ -0,0 +1,33 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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 . */ + +/* A function in a file whose name has an hyphen. */ + +int +ns_hyphen_function (int i) +{ + if (i > 0) + { + label1: + return i + 20; + } + else + { + label2: + return i + 10; + } +} diff --git a/gdb/testsuite/gdb.linespec/cpls.cc b/gdb/testsuite/gdb.linespec/cpls.cc new file mode 100644 index 0000000..776dd4c --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpls.cc @@ -0,0 +1,386 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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 . */ + +/* Code for the all-param-prefixes test. */ + +void +param_prefixes_test_long (long) +{} + +void +param_prefixes_test_intp_intr (int *, int&) +{} + +/* Code for the overload test. */ + +void +overload_ambiguous_test (long) +{} + +void +overload_ambiguous_test (int, int) +{} + +void +overload_ambiguous_test (int, long) +{} + +/* Code for the overload-2 test. */ + +/* Generate functions/methods all with the same name, in different + scopes, but all with different parameters. */ + +struct overload2_arg1 {}; +struct overload2_arg2 {}; +struct overload2_arg3 {}; +struct overload2_arg4 {}; +struct overload2_arg5 {}; +struct overload2_arg6 {}; +struct overload2_arg7 {}; +struct overload2_arg8 {}; +struct overload2_arg9 {}; +struct overload2_arga {}; + +#define GEN_OVERLOAD2_FUNCTIONS(ARG1, ARG2) \ + void \ + overload2_function (ARG1) \ + {} \ + \ + struct struct_overload2_test \ + { \ + void overload2_function (ARG2); \ + }; \ + \ + void \ + struct_overload2_test::overload2_function (ARG2) \ + {} + +/* In the global namespace. */ +GEN_OVERLOAD2_FUNCTIONS( overload2_arg1, overload2_arg2) + +namespace +{ + /* In an anonymous namespace. */ + GEN_OVERLOAD2_FUNCTIONS (overload2_arg3, overload2_arg4) +} + +namespace ns_overload2_test +{ + /* In a namespace. */ + GEN_OVERLOAD2_FUNCTIONS (overload2_arg5, overload2_arg6) + + namespace + { + /* In a nested anonymous namespace. */ + GEN_OVERLOAD2_FUNCTIONS (overload2_arg7, overload2_arg8) + + namespace ns_overload2_test + { + /* In a nested namespace. */ + GEN_OVERLOAD2_FUNCTIONS (overload2_arg9, overload2_arga) + } + } +} + +/* Code for the overload-3 test. */ + +#define GEN_OVERLOAD3_FUNCTIONS(ARG1, ARG2) \ + void \ + overload3_function (ARG1) \ + {} \ + void \ + overload3_function (ARG2) \ + {} \ + \ + struct struct_overload3_test \ + { \ + void overload3_function (ARG1); \ + void overload3_function (ARG2); \ + }; \ + \ + void \ + struct_overload3_test::overload3_function (ARG1) \ + {} \ + void \ + struct_overload3_test::overload3_function (ARG2) \ + {} + +/* In the global namespace. */ +GEN_OVERLOAD3_FUNCTIONS (int, long) + +namespace +{ + /* In an anonymous namespace. */ + GEN_OVERLOAD3_FUNCTIONS (int, long) +} + +namespace ns_overload3_test +{ + /* In a namespace. */ + GEN_OVERLOAD3_FUNCTIONS (int, long) + + namespace + { + /* In a nested anonymous namespace. */ + GEN_OVERLOAD3_FUNCTIONS (int, long) + + namespace ns_overload3_test + { + /* In a nested namespace. */ + GEN_OVERLOAD3_FUNCTIONS (int, long) + } + } +} + +/* Code for the template-overload tests. */ + +template +struct template_struct +{ + T template_overload_fn (T); +}; + +template +T template_struct::template_overload_fn (T t) +{ + return t; +} + +template_struct template_struct_int; +template_struct template_struct_long; + +/* Code for the template2-ret-type tests. */ + +template +struct template2_ret_type {}; + +template +struct template2_struct +{ + template + T template2_fn (T = T (), T2 t2 = T2 (), T3 t3 = T3 ()); +}; + +template +template +T template2_struct::template2_fn (T t, T2 t2, T3 t3) +{ + return T (); +} + +template2_struct > template2_struct_inst; + +/* Code for the const-overload tests. */ + +struct struct_with_const_overload +{ + void const_overload_fn (); + void const_overload_fn () const; +}; + +void +struct_with_const_overload::const_overload_fn () +{} + +void +struct_with_const_overload::const_overload_fn () const +{} + +void +not_overloaded_fn () +{} + +/* Code for the incomplete-scope-colon tests. */ + +struct struct_incomplete_scope_colon_test +{ + void incomplete_scope_colon_test (); +}; + +void +struct_incomplete_scope_colon_test::incomplete_scope_colon_test () +{} + +namespace ns_incomplete_scope_colon_test +{ + void incomplete_scope_colon_test () {} +} + +namespace ns2_incomplete_scope_colon_test +{ + struct struct_in_ns2_incomplete_scope_colon_test + { + void incomplete_scope_colon_test (); + }; + + void + struct_in_ns2_incomplete_scope_colon_test::incomplete_scope_colon_test () + {} +} + +/* Code for the anon-ns tests. */ + +namespace +{ + void anon_ns_function () + {} + + struct anon_ns_struct + { + void anon_ns_function (); + }; + + void + anon_ns_struct::anon_ns_function () + {} +} + +namespace the_anon_ns_wrapper_ns +{ + +namespace +{ + void anon_ns_function () + {} + + struct anon_ns_struct + { + void anon_ns_function (); + }; + + void + anon_ns_struct::anon_ns_function () + {} +} + +} /* the_anon_ns_wrapper_ns */ + +/* Code for the global-ns-scope-op tests. */ + +void global_ns_scope_op_function () +{ +} + +/* Add a function with the same name to a namespace. We want to test + that "b ::global_ns_function" does NOT select it. */ +namespace the_global_ns_scope_op_ns +{ + void global_ns_scope_op_function () + { + } +} + +/* Code for the ambiguous-prefix tests. */ + +/* Create a few functions/methods with the same "ambiguous_prefix_" + prefix. They in different scopes, but "b ambiguous_prefix_" + should list them all, and figure out the LCD is + ambiguous_prefix_. */ + +void ambiguous_prefix_global_func () +{ +} + +namespace the_ambiguous_prefix_ns +{ + void ambiguous_prefix_ns_func () + { + } +} + +struct the_ambiguous_prefix_struct +{ + void ambiguous_prefix_method (); +}; + +void +the_ambiguous_prefix_struct::ambiguous_prefix_method () +{ +} + +/* Code for the function-labels test. */ + +int +function_with_labels (int i) +{ + if (i > 0) + { + label1: + return i + 20; + } + else + { + label2: + return i + 10; + } +} + +/* Code for the no-data-symbols and if-expression tests. */ + +int code_data = 0; + +int another_data = 0; + +/* A function that has a same "code" prefix as the global above. We + want to ensure that completing on "b code" doesn't offer the data + symbol. */ +void +code_function () +{ +} + +/* Code for the operator< tests. */ + +enum foo_enum + { + foo_value + }; + +bool operator<(foo_enum lhs, foo_enum rhs) +{ + label1: + return false; +} + +/* Code for the in-source-file-unconstrained / + in-source-file-ambiguous tests. */ + +int +file_constrained_test_cpls_function (int i) +{ + if (i > 0) + { + label1: + return i + 20; + } + else + { + label2: + return i + 10; + } +} + + +int +main () +{ + template2_struct_inst.template2_fn (); + template_struct_int.template_overload_fn(0); + template_struct_long.template_overload_fn(0); + + return 0; +} diff --git a/gdb/testsuite/gdb.linespec/cpls2.cc b/gdb/testsuite/gdb.linespec/cpls2.cc new file mode 100644 index 0000000..a8f1319 --- /dev/null +++ b/gdb/testsuite/gdb.linespec/cpls2.cc @@ -0,0 +1,46 @@ +/* This testcase is part of GDB, the GNU debugger. + + Copyright 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 . */ + +int +file_constrained_test_cpls2_function (int i) +{ + if (i > 0) + { + label1: + return i + 20; + } + else + { + label2: + return i + 10; + } +} + +int +another_file_constrained_test_cpls2_function (int i) +{ + if (i > 0) + { + label1: + return i + 20; + } + else + { + label2: + return i + 10; + } +} diff --git a/gdb/testsuite/gdb.linespec/explicit.exp b/gdb/testsuite/gdb.linespec/explicit.exp index 65d78ca..9cf0162 100644 --- a/gdb/testsuite/gdb.linespec/explicit.exp +++ b/gdb/testsuite/gdb.linespec/explicit.exp @@ -15,6 +15,8 @@ # Tests for explicit locations +load_lib completion-support.exp + standard_testfile explicit.c explicit2.c 3explicit.c set exefile $testfile @@ -222,13 +224,14 @@ namespace eval $testfile { } } - set tst "complete unique file name" - send_gdb "break -source 3ex\t" - gdb_test_multiple "" $tst { - -re "break -source 3explicit.c " { - send_gdb "\n" - gdb_test "" \ - {Source filename requires function, label, or line offset.} $tst + with_test_prefix "complete unique file name" { + foreach qc $completion::maybe_quoted_list { + set cmd "break -source ${qc}3explicit.c${qc}" + test_gdb_complete_unique \ + "break -source ${qc}3ex" \ + $cmd + gdb_test $cmd \ + {Source filename requires function, label, or line offset.} } } @@ -326,10 +329,201 @@ namespace eval $testfile { } } + with_test_prefix "complete unique label name" { + foreach qc $completion::maybe_quoted_list { + test_gdb_complete_unique \ + "break -function myfunction -label ${qc}to" \ + "break -function myfunction -label ${qc}top${qc}" + } + } + + with_test_prefix "complete unique label name with source file" { + test_gdb_complete_unique \ + "break -source explicit.c -function myfunction -label to" \ + "break -source explicit.c -function myfunction -label top" + } + + with_test_prefix "complete unique label name reversed" { + test_gdb_complete_multiple "b -label top -function " "myfunction" "" { + "myfunction" + "myfunction2" + "myfunction3" + "myfunction4" + } + } + + with_test_prefix "complete non-unique label name" { + test_gdb_complete_multiple "b -function myfunction -label " "" "" { + "done" + "top" + } + } + + # The program is stopped at myfunction, so gdb is able to + # infer the label's function. + with_test_prefix "complete label name with no function" { + test_gdb_complete_unique \ + "break -label to" \ + "break -label top" + check_bp_locations_match_list \ + "break -label top" { + "-function myfunction -label top" + } + } + + # See above. + with_test_prefix "complete label name with source file but no function" { + test_gdb_complete_unique \ + "break -source explicit.c -label to" \ + "break -source explicit.c -label top" + check_bp_locations_match_list \ + "break -source explicit.c -label top" { + "-source explicit.c -function myfunction -label top" + } + } + + with_test_prefix "complete label name with wrong source file" { + test_gdb_complete_none \ + "break -source explicit2.c -function myfunction -label to" + check_setting_bp_fails \ + "break -source explicit2.c -function myfunction -label top" + } + + # Get rid of symbols from shared libraries, otherwise + # "b -source thr" could find some system library's + # source. + gdb_test_no_output "nosharedlibrary" + + # Test that after a seemingly finished option argument, + # completion matches both the explicit location options and + # the linespec keywords. + set completions_list { + "-function" + "-label" + "-line" + "-source" + "if" + "task" + "thread" + } + foreach what { "-function" "-label" "-line" "-source" } { + with_test_prefix "complete after $what" { + if {$what != "-line"} { + set w "$what argument " + test_gdb_complete_multiple \ + "b $w" "" "" $completions_list + test_gdb_complete_unique \ + "b $w thr" \ + "b $w thread" + test_gdb_complete_unique \ + "b $w -fun" \ + "b $w -function" + } else { + # After -line, we expect a number / offset. + foreach line {"10" "+10" "-10"} { + set w "-line $line" + test_gdb_complete_multiple \ + "b $w " "" "" $completions_list + test_gdb_complete_unique \ + "b $w thr" \ + "b $w thread" + test_gdb_complete_unique \ + "b $w -fun" \ + "b $w -function" + } + + # With an invalid -line argument, we don't get any + # completions. + test_gdb_complete_none "b -line argument " + } + + # Don't complete a linespec keyword ("thread") or + # another option name when expecting an option + # argument. + test_gdb_complete_none "b $what thr" + test_gdb_complete_none "b $what -fun" + } + } + + + # Tests that ensure that after "if" we complete on expressions + # are in cpcompletion.exp. + + # Disable the completion limit for the rest of the testcase. + gdb_test_no_output "set max-completions unlimited" + + # Get rid of symbols from shared libraries, otherwise the + # completions match list for "break " is huge and makes + # the test below quite long while the gdb_test_multiple loop + # below consumes the matches. Not doing this added ~20 + # seconds at the time of writing. (Actually, already done above.) + # gdb_test_no_output "nosharedlibrary" + + # Test completion with no input argument. We should see all + # the options, plus all the functions. To keep it simple, as + # proxy, we check for presence of one explicit location + # option, one probe location, and one function. + set saw_opt_function 0 + set saw_opt_probe_stap 0 + set saw_function 0 + + set tst "complete with no arguments" + send_gdb "break \t" + gdb_test_multiple "" $tst { + "break \\\x07" { + send_gdb "\t\t" + gdb_test_multiple "" $tst { + "Display all" { + send_gdb "y" + exp_continue + } + -re "-function" { + set saw_opt_function 1 + exp_continue + } + -re "-probe-stap" { + set saw_opt_probe_stap 1 + exp_continue + } + -re "myfunction4" { + set saw_function 1 + exp_continue + } + -re "\r\n$gdb_prompt " { + gdb_assert {$saw_opt_function && $saw_opt_probe_stap && $saw_function} $tst + } + -re " " { + exp_continue + } + } + } + } + clear_input_line $tst + # NOTE: We don't bother testing more elaborate combinations of options, # such as "-func main -sour 3ex\t" (main is defined in explicit.c). # The completer cannot handle these yet. + # The following completion tests require having no symbols + # loaded. + gdb_exit + gdb_start + + # The match list you get when you complete with no options + # specified at all. + set completion_list { + "-function" + "-label" + "-line" + "-probe" + "-probe-dtrace" + "-probe-stap" + "-source" + } + with_test_prefix "complete with no arguments and no symbols" { + test_gdb_complete_multiple "b " "" "-" $completion_list + test_gdb_complete_multiple "b " "-" "" $completion_list + } } # End of completion tests. diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp new file mode 100644 index 0000000..70e6aec --- /dev/null +++ b/gdb/testsuite/lib/completion-support.exp @@ -0,0 +1,471 @@ +# Copyright 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 . + +# 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 {"if" "task" "thread"} + + variable explicit_opts_list \ + {"-function" "-label" "-line" "-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" + gdb_test_multiple "" "$test" { + -re "^$complete_line_re$append_char_re$" { + pass "$test" + } + } + + clear_input_line $test +} + +# Test that completing INPUT_LINE with TAB completes to "INPUT_LINE + +# ADD_COMPLETED_LINE" and that it displays the completion matches in +# COMPLETION_LIST. + +proc test_gdb_complete_tab_multiple { input_line add_completed_line \ + completion_list } { + 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] + + 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" + } + gdb_test_multiple "" "$test (second tab)" { + -re "$expected_re\r\n$gdb_prompt $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. + +proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list start_quote_char end_quote_char } { + global gdb_prompt + + set expected_re [make_cmd_completion_list_re $cmd_prefix $completion_list $start_quote_char $end_quote_char] + 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 } { + 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] + test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re + + # Trim INPUT_LINE and COMPLETE LINE, for the case we're completing + # a command with leading whitespace. Leading command whitespace + # is discarded by GDB. + set input_line [string trimleft $input_line] + 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 $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 +} + +# 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. + +proc test_gdb_complete_multiple { cmd_prefix completion_word add_completed_line completion_list {start_quote_char ""} {end_quote_char ""}} { + test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list + test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char +} + +# 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}} { + if {$end == -1} { + set end [string length $completion] + } + + for {set i $start} {$i < $end} {incr i} { + set line [string range $completion 0 $i] + test_gdb_complete_unique "$line" "$completion" + } +} + +# 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 + } + } +}