Home | History | Annotate | Line # | Download | only in gcc
opt-suggestions.cc revision 1.1.1.1
      1 /* Provide option suggestion for --complete option and a misspelled
      2    used by a user.
      3    Copyright (C) 2016-2022 Free Software Foundation, Inc.
      4 
      5 This file is part of GCC.
      6 
      7 GCC is free software; you can redistribute it and/or modify it under
      8 the terms of the GNU General Public License as published by the Free
      9 Software Foundation; either version 3, or (at your option) any later
     10 version.
     11 
     12 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
     13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15 for more details.
     16 
     17 You should have received a copy of the GNU General Public License
     18 along with GCC; see the file COPYING3.  If not see
     19 <http://www.gnu.org/licenses/>.  */
     20 
     21 #include "config.h"
     22 #include "system.h"
     23 #include "coretypes.h"
     24 #include "tm.h"
     25 #include "opts.h"
     26 #include "spellcheck.h"
     27 #include "opt-suggestions.h"
     28 #include "common/common-target.h"
     29 #include "selftest.h"
     30 
     31 option_proposer::~option_proposer ()
     32 {
     33   delete m_option_suggestions;
     34 }
     35 
     36 const char *
     37 option_proposer::suggest_option (const char *bad_opt)
     38 {
     39   /* Lazily populate m_option_suggestions.  */
     40   if (!m_option_suggestions)
     41     build_option_suggestions (NULL);
     42   gcc_assert (m_option_suggestions);
     43 
     44   /* "m_option_suggestions" is now populated.  Use it.  */
     45   return find_closest_string
     46     (bad_opt,
     47      (auto_vec <const char *> *) m_option_suggestions);
     48 }
     49 
     50 /* Populate RESULTS with valid completions of options that begin
     51    with OPTION_PREFIX.  */
     52 
     53 void
     54 option_proposer::get_completions (const char *option_prefix,
     55 				  auto_string_vec &results)
     56 {
     57   /* Bail out for an invalid input.  */
     58   if (option_prefix == NULL || option_prefix[0] == '\0')
     59     return;
     60 
     61   /* Option suggestions are built without first leading dash character.  */
     62   if (option_prefix[0] == '-')
     63     option_prefix++;
     64 
     65   size_t length = strlen (option_prefix);
     66 
     67   /* Lazily populate m_option_suggestions.  */
     68   if (!m_option_suggestions)
     69     build_option_suggestions (option_prefix);
     70   gcc_assert (m_option_suggestions);
     71 
     72   for (unsigned i = 0; i < m_option_suggestions->length (); i++)
     73     {
     74       char *candidate = (*m_option_suggestions)[i];
     75       if (strlen (candidate) >= length
     76 	  && strstr (candidate, option_prefix) == candidate)
     77 	results.safe_push (concat ("-", candidate, NULL));
     78     }
     79 }
     80 
     81 /* Print on stdout a list of valid options that begin with OPTION_PREFIX,
     82    one per line, suitable for use by Bash completion.
     83 
     84    Implementation of the "-completion=" option.  */
     85 
     86 void
     87 option_proposer::suggest_completion (const char *option_prefix)
     88 {
     89   auto_string_vec results;
     90   get_completions (option_prefix, results);
     91   for (unsigned i = 0; i < results.length (); i++)
     92     printf ("%s\n", results[i]);
     93 }
     94 
     95 void
     96 option_proposer::build_option_suggestions (const char *prefix)
     97 {
     98   gcc_assert (m_option_suggestions == NULL);
     99   m_option_suggestions = new auto_string_vec ();
    100 
    101   /* We build a vec of m_option_suggestions, using add_misspelling_candidates
    102      to add copies of strings, without a leading dash.  */
    103 
    104   for (unsigned int i = 0; i < cl_options_count; i++)
    105     {
    106       const struct cl_option *option = &cl_options[i];
    107       const char *opt_text = option->opt_text;
    108       switch (i)
    109 	{
    110 	default:
    111 	  if (option->var_type == CLVC_ENUM)
    112 	    {
    113 	      const struct cl_enum *e = &cl_enums[option->var_enum];
    114 	      for (unsigned j = 0; e->values[j].arg != NULL; j++)
    115 		{
    116 		  char *with_arg = concat (opt_text, e->values[j].arg, NULL);
    117 		  add_misspelling_candidates (m_option_suggestions, option,
    118 					      with_arg);
    119 		  free (with_arg);
    120 		}
    121 
    122 	      /* Add also variant without an option argument.  */
    123 	      add_misspelling_candidates (m_option_suggestions, option,
    124 					  opt_text);
    125 	    }
    126 	  else
    127 	    {
    128 	      bool option_added = false;
    129 	      if (option->flags & CL_TARGET)
    130 		{
    131 		  vec<const char *> option_values
    132 		    = targetm_common.get_valid_option_values (i, prefix);
    133 		  if (!option_values.is_empty ())
    134 		    {
    135 		      option_added = true;
    136 		      for (unsigned j = 0; j < option_values.length (); j++)
    137 			{
    138 			  char *with_arg = concat (opt_text, option_values[j],
    139 						   NULL);
    140 			  add_misspelling_candidates (m_option_suggestions, option,
    141 						      with_arg);
    142 			  free (with_arg);
    143 			}
    144 		    }
    145 		  option_values.release ();
    146 		}
    147 
    148 	      if (!option_added)
    149 		add_misspelling_candidates (m_option_suggestions, option,
    150 					    opt_text);
    151 	    }
    152 	  break;
    153 
    154 	case OPT_fsanitize_:
    155 	case OPT_fsanitize_recover_:
    156 	  /* -fsanitize= and -fsanitize-recover= can take
    157 	     a comma-separated list of arguments.  Given that combinations
    158 	     are supported, we can't add all potential candidates to the
    159 	     vec, but if we at least add them individually without commas,
    160 	     we should do a better job e.g. correcting
    161 	       "-sanitize=address"
    162 	     to
    163 	       "-fsanitize=address"
    164 	     rather than to "-Wframe-address" (PR driver/69265).  */
    165 	  {
    166 	    /* Add also variant without an option argument.  */
    167 	    add_misspelling_candidates (m_option_suggestions, option,
    168 					opt_text);
    169 
    170 	    struct cl_option optb;
    171 	    for (int j = 0; sanitizer_opts[j].name != NULL; ++j)
    172 	      {
    173 		/* -fsanitize=all is not valid, only -fno-sanitize=all.
    174 		   So don't register the positive misspelling candidates
    175 		   for it.  */
    176 		if (sanitizer_opts[j].flag == ~0U && i == OPT_fsanitize_)
    177 		  {
    178 		    optb = *option;
    179 		    optb.opt_text = opt_text = "-fno-sanitize=";
    180 		    optb.cl_reject_negative = true;
    181 		    option = &optb;
    182 		  }
    183 		/* Get one arg at a time e.g. "-fsanitize=address".  */
    184 		char *with_arg = concat (opt_text,
    185 					 sanitizer_opts[j].name,
    186 					 NULL);
    187 		/* Add with_arg and all of its variant spellings e.g.
    188 		   "-fno-sanitize=address" to candidates (albeit without
    189 		   leading dashes).  */
    190 		add_misspelling_candidates (m_option_suggestions, option,
    191 					    with_arg);
    192 		free (with_arg);
    193 	      }
    194 	  }
    195 	  break;
    196 	}
    197     }
    198 }
    199 
    200 #if CHECKING_P
    201 
    202 namespace selftest {
    203 
    204 /* Verify that PROPOSER generates sane auto-completion suggestions
    205    for OPTION_PREFIX.  */
    206 
    207 static void
    208 verify_autocompletions (option_proposer &proposer, const char *option_prefix)
    209 {
    210   auto_string_vec suggestions;
    211   proposer.get_completions (option_prefix, suggestions);
    212 
    213   /* There must be at least one suggestion, and every suggestion must
    214      indeed begin with OPTION_PREFIX.  */
    215 
    216   ASSERT_GT (suggestions.length (), 0);
    217 
    218   for (unsigned i = 0; i < suggestions.length (); i++)
    219     ASSERT_STR_STARTSWITH (suggestions[i], option_prefix);
    220 }
    221 
    222 /* Verify that valid options are auto-completed correctly.  */
    223 
    224 static void
    225 test_completion_valid_options (option_proposer &proposer)
    226 {
    227   const char *option_prefixes[] =
    228   {
    229     "-fno-var-tracking-assignments-toggle",
    230     "-fpredictive-commoning",
    231     "--param=stack-clash-protection-guard-size",
    232     "--param=max-predicted-iterations",
    233     "-ftree-loop-distribute-patterns",
    234     "-fno-var-tracking",
    235     "-Walloc-zero",
    236     "--param=ipa-cp-value-list-size",
    237     "-Wsync-nand",
    238     "-Wno-attributes",
    239     "--param=tracer-dynamic-coverage-feedback",
    240     "-Wno-format-contains-nul",
    241     "-Wnamespaces",
    242     "-fisolate-erroneous-paths-attribute",
    243     "-Wno-underflow",
    244     "-Wtarget-lifetime",
    245     "--param=asan-globals",
    246     "-Wno-empty-body",
    247     "-Wno-odr",
    248     "-Wformat-zero-length",
    249     "-Wstringop-truncation",
    250     "-fno-ipa-vrp",
    251     "-fmath-errno",
    252     "-Warray-temporaries",
    253     "-Wno-unused-label",
    254     "-Wreturn-local-addr",
    255     "--param=sms-dfa-history",
    256     "--param=asan-instrument-reads",
    257     "-Wreturn-type",
    258     "-Wc++17-compat",
    259     "-Wno-effc++",
    260     "--param=max-fields-for-field-sensitive",
    261     "-fisolate-erroneous-paths-dereference",
    262     "-fno-defer-pop",
    263     "-Wcast-align=strict",
    264     "-foptimize-strlen",
    265     "-Wpacked-not-aligned",
    266     "-funroll-loops",
    267     "-fif-conversion2",
    268     "-Wdesignated-init",
    269     "--param=max-iterations-computation-cost",
    270     "-Wmultiple-inheritance",
    271     "-fno-sel-sched-reschedule-pipelined",
    272     "-Wassign-intercept",
    273     "-Wno-format-security",
    274     "-fno-sched-stalled-insns",
    275     "-fno-tree-tail-merge",
    276     "-Wlong-long",
    277     "-Wno-unused-but-set-parameter",
    278     NULL
    279   };
    280 
    281   for (const char **ptr = option_prefixes; *ptr != NULL; ptr++)
    282     verify_autocompletions (proposer, *ptr);
    283 }
    284 
    285 /* Verify that valid parameters are auto-completed correctly,
    286    both with the "--param=PARAM" form and the "--param PARAM" form.  */
    287 
    288 static void
    289 test_completion_valid_params (option_proposer &proposer)
    290 {
    291   const char *option_prefixes[] =
    292   {
    293     "--param=sched-state-edge-prob-cutoff",
    294     "--param=iv-consider-all-candidates-bound",
    295     "--param=align-threshold",
    296     "--param=prefetch-min-insn-to-mem-ratio",
    297     "--param=max-unrolled-insns",
    298     "--param=max-early-inliner-iterations",
    299     "--param=max-vartrack-reverse-op-size",
    300     "--param=ipa-cp-loop-hint-bonus",
    301     "--param=tracer-min-branch-ratio",
    302     "--param=graphite-max-arrays-per-scop",
    303     "--param=sink-frequency-threshold",
    304     "--param=max-cse-path-length",
    305     "--param=sra-max-scalarization-size-Osize",
    306     "--param=prefetch-latency",
    307     "--param=dse-max-object-size",
    308     "--param=asan-globals",
    309     "--param=max-vartrack-size",
    310     "--param=case-values-threshold",
    311     "--param=max-slsr-cand-scan",
    312     "--param=min-insn-to-prefetch-ratio",
    313     "--param=tracer-min-branch-probability",
    314     "--param sink-frequency-threshold",
    315     "--param max-cse-path-length",
    316     "--param sra-max-scalarization-size-Osize",
    317     "--param prefetch-latency",
    318     "--param dse-max-object-size",
    319     "--param asan-globals",
    320     "--param max-vartrack-size",
    321     NULL
    322   };
    323 
    324   for (const char **ptr = option_prefixes; *ptr != NULL; ptr++)
    325     verify_autocompletions (proposer, *ptr);
    326 }
    327 
    328 /* Return true when EXPECTED is one of completions for OPTION_PREFIX string.  */
    329 
    330 static bool
    331 in_completion_p (option_proposer &proposer, const char *option_prefix,
    332 		 const char *expected)
    333 {
    334   auto_string_vec suggestions;
    335   proposer.get_completions (option_prefix, suggestions);
    336 
    337   for (unsigned i = 0; i < suggestions.length (); i++)
    338     {
    339       char *r = suggestions[i];
    340       if (strcmp (r, expected) == 0)
    341 	return true;
    342     }
    343 
    344   return false;
    345 }
    346 
    347 /* Return true when PROPOSER does not find any partial completion
    348    for OPTION_PREFIX.  */
    349 
    350 static bool
    351 empty_completion_p (option_proposer &proposer, const char *option_prefix)
    352 {
    353   auto_string_vec suggestions;
    354   proposer.get_completions (option_prefix, suggestions);
    355   return suggestions.is_empty ();
    356 }
    357 
    358 /* Verify autocompletions of partially-complete options.  */
    359 
    360 static void
    361 test_completion_partial_match (option_proposer &proposer)
    362 {
    363   ASSERT_TRUE (in_completion_p (proposer, "-fsani", "-fsanitize=address"));
    364   ASSERT_TRUE (in_completion_p (proposer, "-fsani",
    365 				"-fsanitize-address-use-after-scope"));
    366   ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf-functions"));
    367   ASSERT_TRUE (in_completion_p (proposer, "-fipa-icf", "-fipa-icf"));
    368   ASSERT_TRUE (in_completion_p (proposer, "--param=",
    369 				"--param=max-vartrack-reverse-op-size="));
    370   ASSERT_TRUE (in_completion_p (proposer, "--param ",
    371 				"--param max-vartrack-reverse-op-size="));
    372 
    373   ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf", "-fipa"));
    374   ASSERT_FALSE (in_completion_p (proposer, "-fipa-icf-functions", "-fipa-icf"));
    375 
    376   ASSERT_FALSE (empty_completion_p (proposer, "-"));
    377   ASSERT_FALSE (empty_completion_p (proposer, "-fipa"));
    378   ASSERT_FALSE (empty_completion_p (proposer, "--par"));
    379 }
    380 
    381 /* Verify that autocompletion does not return any match for garbage inputs.  */
    382 
    383 static void
    384 test_completion_garbage (option_proposer &proposer)
    385 {
    386   ASSERT_TRUE (empty_completion_p (proposer, NULL));
    387   ASSERT_TRUE (empty_completion_p (proposer, ""));
    388   ASSERT_TRUE (empty_completion_p (proposer, "- "));
    389   ASSERT_TRUE (empty_completion_p (proposer, "123456789"));
    390   ASSERT_TRUE (empty_completion_p (proposer, "---------"));
    391   ASSERT_TRUE (empty_completion_p (proposer, "#########"));
    392   ASSERT_TRUE (empty_completion_p (proposer, "- - - - - -"));
    393   ASSERT_TRUE (empty_completion_p (proposer, "-fsanitize=address2"));
    394 }
    395 
    396 /* Run all of the selftests within this file.  */
    397 
    398 void
    399 opt_suggestions_cc_tests ()
    400 {
    401   option_proposer proposer;
    402 
    403   test_completion_valid_options (proposer);
    404   test_completion_valid_params (proposer);
    405   test_completion_partial_match (proposer);
    406   test_completion_garbage (proposer);
    407 }
    408 
    409 } // namespace selftest
    410 
    411 #endif /* #if CHECKING_P */
    412