Come up with new --completion option.
authorMartin Liska <mliska@suse.cz>
Thu, 28 Jun 2018 07:11:16 +0000 (09:11 +0200)
committerMartin Liska <marxin@gcc.gnu.org>
Thu, 28 Jun 2018 07:11:16 +0000 (07:11 +0000)
2018-06-28  Martin Liska  <mliska@suse.cz>

* common.opt: Introduce -completion option.
* gcc.c (driver_handle_option): Handle it.
(driver::main): Print completions if completion
        is set.
* opt-suggestions.c (option_proposer::get_completions):
        New function.
(option_proposer::suggest_completion): Likewise.
(option_proposer::find_param_completions): Likewise.
(verify_autocompletions): Likewise.
(test_completion_valid_options): Likewise.
(test_completion_valid_params): Likewise.
(in_completion_p): Likewise.
(empty_completion_p): Likewise.
(test_completion_partial_match): Likewise.
(test_completion_garbage): Likewise.
(opt_proposer_c_tests): Likewise.
* opt-suggestions.h: Declare new functions.
* opts.c (common_handle_option): Handle OPT__completion_.
* selftest-run-tests.c (selftest::run_tests): Add
        opt_proposer_c_tests.
* selftest.c (assert_str_startswith): New.
* selftest.h (assert_str_startswith): Likewise.
(opt_proposer_c_tests): New.
(ASSERT_STR_STARTSWITH): Likewise.

From-SVN: r262210

gcc/ChangeLog
gcc/common.opt
gcc/gcc.c
gcc/opt-suggestions.c
gcc/opt-suggestions.h
gcc/opts.c
gcc/selftest-run-tests.c
gcc/selftest.c
gcc/selftest.h

index aee4f0fbf7a956a3900c7bd81782d443a96f461a..d4d1d86e2d88f0b003cb78b407d9bf137974b356 100644 (file)
@@ -1,3 +1,30 @@
+2018-06-28  Martin Liska  <mliska@suse.cz>
+
+       * common.opt: Introduce -completion option.
+       * gcc.c (driver_handle_option): Handle it.
+       (driver::main): Print completions if completion
+        is set.
+       * opt-suggestions.c (option_proposer::get_completions):
+        New function.
+       (option_proposer::suggest_completion): Likewise.
+       (option_proposer::find_param_completions): Likewise.
+       (verify_autocompletions): Likewise.
+       (test_completion_valid_options): Likewise.
+       (test_completion_valid_params): Likewise.
+       (in_completion_p): Likewise.
+       (empty_completion_p): Likewise.
+       (test_completion_partial_match): Likewise.
+       (test_completion_garbage): Likewise.
+       (opt_proposer_c_tests): Likewise.
+       * opt-suggestions.h: Declare new functions.
+       * opts.c (common_handle_option): Handle OPT__completion_.
+       * selftest-run-tests.c (selftest::run_tests): Add
+        opt_proposer_c_tests.
+       * selftest.c (assert_str_startswith): New.
+       * selftest.h (assert_str_startswith): Likewise.
+       (opt_proposer_c_tests): New.
+       (ASSERT_STR_STARTSWITH): Likewise.
+
 2018-06-28  Martin Liska  <mliska@suse.cz>
 
        * Makefile.in: Add opt-suggestions.o.
index 0d1445b852977c7384eb4b8db86b07177d241900..5a50bc27710a9a38204249de3e9e4da72686f632 100644 (file)
@@ -255,6 +255,10 @@ Driver Alias(S)
 -compile
 Driver Alias(c)
 
+-completion=
+Common Driver Joined Undocumented
+Provide bash completion for options starting with provided string.
+
 -coverage
 Driver Alias(coverage)
 
index dda1fd353988c28d851de265cb809bfd98ecbb7a..9ed8a03af0a2aa017be1fc75a81823dda94d7816 100644 (file)
--- a/gcc/gcc.c
+++ b/gcc/gcc.c
@@ -221,6 +221,10 @@ static int print_help_list;
 
 static int print_version;
 
+/* Flag that stores string prefix for which we provide bash completion.  */
+
+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
@@ -3890,6 +3894,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 +7309,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;
 
index 90ab80e627d768358f4152f5630a710b4f3073a7..894eea5f37c0bd01a90039ba106571114961c903 100644 (file)
@@ -47,6 +47,66 @@ option_proposer::suggest_option (const char *bad_opt)
      (auto_vec <const char *> *) m_option_suggestions);
 }
 
+/* Populate RESULTS with valid completions of options that begin
+   with OPTION_PREFIX.  */
+
+void
+option_proposer::get_completions (const char *option_prefix,
+                                 auto_string_vec &results)
+{
+  /* Bail out for an invalid input.  */
+  if (option_prefix == NULL || option_prefix[0] == '\0')
+    return;
+
+  /* Option suggestions are built without first leading dash character.  */
+  if (option_prefix[0] == '-')
+    option_prefix++;
+
+  size_t length = strlen (option_prefix);
+
+  /* Handle OPTION_PREFIX starting with "-param".  */
+  const char *prefix = "-param";
+  if (length >= strlen (prefix)
+      && strstr (option_prefix, prefix) == option_prefix)
+    {
+      /* We support both '-param-xyz=123' and '-param xyz=123' */
+      option_prefix += strlen (prefix);
+      char separator = option_prefix[0];
+      option_prefix++;
+      if (separator == ' ' || separator == '=')
+       find_param_completions (separator, option_prefix, results);
+    }
+  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, option_prefix) == candidate)
+           results.safe_push (concat ("-", candidate, NULL));
+       }
+    }
+}
+
+/* Print on stdout a list of valid options that begin with OPTION_PREFIX,
+   one per line, suitable for use by Bash completion.
+
+   Implementation of the "-completion=" option.  */
+
+void
+option_proposer::suggest_completion (const char *option_prefix)
+{
+  auto_string_vec results;
+  get_completions (option_prefix, results);
+  for (unsigned i = 0; i < results.length (); i++)
+    printf ("%s\n", results[i]);
+}
+
 void
 option_proposer::build_option_suggestions (void)
 {
@@ -120,3 +180,236 @@ option_proposer::build_option_suggestions (void)
        }
     }
 }
+
+/* Find parameter completions for --param format with SEPARATOR.
+   Again, save the completions into results.  */
+
+void
+option_proposer::find_param_completions (const char separator,
+                                        const char *param_prefix,
+                                        auto_string_vec &results)
+{
+  char separator_str[] = {separator, '\0'};
+  size_t length = strlen (param_prefix);
+  for (unsigned i = 0; i < get_num_compiler_params (); ++i)
+    {
+      const char *candidate = compiler_params[i].option;
+      if (strlen (candidate) >= length
+         && strstr (candidate, param_prefix) == candidate)
+       results.safe_push (concat ("--param", separator_str, candidate, NULL));
+    }
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that PROPOSER generates sane auto-completion suggestions
+   for OPTION_PREFIX.  */
+
+static void
+verify_autocompletions (option_proposer &proposer, const char *option_prefix)
+{
+  auto_string_vec suggestions;
+  proposer.get_completions (option_prefix, suggestions);
+
+  /* There must be at least one suggestion, and every suggestion must
+     indeed begin with OPTION_PREFIX.  */
+
+  ASSERT_GT (suggestions.length (), 0);
+
+  for (unsigned i = 0; i < suggestions.length (); i++)
+    ASSERT_STR_STARTSWITH (suggestions[i], option_prefix);
+}
+
+/* Verify that valid options are auto-completed correctly.  */
+
+static void
+test_completion_valid_options (option_proposer &proposer)
+{
+  const char *option_prefixes[] =
+  {
+    "-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 = option_prefixes; *ptr != NULL; ptr++)
+    verify_autocompletions (proposer, *ptr);
+}
+
+/* Verify that valid parameters are auto-completed correctly,
+   both with the "--param=PARAM" form and the "--param PARAM" form.  */
+
+static void
+test_completion_valid_params (option_proposer &proposer)
+{
+  const char *option_prefixes[] =
+  {
+    "--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 = option_prefixes; *ptr != NULL; ptr++)
+    verify_autocompletions (proposer, *ptr);
+}
+
+/* Return true when EXPECTED is one of completions for OPTION_PREFIX string.  */
+
+static bool
+in_completion_p (option_proposer &proposer, const char *option_prefix,
+                const char *expected)
+{
+  auto_string_vec suggestions;
+  proposer.get_completions (option_prefix, suggestions);
+
+  for (unsigned i = 0; i < suggestions.length (); i++)
+    {
+      char *r = suggestions[i];
+      if (strcmp (r, expected) == 0)
+       return true;
+    }
+
+  return false;
+}
+
+/* Return true when PROPOSER does not find any partial completion
+   for OPTION_PREFIX.  */
+
+static bool
+empty_completion_p (option_proposer &proposer, const char *option_prefix)
+{
+  auto_string_vec suggestions;
+  proposer.get_completions (option_prefix, suggestions);
+  return suggestions.is_empty ();
+}
+
+/* Verify autocompletions of partially-complete options.  */
+
+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"));
+
+  ASSERT_FALSE (empty_completion_p (proposer, "-"));
+  ASSERT_FALSE (empty_completion_p (proposer, "-fipa"));
+  ASSERT_FALSE (empty_completion_p (proposer, "--par"));
+}
+
+/* Verify that autocompletion does not return any match for garbage inputs.  */
+
+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"));
+}
+
+/* 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 */
index ccd4e45a474fffe81bf69d87d7582809861164dc..222bafa12cd4969c574a4e64659798fa78aad71c 100644 (file)
@@ -45,12 +45,27 @@ public:
      The returned string is owned by the option_proposer instance.  */
   const char *suggest_option (const char *bad_opt);
 
+  /* Print on stdout a list of valid options that begin with OPTION_PREFIX,
+     one per line, suitable for use by Bash completion.
+
+     Implementation of the "-completion=" option.  */
+  void suggest_completion (const char *option_prefix);
+
+  /* Populate RESULTS with valid completions of options that begin
+     with OPTION_PREFIX.  */
+  void get_completions (const char *option_prefix, auto_string_vec &results);
+
 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 ();
 
+  /* Find parameter completions for --param format with SEPARATOR.
+     Again, save the completions into results.  */
+  void find_param_completions (const char separator, const char *param_prefix,
+                              auto_string_vec &results);
+
 private:
   /* Cache with all suggestions.  */
   auto_string_vec *m_option_suggestions;
index 33efcc0d6e7db73ba60cab62ef146e976e75ca21..ed102c05c222ae918a64807f89f68c0f9f3ac2e5 100644 (file)
@@ -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,
index a9aacc02f1a030dd066b5613722bcc0234b34eb2..7f4d6f3a246ab36d04179a8329c738e3039390ec 100644 (file)
@@ -71,6 +71,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 ();
index 74adc63e65cd004c4d26757511c4422534643dd1..27de9a41b794688952004fdd17808ade64896ca9 100644 (file)
@@ -125,6 +125,40 @@ assert_str_contains (const location &loc,
         desc_haystack, desc_needle, val_haystack, val_needle);
 }
 
+/* Implementation detail of ASSERT_STR_STARTSWITH.
+   Determine if VAL_STR starts with VAL_PREFIX.
+   ::selftest::pass if VAL_STR does start with VAL_PREFIX.
+   ::selftest::fail if it does not, or either is NULL (using
+   DESC_STR and DESC_PREFIX in the error message).  */
+
+void
+assert_str_startswith (const location &loc,
+                      const char *desc_str,
+                      const char *desc_prefix,
+                      const char *val_str,
+                      const char *val_prefix)
+{
+  /* If val_str is NULL, fail with a custom error message.  */
+  if (val_str == NULL)
+    fail_formatted (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=NULL",
+                   desc_str, desc_prefix);
+
+  /* If val_prefix is NULL, fail with a custom error message.  */
+  if (val_prefix == NULL)
+    fail_formatted (loc,
+                   "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=NULL",
+                   desc_str, desc_prefix, val_str);
+
+  const char *test = strstr (val_str, val_prefix);
+  if (test == val_str)
+    pass (loc, "ASSERT_STR_STARTSWITH");
+  else
+    fail_formatted
+       (loc, "ASSERT_STR_STARTSWITH (%s, %s) str=\"%s\" prefix=\"%s\"",
+        desc_str, desc_prefix, val_str, val_prefix);
+}
+
+
 /* Constructor.  Generate a name for the file.  */
 
 named_temp_file::named_temp_file (const char *suffix)
index a5507cc7693d6334b989f575ed3cd4c8a85b143c..d66fb93d1a59a7ec88116186b63d1e9e2c9ca4b7 100644 (file)
@@ -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_str,
+                                  const char *desc_prefix,
+                                  const char *val_str,
+                                  const char *val_prefix);
+
+
 /* A named temporary file for use in selftests.
    Usable for writing out files, and as the base class for
    temp_source_file.
@@ -217,6 +226,7 @@ extern void unique_ptr_tests_cc_tests ();
 extern void vec_c_tests ();
 extern void vec_perm_indices_c_tests ();
 extern void wide_int_cc_tests ();
+extern void opt_proposer_c_tests ();
 
 extern int num_passes;
 
@@ -402,6 +412,16 @@ extern int num_passes;
                                   (HAYSTACK), (NEEDLE));               \
   SELFTEST_END_STMT
 
+/* Evaluate STR and PREFIX and determine if STR starts with PREFIX.
+     ::selftest::pass if STR does start with PREFIX.
+     ::selftest::fail if does not, or either is NULL.  */
+
+#define ASSERT_STR_STARTSWITH(STR, PREFIX)                                 \
+  SELFTEST_BEGIN_STMT                                                      \
+  ::selftest::assert_str_startswith (SELFTEST_LOCATION, #STR, #PREFIX,     \
+                                    (STR), (PREFIX));                      \
+  SELFTEST_END_STMT
+
 /* Evaluate PRED1 (VAL1), calling ::selftest::pass if it is true,
    ::selftest::fail if it is false.  */