From 61789eedf831541b415691f7376a83fa81e6d73b Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Fri, 12 Feb 2016 17:39:27 +0000 Subject: PR driver/69265 and 69453: improved suggestions for various misspelled options gcc/ChangeLog: PR driver/69265 PR driver/69453 * gcc.c (driver::driver): Initialize m_option_suggestions. (driver::~driver): Clean up m_option_suggestions. (suggest_option): Convert to... (driver::suggest_option): ...this, and split out into driver::build_option_suggestions and find_closest_string. (driver::build_option_suggestions): New function, from first half of suggest_option. Special-case OPT_fsanitize_ and OPT_fsanitize_recover_, making use of the sanitizer_opts array. For options of enum types, add the various enum values to the candidate strings. (driver::handle_unrecognized_options): Remove "const". * gcc.h (driver::handle_unrecognized_options): Likewise. (driver::build_option_suggestions): New decl. (driver::suggest_option): New decl. (driver::m_option_suggestions): New field. * opts-common.c (add_misspelling_candidates): New function. * opts.c (sanitizer_opts): Remove decl of struct sanitizer_opts_s and make non-static. * opts.h (sanitizer_opts): New array decl. (add_misspelling_candidates): New function decl. * spellcheck.c (find_closest_string): New function. * spellcheck.h (find_closest_string): New function decl. gcc/testsuite/ChangeLog: PR driver/69265 PR driver/69453 * gcc.dg/spellcheck-options-3.c: New test case. * gcc.dg/spellcheck-options-4.c: New test case. * gcc.dg/spellcheck-options-5.c: New test case. * gcc.dg/spellcheck-options-6.c: New test case. * gcc.dg/spellcheck-options-7.c: New test case. * gcc.dg/spellcheck-options-8.c: New test case. * gcc.dg/spellcheck-options-9.c: New test case. * gcc.dg/spellcheck-options-10.c: New test case. From-SVN: r233382 --- gcc/gcc.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 28 deletions(-) (limited to 'gcc/gcc.c') diff --git a/gcc/gcc.c b/gcc/gcc.c index 683b30f..99fa5e3 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -7135,7 +7135,8 @@ compare_files (char *cmpfile[]) driver::driver (bool can_finalize, bool debug) : explicit_link_files (NULL), - decoded_options (NULL) + decoded_options (NULL), + m_option_suggestions (NULL) { env.init (can_finalize, debug); } @@ -7144,6 +7145,14 @@ driver::~driver () { XDELETEVEC (explicit_link_files); XDELETEVEC (decoded_options); + if (m_option_suggestions) + { + int i; + char *str; + FOR_EACH_VEC_ELT (*m_option_suggestions, i, str) + free (str); + delete m_option_suggestions; + } } /* driver::main is implemented as a series of driver:: method calls. */ @@ -7632,49 +7641,96 @@ driver::maybe_putenv_OFFLOAD_TARGETS () const offload_targets = NULL; } -/* Helper function for driver::handle_unrecognized_options. +/* Helper function for driver::suggest_option. Populate + m_option_suggestions with candidate strings for misspelled options. + The strings will be freed by the driver's dtor. */ - Given an unrecognized option BAD_OPT (without the leading dash), - locate the closest reasonable matching option (again, without the - leading dash), or NULL. */ - -static const char * -suggest_option (const char *bad_opt) +void +driver::build_option_suggestions (void) { - const cl_option *best_option = NULL; - edit_distance_t best_distance = MAX_EDIT_DISTANCE; + gcc_assert (m_option_suggestions == NULL); + m_option_suggestions = new auto_vec (); + + /* We build a vec of m_option_suggestions, using add_misspelling_candidates + to add copies of strings, without a leading dash. */ for (unsigned int i = 0; i < cl_options_count; i++) { - edit_distance_t dist = levenshtein_distance (bad_opt, - cl_options[i].opt_text + 1); - if (dist < best_distance) + const struct cl_option *option = &cl_options[i]; + const char *opt_text = option->opt_text; + switch (i) { - best_distance = dist; - best_option = &cl_options[i]; + default: + if (option->var_type == CLVC_ENUM) + { + const struct cl_enum *e = &cl_enums[option->var_enum]; + for (unsigned j = 0; e->values[j].arg != NULL; j++) + { + char *with_arg = concat (opt_text, e->values[j].arg, NULL); + add_misspelling_candidates (m_option_suggestions, with_arg); + free (with_arg); + } + } + else + add_misspelling_candidates (m_option_suggestions, opt_text); + break; + + case OPT_fsanitize_: + case OPT_fsanitize_recover_: + /* -fsanitize= and -fsanitize-recover= can take + a comma-separated list of arguments. Given that combinations + are supported, we can't add all potential candidates to the + vec, but if we at least add them individually without commas, + we should do a better job e.g. correcting + "-sanitize=address" + to + "-fsanitize=address" + rather than to "-Wframe-address" (PR driver/69265). */ + { + for (int j = 0; sanitizer_opts[j].name != NULL; ++j) + { + /* Get one arg at a time e.g. "-fsanitize=address". */ + char *with_arg = concat (opt_text, + sanitizer_opts[j].name, + NULL); + /* Add with_arg and all of its variant spellings e.g. + "-fno-sanitize=address" to candidates (albeit without + leading dashes). */ + add_misspelling_candidates (m_option_suggestions, with_arg); + free (with_arg); + } + } + break; } } +} - if (!best_option) - return NULL; +/* Helper function for driver::handle_unrecognized_options. - /* If more than half of the letters were misspelled, the suggestion is - likely to be meaningless. */ - if (best_option) - { - unsigned int cutoff = MAX (strlen (bad_opt), - strlen (best_option->opt_text + 1)) / 2; - if (best_distance > cutoff) - return NULL; - } + Given an unrecognized option BAD_OPT (without the leading dash), + locate the closest reasonable matching option (again, without the + leading dash), or NULL. + + The returned string is owned by the driver instance. */ + +const char * +driver::suggest_option (const char *bad_opt) +{ + /* Lazily populate m_option_suggestions. */ + if (!m_option_suggestions) + build_option_suggestions (); + gcc_assert (m_option_suggestions); - return best_option->opt_text + 1; + /* "m_option_suggestions" is now populated. Use it. */ + return find_closest_string + (bad_opt, + (auto_vec *) m_option_suggestions); } /* Reject switches that no pass was interested in. */ void -driver::handle_unrecognized_options () const +driver::handle_unrecognized_options () { for (size_t i = 0; (int) i < n_switches; i++) if (! switches[i].validated) -- cgit v1.1