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