This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.

RFC: bash completion

From: Martin Liška <mliska at suse dot cz>

To: GCC Development <gcc at gcc dot gnu dot org>

Cc: David Malcolm <dmalcolm at redhat dot com>

Date: Tue, 24 Apr 2018 16:45:05 +0200

Subject: RFC: bash completion

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.html 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

>From 7bf882961cd81d3f12df017398625ee190c9808f Mon Sep 17 00:00:00 2001 From: marxin <mliska@suse.cz> Date: Fri, 23 Feb 2018 12:28:43 +0100 Subject: [PATCH] First version of prototype. --- gcc.sh | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/common.opt | 4 ++++ gcc/gcc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ gcc/gcc.h | 1 + gcc/opts.c | 3 +++ gcc/params.c | 13 +++++++++++++ gcc/params.h | 1 + 7 files changed, 121 insertions(+) create mode 100644 gcc.sh 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/common.opt b/gcc/common.opt index d6ef85928f3..389d4fa0385 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -254,6 +254,10 @@ Driver Alias(S) -compile Driver Alias(c) +-completion= +Common Driver Joined +--param Bash completion. + -coverage Driver Alias(coverage) diff --git a/gcc/gcc.c b/gcc/gcc.c index a716f708259..dab88f44d27 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -220,6 +220,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 +3820,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; @@ -7300,6 +7307,12 @@ driver::main (int argc, char **argv) maybe_putenv_OFFLOAD_TARGETS (); handle_unrecognized_options (); + if (completion) + { + suggest_completion (completion); + return 0; + } + if (!maybe_print_and_exit ()) return 0; @@ -7868,6 +7881,41 @@ driver::suggest_option (const char *bad_opt) (auto_vec <const char *> *) m_option_suggestions); } +void +driver::suggest_completion (const char *starting) +{ + /* Lazily populate m_option_suggestions. */ + if (!m_option_suggestions) + build_option_suggestions (); + gcc_assert (m_option_suggestions); + + if (starting[0] == '-') + starting++; + + size_t l = strlen (starting); + const char *prefix = "-param"; + if (l >= 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 != '=') + return; + + param_print_candidates (separator, starting); + return; + } + + for (int i = 0; i < m_option_suggestions->length (); i++) + { + char *candidate = (*m_option_suggestions)[i]; + if (strlen (candidate) >= l + && strstr (candidate, starting) == candidate) + printf ("-%s

", candidate); + } +} + /* Reject switches that no pass was interested in. */ void diff --git a/gcc/gcc.h b/gcc/gcc.h index ddbf42f78ea..e786e912943 100644 --- a/gcc/gcc.h +++ b/gcc/gcc.h @@ -47,6 +47,7 @@ class driver void maybe_putenv_OFFLOAD_TARGETS () const; void build_option_suggestions (void); const char *suggest_option (const char *bad_opt); + void suggest_completion (const char *starting); void handle_unrecognized_options (); int maybe_print_and_exit () const; bool prepare_infiles (); 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/params.c b/gcc/params.c index 623296ce49b..6b97e524984 100644 --- a/gcc/params.c +++ b/gcc/params.c @@ -179,6 +179,19 @@ param_string_value_p (enum compiler_param index, const char *value_name, return true; } +void +param_print_candidates (const char separator, const char *starting) +{ + size_t l = strlen (starting); + for (unsigned i = 0; i < num_compiler_params; ++i) + { + const char *candidate = compiler_params[i].option; + if (strlen (candidate) >= l + && strstr (candidate, starting) == candidate) + printf ("--param%c%s

", separator, candidate); + } +} + /* Set the VALUE associated with the parameter given by NAME in PARAMS and PARAMS_SET. */ diff --git a/gcc/params.h b/gcc/params.h index 98249d2a1f6..7118fea9f14 100644 --- a/gcc/params.h +++ b/gcc/params.h @@ -91,6 +91,7 @@ enum compiler_param extern bool find_param (const char *, enum compiler_param *); extern const char *find_param_fuzzy (const char *name); extern bool param_string_value_p (enum compiler_param, const char *, int *); +extern void param_print_candidates (const char separator, const char *starting); /* The value of the parameter given by ENUM. Not an lvalue. */ #define PARAM_VALUE(ENUM) \ -- 2.16.3