This patch adds hints to the option-not-found error in the driver, using the Levenshtein distance implementation posted here: "[PATCH 0/2] Levenshtein-based suggestions (v3)" https://gcc.gnu.org/ml/gcc-patches/2015-10/msg03379.html
It splits out the identifier-based implementation into a new spellcheck-tree.c, keeping the core in spellcheck.c, since the tree checking code needed for IDENTIFIER_POINTER etc isn't available in the driver. Bootstrapped®rtested on top of the other patch kit (with nit fixes) on x86_64-pc-linux-gnu; adds 5 PASS results to gcc.sum. OK for trunk as a followup to that patch kit? gcc/ChangeLog: PR driver/67613 * Makefile.in (GCC_OBJS): Add spellcheck.o. (OBJS): Add spellcheck-tree.o. * gcc.c: Include "spellcheck.h". (suggest_option): New function. (driver::handle_unrecognized_options): Call suggest_option to provide a hint about misspelled options. * spellcheck.c: Update file comment. (levenshtein_distance): Convert 4-param implementation from static to extern scope. Remove note about unit tests from leading comment for const char * implementation. Move tree implementation to... * spellcheck-tree.c: New file. * spellcheck.h (levenshtein_distance): Add 4-param decl. gcc/testsuite/ChangeLog: PR driver/67613 * gcc/testsuite/gcc.dg/spellcheck-options-1.c: New file. * gcc/testsuite/gcc.dg/spellcheck-options-2.c: New file. --- gcc/Makefile.in | 3 +- gcc/gcc.c | 51 ++++++++++++++++++++++++++++- gcc/spellcheck-tree.c | 39 ++++++++++++++++++++++ gcc/spellcheck.c | 21 ++---------- gcc/spellcheck.h | 4 +++ gcc/testsuite/gcc.dg/spellcheck-options-1.c | 4 +++ gcc/testsuite/gcc.dg/spellcheck-options-2.c | 5 +++ 7 files changed, 107 insertions(+), 20 deletions(-) create mode 100644 gcc/spellcheck-tree.c create mode 100644 gcc/testsuite/gcc.dg/spellcheck-options-1.c create mode 100644 gcc/testsuite/gcc.dg/spellcheck-options-2.c diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 9fb643e..a57bd40 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1151,7 +1151,7 @@ CXX_TARGET_OBJS=@cxx_target_objs@ FORTRAN_TARGET_OBJS=@fortran_target_objs@ # Object files for gcc many-languages driver. -GCC_OBJS = gcc.o gcc-main.o ggc-none.o +GCC_OBJS = gcc.o gcc-main.o ggc-none.o spellcheck.o c-family-warn = $(STRICT_WARN) @@ -1395,6 +1395,7 @@ OBJS = \ simplify-rtx.o \ sparseset.o \ spellcheck.o \ + spellcheck-tree.o \ sreal.o \ stack-ptr-mod.o \ statistics.o \ diff --git a/gcc/gcc.c b/gcc/gcc.c index bbc9b23..ef0fb4e 100644 --- a/gcc/gcc.c +++ b/gcc/gcc.c @@ -42,6 +42,7 @@ compilation is specified by a string called a "spec". */ #include "opts.h" #include "params.h" #include "filenames.h" +#include "spellcheck.h" @@ -7598,6 +7599,45 @@ driver::maybe_putenv_OFFLOAD_TARGETS () const offload_targets = NULL; } +/* 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. */ + +static const char * +suggest_option (const char *bad_opt) +{ + const cl_option *best_option = NULL; + edit_distance_t best_distance = MAX_EDIT_DISTANCE; + + 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; +} + /* Reject switches that no pass was interested in. */ void @@ -7605,7 +7645,16 @@ driver::handle_unrecognized_options () const { for (size_t i = 0; (int) i < n_switches; i++) if (! switches[i].validated) - error ("unrecognized command line option %<-%s%>", switches[i].part1); + { + const char *hint = suggest_option (switches[i].part1); + if (hint) + error ("unrecognized command line option %<-%s%>;" + " did you mean %<-%s%>?", + switches[i].part1, hint); + else + error ("unrecognized command line option %<-%s%>", + switches[i].part1); + } } /* Handle the various -print-* options, returning 0 if the driver diff --git a/gcc/spellcheck-tree.c b/gcc/spellcheck-tree.c new file mode 100644 index 0000000..d203776 --- /dev/null +++ b/gcc/spellcheck-tree.c @@ -0,0 +1,39 @@ +/* Find near-matches for identifiers. + Copyright (C) 2015 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +<http://www.gnu.org/licenses/>. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "tm.h" +#include "tree.h" +#include "spellcheck.h" + +/* Calculate Levenshtein distance between two identifiers. */ + +edit_distance_t +levenshtein_distance (tree ident_s, tree ident_t) +{ + gcc_assert (TREE_CODE (ident_s) == IDENTIFIER_NODE); + gcc_assert (TREE_CODE (ident_t) == IDENTIFIER_NODE); + + return levenshtein_distance (IDENTIFIER_POINTER (ident_s), + IDENTIFIER_LENGTH (ident_s), + IDENTIFIER_POINTER (ident_t), + IDENTIFIER_LENGTH (ident_t)); +} diff --git a/gcc/spellcheck.c b/gcc/spellcheck.c index 4d4c176..31fd22b 100644 --- a/gcc/spellcheck.c +++ b/gcc/spellcheck.c @@ -1,4 +1,4 @@ -/* Find near-matches for strings and identifiers. +/* Find near-matches for strings. Copyright (C) 2015 Free Software Foundation, Inc. This file is part of GCC. @@ -30,7 +30,7 @@ along with GCC; see the file COPYING3. If not see This implementation uses the Wagner-Fischer algorithm. */ -static edit_distance_t +edit_distance_t levenshtein_distance (const char *s, int len_s, const char *t, int len_t) { @@ -112,25 +112,10 @@ levenshtein_distance (const char *s, int len_s, return result; } -/* Calculate Levenshtein distance between two nil-terminated strings. - This exists purely for the unit tests. */ +/* Calculate Levenshtein distance between two nil-terminated strings. */ edit_distance_t levenshtein_distance (const char *s, const char *t) { return levenshtein_distance (s, strlen (s), t, strlen (t)); } - -/* Calculate Levenshtein distance between two identifiers. */ - -edit_distance_t -levenshtein_distance (tree ident_s, tree ident_t) -{ - gcc_assert (TREE_CODE (ident_s) == IDENTIFIER_NODE); - gcc_assert (TREE_CODE (ident_t) == IDENTIFIER_NODE); - - return levenshtein_distance (IDENTIFIER_POINTER (ident_s), - IDENTIFIER_LENGTH (ident_s), - IDENTIFIER_POINTER (ident_t), - IDENTIFIER_LENGTH (ident_t)); -} diff --git a/gcc/spellcheck.h b/gcc/spellcheck.h index 58355d6..673a756 100644 --- a/gcc/spellcheck.h +++ b/gcc/spellcheck.h @@ -24,6 +24,10 @@ typedef unsigned int edit_distance_t; const edit_distance_t MAX_EDIT_DISTANCE = UINT_MAX; extern edit_distance_t +levenshtein_distance (const char *s, int len_s, + const char *t, int len_t); + +extern edit_distance_t levenshtein_distance (const char *s, const char *t); extern edit_distance_t diff --git a/gcc/testsuite/gcc.dg/spellcheck-options-1.c b/gcc/testsuite/gcc.dg/spellcheck-options-1.c new file mode 100644 index 0000000..cd5fdca --- /dev/null +++ b/gcc/testsuite/gcc.dg/spellcheck-options-1.c @@ -0,0 +1,4 @@ +/* { dg-do compile } */ +/* { dg-options "-Wcoercion" } */ +/* { dg-error "unrecognized command line option '-Wcoercion'; did you mean '-Wconversion'?" "" { target *-*-* } 0 } */ + diff --git a/gcc/testsuite/gcc.dg/spellcheck-options-2.c b/gcc/testsuite/gcc.dg/spellcheck-options-2.c new file mode 100644 index 0000000..786266d --- /dev/null +++ b/gcc/testsuite/gcc.dg/spellcheck-options-2.c @@ -0,0 +1,5 @@ +/* { dg-do compile } */ +/* { dg-options "-Wthis-should-not-get-a-hint" } */ +/* { dg-bogus "did you mean" "" { target *-*-* } 0 } */ +/* { dg-error "unrecognized command line option '-Wthis-should-not-get-a-hint'" "" { target *-*-* } 0 } */ + -- 1.8.5.3