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