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
This commit is contained in:
David Malcolm 2016-02-12 17:39:27 +00:00 committed by David Malcolm
parent c3090c1f52
commit 61789eedf8
17 changed files with 283 additions and 39 deletions

View File

@ -1,3 +1,30 @@
2016-02-12 David Malcolm <dmalcolm@redhat.com>
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.
2016-02-12 Jakub Jelinek <jakub@redhat.com>
PR rtl-optimization/69764

120
gcc/gcc.c
View File

@ -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::suggest_option. Populate
m_option_suggestions with candidate strings for misspelled options.
The strings will be freed by the driver's dtor. */
void
driver::build_option_suggestions (void)
{
gcc_assert (m_option_suggestions == NULL);
m_option_suggestions = new auto_vec <char *> ();
/* 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++)
{
const struct cl_option *option = &cl_options[i];
const char *opt_text = option->opt_text;
switch (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;
}
}
}
/* Helper function for driver::handle_unrecognized_options.
Given an unrecognized option BAD_OPT (without the leading dash),
locate the closest reasonable matching option (again, without the
leading dash), or NULL. */
leading dash), or NULL.
static const char *
suggest_option (const char *bad_opt)
The returned string is owned by the driver instance. */
const char *
driver::suggest_option (const char *bad_opt)
{
const cl_option *best_option = NULL;
edit_distance_t best_distance = MAX_EDIT_DISTANCE;
/* Lazily populate m_option_suggestions. */
if (!m_option_suggestions)
build_option_suggestions ();
gcc_assert (m_option_suggestions);
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)
{
best_distance = dist;
best_option = &cl_options[i];
}
}
if (!best_option)
return NULL;
/* 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;
}
return best_option->opt_text + 1;
/* "m_option_suggestions" is now populated. Use it. */
return find_closest_string
(bad_opt,
(auto_vec <const char *> *) 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)

View File

@ -45,7 +45,9 @@ class driver
void putenv_COLLECT_GCC (const char *argv0) const;
void maybe_putenv_COLLECT_LTO_WRAPPER () const;
void maybe_putenv_OFFLOAD_TARGETS () const;
void handle_unrecognized_options () const;
void build_option_suggestions (void);
const char *suggest_option (const char *bad_opt);
void handle_unrecognized_options ();
int maybe_print_and_exit () const;
bool prepare_infiles ();
void do_spec_on_infiles () const;
@ -57,6 +59,7 @@ class driver
char *explicit_link_files;
struct cl_decoded_option *decoded_options;
unsigned int decoded_options_count;
auto_vec <char *> *m_option_suggestions;
};
/* The mapping of a spec function name to the C function that

View File

@ -365,6 +365,47 @@ static const struct option_map option_map[] =
{ "--no-", NULL, "-f", false, true }
};
/* Helper function for gcc.c's driver::suggest_option, for populating the
vec of suggestions for misspelled options.
option_map above provides various prefixes for spelling command-line
options, which decode_cmdline_option uses to map spellings of options
to specific options. We want to do the reverse: to find all the ways
that a user could validly spell an option.
Given valid OPT_TEXT (with a leading dash), add it and all of its valid
variant spellings to CANDIDATES, each without a leading dash.
For example, given "-Wabi-tag", the following are added to CANDIDATES:
"Wabi-tag"
"Wno-abi-tag"
"-warn-abi-tag"
"-warn-no-abi-tag".
The added strings must be freed using free. */
void
add_misspelling_candidates (auto_vec<char *> *candidates,
const char *opt_text)
{
gcc_assert (candidates);
gcc_assert (opt_text);
candidates->safe_push (xstrdup (opt_text + 1));
for (unsigned i = 0; i < ARRAY_SIZE (option_map); i++)
{
const char *opt0 = option_map[i].opt0;
const char *new_prefix = option_map[i].new_prefix;
size_t new_prefix_len = strlen (new_prefix);
if (strncmp (opt_text, new_prefix, new_prefix_len) == 0)
{
char *alternative = concat (opt0 + 1, opt_text + new_prefix_len,
NULL);
candidates->safe_push (alternative);
}
}
}
/* Decode the switch beginning at ARGV for the language indicated by
LANG_MASK (including CL_COMMON and CL_TARGET if applicable), into
the structure *DECODED. Returns the number of switches

View File

@ -1434,12 +1434,7 @@ enable_fdo_optimizations (struct gcc_options *opts,
}
/* -f{,no-}sanitize{,-recover}= suboptions. */
static const struct sanitizer_opts_s
{
const char *const name;
unsigned int flag;
size_t len;
} sanitizer_opts[] =
const struct sanitizer_opts_s sanitizer_opts[] =
{
#define SANITIZER_OPT(name, flags) { #name, flags, sizeof #name - 1 }
SANITIZER_OPT (address, SANITIZE_ADDRESS | SANITIZE_USER_ADDRESS),

View File

@ -404,4 +404,15 @@ extern void set_struct_debug_option (struct gcc_options *opts,
const char *value);
extern bool opt_enum_arg_to_value (size_t opt_index, const char *arg,
int *value, unsigned int lang_mask);
extern const struct sanitizer_opts_s
{
const char *const name;
unsigned int flag;
size_t len;
} sanitizer_opts[];
extern void add_misspelling_candidates (auto_vec<char *> *candidates,
const char *base_option);
#endif

View File

@ -119,3 +119,49 @@ levenshtein_distance (const char *s, const char *t)
{
return levenshtein_distance (s, strlen (s), t, strlen (t));
}
/* Given TARGET, a non-NULL string, and CANDIDATES, a non-NULL ptr to
an autovec of non-NULL strings, determine which element within
CANDIDATES has the lowest edit distance to TARGET. If there are
multiple elements with the same minimal distance, the first in the
vector wins.
If more than half of the letters were misspelled, the suggestion is
likely to be meaningless, so return NULL for this case. */
const char *
find_closest_string (const char *target,
const auto_vec<const char *> *candidates)
{
gcc_assert (target);
gcc_assert (candidates);
int i;
const char *candidate;
const char *best_candidate = NULL;
edit_distance_t best_distance = MAX_EDIT_DISTANCE;
size_t len_target = strlen (target);
FOR_EACH_VEC_ELT (*candidates, i, candidate)
{
gcc_assert (candidate);
edit_distance_t dist
= levenshtein_distance (target, len_target,
candidate, strlen (candidate));
if (dist < best_distance)
{
best_distance = dist;
best_candidate = candidate;
}
}
/* If more than half of the letters were misspelled, the suggestion is
likely to be meaningless. */
if (best_candidate)
{
unsigned int cutoff = MAX (len_target, strlen (best_candidate)) / 2;
if (best_distance > cutoff)
return NULL;
}
return best_candidate;
}

View File

@ -31,6 +31,10 @@ levenshtein_distance (const char *s, int len_s,
extern edit_distance_t
levenshtein_distance (const char *s, const char *t);
extern const char *
find_closest_string (const char *target,
const auto_vec<const char *> *candidates);
/* spellcheck-tree.c */
extern edit_distance_t

View File

@ -1,3 +1,16 @@
2016-02-12 David Malcolm <dmalcolm@redhat.com>
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.
2016-02-12 Jakub Jelinek <jakub@redhat.com>
PR rtl-optimization/69764

View File

@ -0,0 +1,6 @@
/* Verify that we include -Wno- variants when considering hints
for misspelled options (PR driver/69453). */
/* { dg-do compile } */
/* { dg-options "-fno-if-convert" } */
/* { dg-error "unrecognized command line option .-fno-if-convert.; did you mean .-fno-if-conversion.?" "" { target *-*-* } 0 } */

View File

@ -0,0 +1,6 @@
/* Verify that we provide simple suggestions for the arguments of
"-fsanitize=" when it is misspelled (PR driver/69265). */
/* { dg-do compile } */
/* { dg-options "-sanitize=address" } */
/* { dg-error "unrecognized command line option '-sanitize=address'; did you mean '-fsanitize=address'?" "" { target *-*-* } 0 } */

View File

@ -0,0 +1,6 @@
/* Verify that we provide simple suggestions for the arguments of
"-fsanitize-recover=" when it is misspelled (PR driver/69265). */
/* { dg-do compile } */
/* { dg-options "-sanitize-recover=integer-divide-by-0" } */
/* { dg-error "unrecognized command line option '-sanitize-recover=integer-divide-by-0'; did you mean '-fsanitize-recover=integer-divide-by-zero'?" "" { target *-*-* } 0 } */

View File

@ -0,0 +1,6 @@
/* Verify that we provide suggestions (with arguments) for the "-fno-"
variant of "-fsanitize=" when it is misspelled (PR driver/69265). */
/* { dg-do compile } */
/* { dg-options "-no-sanitize=all" } */
/* { dg-error "unrecognized command line option '-no-sanitize=all'; did you mean '-fno-sanitize=all'?" "" { target *-*-* } 0 } */

View File

@ -0,0 +1,6 @@
/* Verify that we can generate a suggestion of "--warn-no-abi-tag"
from c.opt's "Wabi-tag" (PR driver/69265). */
/* { dg-do compile } */
/* { dg-options "-fwarn-no-abi-tag" } */
/* { dg-error "unrecognized command line option '-fwarn-no-abi-tag'; did you mean '--warn-no-abi-tag'?" "" { target *-*-* } 0 } */

View File

@ -0,0 +1,6 @@
/* Verify that we provide a hint if the user misspells an option that
takes an argument (PR driver/69265). */
/* { dg-do compile } */
/* { dg-options "-tls-model=global-dynamic" } */
/* { dg-error "unrecognized command line option '-tls-model=global-dynamic'; did you mean '-ftls-model=global-dynamic'?" "" { target *-*-* } 0 } */

View File

@ -0,0 +1,6 @@
/* Verify that we include -Wno- variants when considering hints
for misspelled options (PR driver/69453). */
/* { dg-do compile } */
/* { dg-options "--Wno-narrowing" } */
/* { dg-error "unrecognized command line option '--Wno-narrowing'; did you mean '-Wno-narrowing'?" "" { target *-*-* } 0 } */

View File

@ -0,0 +1,6 @@
/* Verify that we include -Wno- variants when considering hints
for misspelled options (PR driver/69453). */
/* { dg-do compile } */
/* { dg-options "-fmo-unroll-loops" } */
/* { dg-error "unrecognized command line option '-fmo-unroll-loops'; did you mean '-fno-unroll-loops'?" "" { target *-*-* } 0 } */