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 5This file is part of GCC. 6 7GCC is free software; you can redistribute it and/or modify it under 8the terms of the GNU General Public License as published by the Free 9Software Foundation; either version 3, or (at your option) any later 10version. 11 12GCC is distributed in the hope that it will be useful, but WITHOUT ANY 13WARRANTY; without even the implied warranty of MERCHANTABILITY or 14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 15for more details. 16 17You should have received a copy of the GNU General Public License 18along 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 31option_proposer::~option_proposer () 32{ 33 delete m_option_suggestions; 34} 35 36const char * 37option_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 53void 54option_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 86void 87option_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 95void 96option_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 for (int j = 0; sanitizer_opts[j].name != NULL; ++j) 171 { 172 struct cl_option optb; 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 202namespace selftest { 203 204/* Verify that PROPOSER generates sane auto-completion suggestions 205 for OPTION_PREFIX. */ 206 207static void 208verify_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 224static void 225test_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 288static void 289test_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 330static bool 331in_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 350static bool 351empty_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 360static void 361test_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 383static void 384test_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 398void 399opt_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