* Re: RFC: bash completion
2018-04-24 16:49 ` David Malcolm
@ 2018-04-25 13:13 ` Martin Liška
2018-04-25 13:53 ` Martin Liška
1 sibling, 0 replies; 9+ messages in thread
From: Martin Liška @ 2018-04-25 13:13 UTC (permalink / raw)
To: David Malcolm, GCC Development
[-- Attachment #1: Type: text/plain, Size: 4619 bytes --]
On 04/24/2018 06:27 PM, David Malcolm wrote:
> On Tue, 2018-04-24 at 16:45 +0200, Martin Liška wrote:
>> Hi.
>>
>> Some time ago, I investigated quite new feature of clang which
>> is support of --autocomplete argument. That can be run from bash
>> completion
>> script and one gets more precise completion hints:
>>
>> http://blog.llvm.org/2017/09/clang-bash-better-auto-completion-is.htm
>> l
>> https://www.youtube.com/watch?v=zLPwPdZBpSY
>>
>> I like the idea and I would describe how is that better to current
>> GCC completion
>> script sitting here:
>> https://github.com/scop/bash-completion/blob/master/completions/gcc
>>
>> 1) gcc -fsanitize=^ - no support for option enum values
>> 2) gcc -fno-sa^ - no support for negative options
>> 3) gcc --param=^ - no support for param names
>>
>> These are main limitations I see. I'm attaching working prototype,
>> which you
>> can test by installed GCC, setting it on $PATH and doing:
>> $ source gcc.sh
>>
>> Then bash completion is provided via the newly added option. Some
>> examples:
>>
>> 1)
>> $ gcc -fsanitize=
>> address bounds enum
>> integer-divide-by-zero nonnull-
>> attribute pointer-
>> compare return shift-
>> base thread vla-bound
>> alignment bounds-strict float-cast-
>> overflow kernel-
>> address null pointer-
>> overflow returns-nonnull-attribute shift-
>> exponent undefined vptr
>> bool builtin float-divide-
>> by-zero leak object-
>> size pointer-
>> subtract shift signed-integer-
>> overflow unreachable
>>
>> 2)
>> $ gcc -fno-ipa-
>> -fno-ipa-bit-cp -fno-ipa-cp-alignment -fno-ipa-
>> icf -fno-ipa-icf-variables -fno-ipa-profile -fno-
>> ipa-pure-const -fno-ipa-reference -fno-ipa-struct-reorg
>> -fno-ipa-cp -fno-ipa-cp-clone -fno-ipa-icf-
>> functions -fno-ipa-matrix-reorg -fno-ipa-pta -fno-ipa-
>> ra -fno-ipa-sra -fno-ipa-vrp
>>
>> 3)
>> $ gcc --param=lto-
>> lto-max-partition lto-min-partition lto-partitions
>>
>> 4)
>> gcc --param lto-
>> lto-max-partition lto-min-partition lto-partitions
>>
>> The patch is quite lean and if people like, I will prepare a proper
>> patch submission. I know about some limitations that can be then
>> handled incrementally.
>>
>> Thoughts?
>> Martin
>
> Overall, looks good (albeit with various nits). I like how you're
> reusing the m_option_suggestions machinery from the misspelled options
> code. There are some awkward issues e.g. arch-specific completions,
> lang-specific completions, custom option-handling etc, but given that
> as-is this patch seems to be an improvement over the status quo, I'd
> prefer to tackle those later.
I'm sending second version of the patch. I did isolation of m_option_suggestions machinery
to a separate file. Mainly due to selftests that are linked with cc1.
>
> The patch doesn't have tests. There would need to be some way to
> achieve test coverage for the completion code (especially as we start
> to tackle the more interesting cases). I wonder what the best way to
> do that is; perhaps a combination of selftest and DejaGnu? (e.g. what
> about arch-specific completions? what about the interaction with bash?
> etc)
For now I come up with quite some selftests. Integration with bash&DejaGNU
would be challenging.
>
> A few nits:
> * Do we want to hardcode that logging path in gcc.sh?
Sure, that needs to be purged. Crucial question here is where the gcc.sh script
should live. Note that clang has it in: ./tools/clang/utils/bash-autocomplete.sh
and:
head -n1 ./tools/clang/utils/bash-autocomplete.sh
# Please add "source /path/to/bash-autocomplete.sh" to your .bashrc to use this.
Which is not ideal. I would prefer to integrate the script into:
https://github.com/scop/bash-completion/blob/master/completions/gcc
Thoughts?
>
> * Looks like m_option_suggestions isn't needed for handling the "-
> param" case, so maybe put the param-handling case before the "Lazily
> populate m_option_suggestions" code.
>
> * You use "l" ("ell") as a variable name in two places, which I don't
> like, as IMHO it's too close to "1" (one) in some fonts.
Fixed both notes.
Thanks for fast review.
Martin
>
> Thanks
> Dave
>
[-- Attachment #2: 0001-Come-up-with-new-completion-option.patch --]
[-- Type: text/x-patch, Size: 30678 bytes --]
From 80d34e28ac6db6e63a0efe7d389bd9bfde40e434 Mon Sep 17 00:00:00 2001
From: marxin <mliska@suse.cz>
Date: Fri, 23 Feb 2018 12:28:43 +0100
Subject: [PATCH] Come up with new --completion option.
---
gcc.sh | 51 ++++++
gcc/Makefile.in | 2 +-
gcc/c-family/cppspec.c | 1 +
gcc/common.opt | 4 +
gcc/fortran/gfortranspec.c | 1 +
gcc/gcc-main.c | 1 +
gcc/gcc.c | 127 ++------------
gcc/gcc.h | 4 +-
gcc/opt-proposer.c | 420 +++++++++++++++++++++++++++++++++++++++++++++
gcc/opt-proposer.h | 84 +++++++++
gcc/opts.c | 3 +
gcc/selftest-run-tests.c | 1 +
gcc/selftest.c | 33 ++++
gcc/selftest.h | 21 +++
14 files changed, 638 insertions(+), 115 deletions(-)
create mode 100644 gcc.sh
create mode 100644 gcc/opt-proposer.c
create mode 100644 gcc/opt-proposer.h
diff --git a/gcc.sh b/gcc.sh
new file mode 100644
index 00000000000..06b16b3152b
--- /dev/null
+++ b/gcc.sh
@@ -0,0 +1,51 @@
+# Please add "source /path/to/bash-autocomplete.sh" to your .bashrc to use this.
+
+log()
+{
+ echo $1 >> /tmp/bash-completion.log
+}
+
+_gcc()
+{
+ local cur prev prev2 words cword argument prefix
+ _init_completion || return
+ _expand || return
+
+ # extract also for situations like: -fsanitize=add
+ if [[ $cword > 2 ]]; then
+ prev2="${COMP_WORDS[$cword - 2]}"
+ fi
+
+ log "cur: '$cur', prev: '$prev': prev2: '$prev2' cword: '$cword'"
+
+ # sample: -fsan
+ if [[ "$cur" == -* ]]; then
+ argument=$cur
+ # sample: -fsanitize=
+ elif [[ "$cur" == "=" && $prev == -* ]]; then
+ argument=$prev$cur
+ prefix=$prev$cur
+ # sample: -fsanitize=add
+ elif [[ "$prev" == "=" && $prev2 == -* ]]; then
+ argument=$prev2$prev$cur
+ prefix=$prev2$prev
+ # sample: --param lto-
+ elif [[ "$prev" == "--param" ]]; then
+ argument="$prev $cur"
+ prefix="$prev "
+ fi
+
+ log "argument: '$argument', prefix: '$prefix'"
+
+ if [[ "$argument" == "" ]]; then
+ _filedir
+ else
+ # In situation like '-fsanitize=add' $cur is equal to last token.
+ # Thus we need to strip the beginning of suggested option.
+ flags=$( gcc --completion="$argument" 2>/dev/null | sed "s/^$prefix//")
+ log "compgen: $flags"
+ [[ "${flags: -1}" == '=' ]] && compopt -o nospace 2> /dev/null
+ COMPREPLY=( $( compgen -W "$flags" -- "") )
+ fi
+}
+complete -F _gcc gcc
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 20bee0494b1..26fa3dd17df 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1617,7 +1617,7 @@ OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \
# compiler and containing target-dependent code.
OBJS-libcommon-target = $(common_out_object_file) prefix.o params.o \
opts.o opts-common.o options.o vec.o hooks.o common/common-targhooks.o \
- hash-table.o file-find.o spellcheck.o selftest.o
+ hash-table.o file-find.o spellcheck.o selftest.o opt-proposer.o
# This lists all host objects for the front ends.
ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANGUAGES),$($(v)_OBJS))
diff --git a/gcc/c-family/cppspec.c b/gcc/c-family/cppspec.c
index 1e0a8bcd294..794b3ced529 100644
--- a/gcc/c-family/cppspec.c
+++ b/gcc/c-family/cppspec.c
@@ -21,6 +21,7 @@ along with GCC; see the file COPYING3. If not see
#include "system.h"
#include "coretypes.h"
#include "tm.h"
+#include "opt-proposer.h"
#include "gcc.h"
#include "opts.h"
diff --git a/gcc/common.opt b/gcc/common.opt
index d6ef85928f3..9b4ba28f287 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -254,6 +254,10 @@ Driver Alias(S)
-compile
Driver Alias(c)
+-completion=
+Common Driver Joined Undocumented
+--param Bash completion.
+
-coverage
Driver Alias(coverage)
diff --git a/gcc/fortran/gfortranspec.c b/gcc/fortran/gfortranspec.c
index fe1ec0447b3..b95c8810d0f 100644
--- a/gcc/fortran/gfortranspec.c
+++ b/gcc/fortran/gfortranspec.c
@@ -47,6 +47,7 @@ along with GCC; see the file COPYING3. If not see
#include "config.h"
#include "system.h"
#include "coretypes.h"
+#include "opt-proposer.h"
#include "gcc.h"
#include "opts.h"
diff --git a/gcc/gcc-main.c b/gcc/gcc-main.c
index 9e6de743adc..3cfdfdc57fa 100644
--- a/gcc/gcc-main.c
+++ b/gcc/gcc-main.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "obstack.h"
#include "intl.h"
#include "prefix.h"
+#include "opt-proposer.h"
#include "gcc.h"
/* Implement the top-level "main" within the driver in terms of
diff --git a/gcc/gcc.c b/gcc/gcc.c
index a716f708259..e9207bb9823 100644
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -36,6 +36,7 @@ compilation is specified by a string called a "spec". */
#include "obstack.h"
#include "intl.h"
#include "prefix.h"
+#include "opt-proposer.h"
#include "gcc.h"
#include "diagnostic.h"
#include "flags.h"
@@ -220,6 +221,8 @@ static int print_help_list;
static int print_version;
+static const char *completion = NULL;
+
/* Flag indicating whether we should ONLY print the command and
arguments (like verbose_flag) without executing the command.
Displayed arguments are quoted so that the generated command
@@ -3818,6 +3821,11 @@ driver_handle_option (struct gcc_options *opts,
add_linker_option ("--version", strlen ("--version"));
break;
+ case OPT__completion_:
+ validated = true;
+ completion = decoded->arg;
+ break;
+
case OPT__help:
print_help_list = 1;
@@ -7262,8 +7270,7 @@ compare_files (char *cmpfile[])
driver::driver (bool can_finalize, bool debug) :
explicit_link_files (NULL),
- decoded_options (NULL),
- m_option_suggestions (NULL)
+ decoded_options (NULL)
{
env.init (can_finalize, debug);
}
@@ -7272,14 +7279,6 @@ driver::~driver ()
{
XDELETEVEC (explicit_link_files);
XDELETEVEC (decoded_options);
- if (m_option_suggestions)
- {
- int i;
- char *str;
- FOR_EACH_VEC_ELT (*m_option_suggestions, i, str)
- free (str);
- delete m_option_suggestions;
- }
}
/* driver::main is implemented as a series of driver:: method calls. */
@@ -7300,6 +7299,12 @@ driver::main (int argc, char **argv)
maybe_putenv_OFFLOAD_TARGETS ();
handle_unrecognized_options ();
+ if (completion)
+ {
+ m_option_proposer.suggest_completion (completion);
+ return 0;
+ }
+
if (!maybe_print_and_exit ())
return 0;
@@ -7768,106 +7773,6 @@ driver::maybe_putenv_OFFLOAD_TARGETS () const
offload_targets = NULL;
}
-/* Helper function for driver::suggest_option. Populate
- m_option_suggestions with candidate strings for misspelled options.
- The strings will be freed by the driver's dtor. */
-
-void
-driver::build_option_suggestions (void)
-{
- gcc_assert (m_option_suggestions == NULL);
- m_option_suggestions = new auto_vec <char *> ();
-
- /* We build a vec of m_option_suggestions, using add_misspelling_candidates
- to add copies of strings, without a leading dash. */
-
- for (unsigned int i = 0; i < cl_options_count; i++)
- {
- const struct cl_option *option = &cl_options[i];
- const char *opt_text = option->opt_text;
- switch (i)
- {
- default:
- if (option->var_type == CLVC_ENUM)
- {
- const struct cl_enum *e = &cl_enums[option->var_enum];
- for (unsigned j = 0; e->values[j].arg != NULL; j++)
- {
- char *with_arg = concat (opt_text, e->values[j].arg, NULL);
- add_misspelling_candidates (m_option_suggestions, option,
- with_arg);
- free (with_arg);
- }
- }
- else
- add_misspelling_candidates (m_option_suggestions, option,
- opt_text);
- break;
-
- case OPT_fsanitize_:
- case OPT_fsanitize_recover_:
- /* -fsanitize= and -fsanitize-recover= can take
- a comma-separated list of arguments. Given that combinations
- are supported, we can't add all potential candidates to the
- vec, but if we at least add them individually without commas,
- we should do a better job e.g. correcting
- "-sanitize=address"
- to
- "-fsanitize=address"
- rather than to "-Wframe-address" (PR driver/69265). */
- {
- for (int j = 0; sanitizer_opts[j].name != NULL; ++j)
- {
- struct cl_option optb;
- /* -fsanitize=all is not valid, only -fno-sanitize=all.
- So don't register the positive misspelling candidates
- for it. */
- if (sanitizer_opts[j].flag == ~0U && i == OPT_fsanitize_)
- {
- optb = *option;
- optb.opt_text = opt_text = "-fno-sanitize=";
- optb.cl_reject_negative = true;
- option = &optb;
- }
- /* Get one arg at a time e.g. "-fsanitize=address". */
- char *with_arg = concat (opt_text,
- sanitizer_opts[j].name,
- NULL);
- /* Add with_arg and all of its variant spellings e.g.
- "-fno-sanitize=address" to candidates (albeit without
- leading dashes). */
- add_misspelling_candidates (m_option_suggestions, option,
- with_arg);
- free (with_arg);
- }
- }
- break;
- }
- }
-}
-
-/* Helper function for driver::handle_unrecognized_options.
-
- Given an unrecognized option BAD_OPT (without the leading dash),
- locate the closest reasonable matching option (again, without the
- leading dash), or NULL.
-
- The returned string is owned by the driver instance. */
-
-const char *
-driver::suggest_option (const char *bad_opt)
-{
- /* Lazily populate m_option_suggestions. */
- if (!m_option_suggestions)
- build_option_suggestions ();
- gcc_assert (m_option_suggestions);
-
- /* "m_option_suggestions" is now populated. Use it. */
- return find_closest_string
- (bad_opt,
- (auto_vec <const char *> *) m_option_suggestions);
-}
-
/* Reject switches that no pass was interested in. */
void
@@ -7876,7 +7781,7 @@ driver::handle_unrecognized_options ()
for (size_t i = 0; (int) i < n_switches; i++)
if (! switches[i].validated)
{
- const char *hint = suggest_option (switches[i].part1);
+ const char *hint = m_option_proposer.suggest_option (switches[i].part1);
if (hint)
error ("unrecognized command line option %<-%s%>;"
" did you mean %<-%s%>?",
diff --git a/gcc/gcc.h b/gcc/gcc.h
index ddbf42f78ea..a7606183393 100644
--- a/gcc/gcc.h
+++ b/gcc/gcc.h
@@ -45,8 +45,6 @@ class driver
void putenv_COLLECT_GCC (const char *argv0) const;
void maybe_putenv_COLLECT_LTO_WRAPPER () const;
void maybe_putenv_OFFLOAD_TARGETS () const;
- void build_option_suggestions (void);
- const char *suggest_option (const char *bad_opt);
void handle_unrecognized_options ();
int maybe_print_and_exit () const;
bool prepare_infiles ();
@@ -59,7 +57,7 @@ class driver
char *explicit_link_files;
struct cl_decoded_option *decoded_options;
unsigned int decoded_options_count;
- auto_vec <char *> *m_option_suggestions;
+ option_proposer m_option_proposer;
};
/* The mapping of a spec function name to the C function that
diff --git a/gcc/opt-proposer.c b/gcc/opt-proposer.c
new file mode 100644
index 00000000000..08379f0b631
--- /dev/null
+++ b/gcc/opt-proposer.c
@@ -0,0 +1,420 @@
+/* Provide option suggestion for --complete option and a misspelled
+ used by a user.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "opts.h"
+#include "params.h"
+#include "spellcheck.h"
+#include "opt-proposer.h"
+#include "selftest.h"
+
+option_proposer::~option_proposer ()
+{
+ if (m_option_suggestions)
+ {
+ int i;
+ char *str;
+ FOR_EACH_VEC_ELT (*m_option_suggestions, i, str)
+ free (str);
+ delete m_option_suggestions;
+ }
+
+ release_completion_results ();
+}
+
+const char *
+option_proposer::suggest_option (const char *bad_opt)
+{
+ /* Lazily populate m_option_suggestions. */
+ if (!m_option_suggestions)
+ build_option_suggestions ();
+ gcc_assert (m_option_suggestions);
+
+ /* "m_option_suggestions" is now populated. Use it. */
+ return find_closest_string
+ (bad_opt,
+ (auto_vec <const char *> *) m_option_suggestions);
+}
+
+void
+option_proposer::build_completions (const char *starting)
+{
+ release_completion_results ();
+
+ /* Bail out for an invalid input. */
+ if (starting == NULL || starting[0] == '\0')
+ return;
+
+ if (starting[0] == '-')
+ starting++;
+
+ size_t length = strlen (starting);
+
+ /* Handle parameters. */
+ const char *prefix = "-param";
+ if (length >= strlen (prefix) && strstr (starting, prefix) == starting)
+ {
+ /* We support both '-param-xyz=123' and '-param xyz=123' */
+ starting += strlen (prefix);
+ char separator = starting[0];
+ starting++;
+ if (separator == ' ' || separator == '=')
+ find_param_completions (separator, starting);
+ }
+ else
+ {
+ /* Lazily populate m_option_suggestions. */
+ if (!m_option_suggestions)
+ build_option_suggestions ();
+ gcc_assert (m_option_suggestions);
+
+ for (unsigned i = 0; i < m_option_suggestions->length (); i++)
+ {
+ char *candidate = (*m_option_suggestions)[i];
+ if (strlen (candidate) >= length
+ && strstr (candidate, starting) == candidate)
+ m_completion_results.safe_push (concat ("-", candidate, NULL));
+ }
+ }
+}
+
+void
+option_proposer::suggest_completion (const char *starting)
+{
+ build_completions (starting);
+ print_completion_results ();
+ release_completion_results ();
+}
+
+auto_vec <char *> *
+option_proposer::get_completion_suggestions (const char *starting)
+{
+ build_completions (starting);
+ return &m_completion_results;
+}
+
+void
+option_proposer::build_option_suggestions (void)
+{
+ gcc_assert (m_option_suggestions == NULL);
+ m_option_suggestions = new auto_vec <char *> ();
+
+ /* We build a vec of m_option_suggestions, using add_misspelling_candidates
+ to add copies of strings, without a leading dash. */
+
+ for (unsigned int i = 0; i < cl_options_count; i++)
+ {
+ const struct cl_option *option = &cl_options[i];
+ const char *opt_text = option->opt_text;
+ switch (i)
+ {
+ default:
+ if (option->var_type == CLVC_ENUM)
+ {
+ const struct cl_enum *e = &cl_enums[option->var_enum];
+ for (unsigned j = 0; e->values[j].arg != NULL; j++)
+ {
+ char *with_arg = concat (opt_text, e->values[j].arg, NULL);
+ add_misspelling_candidates (m_option_suggestions, option,
+ with_arg);
+ free (with_arg);
+ }
+ }
+ else
+ add_misspelling_candidates (m_option_suggestions, option,
+ opt_text);
+ break;
+
+ case OPT_fsanitize_:
+ case OPT_fsanitize_recover_:
+ /* -fsanitize= and -fsanitize-recover= can take
+ a comma-separated list of arguments. Given that combinations
+ are supported, we can't add all potential candidates to the
+ vec, but if we at least add them individually without commas,
+ we should do a better job e.g. correcting
+ "-sanitize=address"
+ to
+ "-fsanitize=address"
+ rather than to "-Wframe-address" (PR driver/69265). */
+ {
+ for (int j = 0; sanitizer_opts[j].name != NULL; ++j)
+ {
+ struct cl_option optb;
+ /* -fsanitize=all is not valid, only -fno-sanitize=all.
+ So don't register the positive misspelling candidates
+ for it. */
+ if (sanitizer_opts[j].flag == ~0U && i == OPT_fsanitize_)
+ {
+ optb = *option;
+ optb.opt_text = opt_text = "-fno-sanitize=";
+ optb.cl_reject_negative = true;
+ option = &optb;
+ }
+ /* Get one arg at a time e.g. "-fsanitize=address". */
+ char *with_arg = concat (opt_text,
+ sanitizer_opts[j].name,
+ NULL);
+ /* Add with_arg and all of its variant spellings e.g.
+ "-fno-sanitize=address" to candidates (albeit without
+ leading dashes). */
+ add_misspelling_candidates (m_option_suggestions, option,
+ with_arg);
+ free (with_arg);
+ }
+ }
+ break;
+ }
+ }
+}
+
+void
+option_proposer::find_param_completions (const char separator,
+ const char *starting)
+{
+ char separator_str[] {separator, '\0'};
+ size_t length = strlen (starting);
+ for (unsigned i = 0; i < get_num_compiler_params (); ++i)
+ {
+ const char *candidate = compiler_params[i].option;
+ if (strlen (candidate) >= length
+ && strstr (candidate, starting) == candidate)
+ m_completion_results.safe_push (concat ("--param", separator_str,
+ candidate, NULL));
+ }
+}
+
+void
+option_proposer::print_completion_results ()
+{
+ for (unsigned i = 0; i < m_completion_results.length (); i++)
+ printf ("%s\n", m_completion_results[i]);
+}
+
+void
+option_proposer::release_completion_results ()
+{
+ for (unsigned i = 0; i < m_completion_results.length (); i++)
+ free (m_completion_results[i]);
+
+ m_completion_results.truncate (0);
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+static void
+test_valid_option (option_proposer &proposer, const char *option)
+{
+ auto_vec <char *> *suggestions = proposer.get_completion_suggestions (option);
+ ASSERT_GT (suggestions->length (), 0);
+
+ for (unsigned i = 0; i < suggestions->length (); i++)
+ ASSERT_STR_STARTSWITH ((*suggestions)[i], option);
+}
+
+/* Verify that valid options works correctly. */
+
+static void
+test_completion_valid_options (option_proposer &proposer)
+{
+ const char *needles[]
+ {
+ "-fno-var-tracking-assignments-toggle",
+ "-fpredictive-commoning",
+ "--param=stack-clash-protection-guard-size",
+ "--param=max-predicted-iterations",
+ "-ftree-loop-distribute-patterns",
+ "-fno-var-tracking",
+ "-Walloc-zero",
+ "--param=ipa-cp-value-list-size",
+ "-Wsync-nand",
+ "-Wno-attributes",
+ "--param=tracer-dynamic-coverage-feedback",
+ "-Wno-format-contains-nul",
+ "-Wnamespaces",
+ "-fisolate-erroneous-paths-attribute",
+ "-Wno-underflow",
+ "-Wtarget-lifetime",
+ "--param=asan-globals",
+ "-Wno-empty-body",
+ "-Wno-odr",
+ "-Wformat-zero-length",
+ "-Wstringop-truncation",
+ "-fno-ipa-vrp",
+ "-fmath-errno",
+ "-Warray-temporaries",
+ "-Wno-unused-label",
+ "-Wreturn-local-addr",
+ "--param=sms-dfa-history",
+ "--param=asan-instrument-reads",
+ "-Wreturn-type",
+ "-Wc++17-compat",
+ "-Wno-effc++",
+ "--param=max-fields-for-field-sensitive",
+ "-fisolate-erroneous-paths-dereference",
+ "-fno-defer-pop",
+ "-Wcast-align=strict",
+ "-foptimize-strlen",
+ "-Wpacked-not-aligned",
+ "-funroll-loops",
+ "-fif-conversion2",
+ "-Wdesignated-init",
+ "--param=max-iterations-computation-cost",
+ "-Wmultiple-inheritance",
+ "-fno-sel-sched-reschedule-pipelined",
+ "-Wassign-intercept",
+ "-Wno-format-security",
+ "-fno-sched-stalled-insns",
+ "-fbtr-bb-exclusive",
+ "-fno-tree-tail-merge",
+ "-Wlong-long",
+ "-Wno-unused-but-set-parameter",
+ NULL
+ };
+
+ for (const char **ptr = needles; *ptr != NULL; ptr++)
+ test_valid_option (proposer, *ptr);
+}
+
+/* Verify that valid parameter works correctly. */
+
+static void
+test_completion_valid_params (option_proposer &proposer)
+{
+ const char *needles[]
+ {
+ "--param=sched-state-edge-prob-cutoff",
+ "--param=iv-consider-all-candidates-bound",
+ "--param=align-threshold",
+ "--param=prefetch-min-insn-to-mem-ratio",
+ "--param=max-unrolled-insns",
+ "--param=max-early-inliner-iterations",
+ "--param=max-vartrack-reverse-op-size",
+ "--param=ipa-cp-loop-hint-bonus",
+ "--param=tracer-min-branch-ratio",
+ "--param=graphite-max-arrays-per-scop",
+ "--param=sink-frequency-threshold",
+ "--param=max-cse-path-length",
+ "--param=sra-max-scalarization-size-Osize",
+ "--param=prefetch-latency",
+ "--param=dse-max-object-size",
+ "--param=asan-globals",
+ "--param=max-vartrack-size",
+ "--param=case-values-threshold",
+ "--param=max-slsr-cand-scan",
+ "--param=min-insn-to-prefetch-ratio",
+ "--param=tracer-min-branch-probability",
+ "--param sink-frequency-threshold",
+ "--param max-cse-path-length",
+ "--param sra-max-scalarization-size-Osize",
+ "--param prefetch-latency",
+ "--param dse-max-object-size",
+ "--param asan-globals",
+ "--param max-vartrack-size",
+ NULL
+ };
+
+ for (const char **ptr = needles; *ptr != NULL; ptr++)
+ test_valid_option (proposer, *ptr);
+}
+
+/* Return true when EXPECTED is one of completions for OPTION string. */
+
+static bool
+in_completion_p (option_proposer &proposer, const char *option,
+ const char *expected)
+{
+ auto_vec <char *> *suggestions = proposer.get_completion_suggestions (option);
+
+ for (unsigned i = 0; i < suggestions->length (); i++)
+ {
+ char *r = (*suggestions)[i];
+ if (strcmp (r, expected) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+empty_completion_p (option_proposer &proposer, const char *option)
+{
+ auto_vec <char *> *suggestions = proposer.get_completion_suggestions (option);
+ return suggestions->is_empty ();
+}
+
+/* Verify partial completions. */
+
+static void
+test_completion_partial_match (option_proposer &proposer)
+{
+ ASSERT_TRUE (in_completion_p (proposer, "-fsani", "-fsanitize=address"));
+ ASSERT_TRUE (in_completion_p (proposer, "-fsani",
+ "-fsanitize-address-use-after-scope"));
+ ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf-functions"));
+ ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf"));
+ ASSERT_TRUE (in_completion_p (proposer, "--param=",
+ "--param=max-vartrack-reverse-op-size"));
+ ASSERT_TRUE (in_completion_p (proposer, "--param ",
+ "--param max-vartrack-reverse-op-size"));
+
+ ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf", "-fipa"));
+ ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf-functions", "-fipa-icf"));
+}
+
+/* Verify that a garbage does not return a completion match. */
+
+static void
+test_completion_garbage (option_proposer &proposer)
+{
+ ASSERT_TRUE (empty_completion_p (proposer, NULL));
+ ASSERT_TRUE (empty_completion_p (proposer, ""));
+ ASSERT_TRUE (empty_completion_p (proposer, "- "));
+ ASSERT_TRUE (empty_completion_p (proposer, "123456789"));
+ ASSERT_TRUE (empty_completion_p (proposer, "---------"));
+ ASSERT_TRUE (empty_completion_p (proposer, "#########"));
+ ASSERT_TRUE (empty_completion_p (proposer, "- - - - - -"));
+ ASSERT_TRUE (empty_completion_p (proposer, "-fsanitize=address2"));
+
+ ASSERT_FALSE (empty_completion_p (proposer, "-"));
+ ASSERT_FALSE (empty_completion_p (proposer, "-fipa"));
+ ASSERT_FALSE (empty_completion_p (proposer, "--par"));
+}
+
+/* Run all of the selftests within this file. */
+
+void
+opt_proposer_c_tests ()
+{
+ option_proposer proposer;
+
+ test_completion_valid_options (proposer);
+ test_completion_valid_params (proposer);
+ test_completion_partial_match (proposer);
+ test_completion_garbage (proposer);
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/opt-proposer.h b/gcc/opt-proposer.h
new file mode 100644
index 00000000000..b673f02dc70
--- /dev/null
+++ b/gcc/opt-proposer.h
@@ -0,0 +1,84 @@
+/* Provide option suggestion for --complete option and a misspelled
+ used by a user.
+ Copyright (C) 2018 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC 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, or (at your option) any later
+version.
+
+GCC 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 GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_OPT_PROPOSER_H
+#define GCC_OPT_PROPOSER_H
+
+/* Option proposer is class used by driver in order to provide hints
+ for wrong options provided. And it's used by --complete option that's
+ intended to be invoked by BASH in order to provide better option
+ completion support. */
+
+class option_proposer
+{
+public:
+ /* Default constructor. */
+ option_proposer (): m_option_suggestions (NULL), m_completion_results ()
+ {}
+
+ /* Default destructor. */
+ ~option_proposer ();
+
+ /* Helper function for driver::handle_unrecognized_options.
+
+ Given an unrecognized option BAD_OPT (without the leading dash),
+ locate the closest reasonable matching option (again, without the
+ leading dash), or NULL.
+
+ The returned string is owned by the option_proposer instance. */
+ const char *suggest_option (const char *bad_opt);
+
+ /* Print to stdout all options that start with STARTING. */
+ void suggest_completion (const char *starting);
+
+ /* Return vector with completion suggestions that start with STARTING.
+
+ The returned strings are owned by the option_proposer instance. */
+ auto_vec <char *> *get_completion_suggestions (const char *starting);
+
+private:
+ /* Helper function for option_proposer::suggest_option. Populate
+ m_option_suggestions with candidate strings for misspelled options.
+ The strings will be freed by the option_proposer's dtor. */
+ void build_option_suggestions ();
+
+ /* Build completions that start with STARTING and save them
+ into m_completion_results vector. */
+ void build_completions (const char *starting);
+
+ /* Find parameter completions for --param format with SEPARATOR.
+ Again, save the completions into m_completion_results. */
+ void find_param_completions (const char separator, const char *starting);
+
+ /* Print found completions in m_completion_results to stdout. */
+ void print_completion_results ();
+
+ /* Free content of m_completion_results. */
+ void release_completion_results ();
+
+private:
+ /* Cache with all suggestions. */
+ auto_vec <char *> *m_option_suggestions;
+
+ /* Completion cache. */
+ auto_vec <char *> m_completion_results;
+};
+
+#endif /* GCC_OPT_PROPOSER_H */
diff --git a/gcc/opts.c b/gcc/opts.c
index 33efcc0d6e7..ed102c05c22 100644
--- a/gcc/opts.c
+++ b/gcc/opts.c
@@ -1982,6 +1982,9 @@ common_handle_option (struct gcc_options *opts,
opts->x_exit_after_options = true;
break;
+ case OPT__completion_:
+ break;
+
case OPT_fsanitize_:
opts->x_flag_sanitize
= parse_sanitizer_options (arg, loc, code,
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index fe221ff8946..0b45c479192 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -70,6 +70,7 @@ selftest::run_tests ()
fibonacci_heap_c_tests ();
typed_splay_tree_c_tests ();
unique_ptr_tests_cc_tests ();
+ opt_proposer_c_tests ();
/* Mid-level data structures. */
input_c_tests ();
diff --git a/gcc/selftest.c b/gcc/selftest.c
index 5709110c291..4cff89d2909 100644
--- a/gcc/selftest.c
+++ b/gcc/selftest.c
@@ -118,6 +118,39 @@ assert_str_contains (const location &loc,
desc_haystack, desc_needle, val_haystack, val_needle);
}
+/* Implementation detail of ASSERT_STR_STARTSWITH.
+ Use strstr to determine if val_haystack starts with val_needle.
+ ::selftest::pass if it starts.
+ ::selftest::fail if it does not start. */
+
+void
+assert_str_startswith (const location &loc,
+ const char *desc_haystack,
+ const char *desc_needle,
+ const char *val_haystack,
+ const char *val_needle)
+{
+ /* If val_haystack is NULL, fail with a custom error message. */
+ if (val_haystack == NULL)
+ fail_formatted (loc, "ASSERT_STR_STARTSWITH (%s, %s) haystack=NULL",
+ desc_haystack, desc_needle);
+
+ /* If val_needle is NULL, fail with a custom error message. */
+ if (val_needle == NULL)
+ fail_formatted (loc,
+ "ASSERT_STR_STARTSWITH (%s, %s) haystack=\"%s\" needle=NULL",
+ desc_haystack, desc_needle, val_haystack);
+
+ const char *test = strstr (val_haystack, val_needle);
+ if (test == val_haystack)
+ pass (loc, "ASSERT_STR_STARTSWITH");
+ else
+ fail_formatted
+ (loc, "ASSERT_STR_STARTSWITH (%s, %s) haystack=\"%s\" needle=\"%s\"",
+ desc_haystack, desc_needle, val_haystack, val_needle);
+}
+
+
/* Constructor. Generate a name for the file. */
named_temp_file::named_temp_file (const char *suffix)
diff --git a/gcc/selftest.h b/gcc/selftest.h
index e3117c6bfc4..b5d7eeef86e 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -78,6 +78,15 @@ extern void assert_str_contains (const location &loc,
const char *val_haystack,
const char *val_needle);
+/* Implementation detail of ASSERT_STR_STARTSWITH. */
+
+extern void assert_str_startswith (const location &loc,
+ const char *desc_expected,
+ const char *desc_actual,
+ const char *val_expected,
+ const char *val_actual);
+
+
/* A named temporary file for use in selftests.
Usable for writing out files, and as the base class for
temp_source_file.
@@ -216,6 +225,7 @@ extern void wide_int_cc_tests ();
extern void predict_c_tests ();
extern void simplify_rtx_c_tests ();
extern void vec_perm_indices_c_tests ();
+extern void opt_proposer_c_tests ();
extern int num_passes;
@@ -401,6 +411,17 @@ extern int num_passes;
(HAYSTACK), (NEEDLE)); \
SELFTEST_END_STMT
+/* Evaluate HAYSTACK and NEEDLE and use strstr to determine if HAYSTACK
+ starts with NEEDLE.
+ ::selftest::pass if starts.
+ ::selftest::fail if does not start. */
+
+#define ASSERT_STR_STARTSWITH(HAYSTACK, NEEDLE) \
+ SELFTEST_BEGIN_STMT \
+ ::selftest::assert_str_startswith (SELFTEST_LOCATION, #HAYSTACK, #NEEDLE, \
+ (HAYSTACK), (NEEDLE)); \
+ SELFTEST_END_STMT
+
/* Evaluate PRED1 (VAL1), calling ::selftest::pass if it is true,
::selftest::fail if it is false. */
--
2.16.3
^ permalink raw reply [flat|nested] 9+ messages in thread