diff mbox

PR driver/69265: add hint for options with misspelled arguments

Message ID 1462839287-3095-1-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm May 10, 2016, 12:14 a.m. UTC
opts-common.c's cmdline_handle_error handles invalid arguments
for options with CL_ERR_ENUM_ARG by building a strings listing the
valid arguments.  By also building a vec of valid arguments, we
can use find_closest_string and provide a hint if we see a close
misspelling.

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/ChangeLog:
	PR driver/69265
	* Makefile.in (GCC_OBJS): Move spellcheck.o to...
	(OBJS-libcommon-target): ...here.
	* opts-common.c: Include spellcheck.h.
	(cmdline_handle_error): Build a vec of valid options and use it
	to suggest provide hints for misspelled arguments.

gcc/testsuite/ChangeLog:
	PR driver/69265
	* gcc.dg/spellcheck-options-11.c: New test case.
---
 gcc/Makefile.in                              |  4 ++--
 gcc/opts-common.c                            | 11 ++++++++++-
 gcc/testsuite/gcc.dg/spellcheck-options-11.c |  7 +++++++
 3 files changed, 19 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/spellcheck-options-11.c

Comments

Bernhard Reutner-Fischer May 10, 2016, 8:09 p.m. UTC | #1
On Mon, May 09, 2016 at 08:14:47PM -0400, David Malcolm wrote:

> -      inform (loc, "valid arguments to %qs are: %s", option->opt_text, s);
> +      const char *hint = find_closest_string (arg, &candidates);
> +      if (hint)
> +	inform (loc, "valid arguments to %qs are: %s; did you mean %qs?",
> +		option->opt_text, s, hint);
> +      else
> +	inform (loc, "valid arguments to %qs are: %s", option->opt_text, s);
> +

btw.. did you consider a format specifier for this ";did you mean %qs"
common stanza?

inform (loc, "valid arguments to %qs are: %s%qhs", x, y, hint);
(let's say) where the specifier would end up empty if hint was NULL.

I don't really like all these odd conditionals on the hints and it also
adds some bloat to have all these duplicate error strings.

Not sure but maybe this would even help translators?

Is there a -Wno-spelling-suggestions to turn the suggestions off
globally, btw?

thanks,
David Malcolm May 10, 2016, 9:03 p.m. UTC | #2
On Tue, 2016-05-10 at 22:09 +0200, Bernhard Reutner-Fischer wrote:
> On Mon, May 09, 2016 at 08:14:47PM -0400, David Malcolm wrote:
> 
> > -      inform (loc, "valid arguments to %qs are: %s", option
> > ->opt_text, s);
> > +      const char *hint = find_closest_string (arg, &candidates);
> > +      if (hint)
> > +	inform (loc, "valid arguments to %qs are: %s; did you mean
> > %qs?",
> > +		option->opt_text, s, hint);
> > +      else
> > +	inform (loc, "valid arguments to %qs are: %s", option
> > ->opt_text, s);
> > +
> 
> btw.. did you consider a format specifier for this ";did you mean
> %qs"
> common stanza?
> 
> inform (loc, "valid arguments to %qs are: %s%qhs", x, y, hint);
> (let's say) where the specifier would end up empty if hint was NULL.

It would save a conditional, but it seems a bit too much like "magic"
to me; I prefer the explicitness of the existing approach.

> I don't really like all these odd conditionals on the hints and it
> also
> adds some bloat to have all these duplicate error strings.
> 
> Not sure but maybe this would even help translators?

I'm not sure either, but it's generally a bad idea from an i18n
perpective to be building strings at run-time, and the %qhs idea above
feels a bit like that to me.

> Is there a -Wno-spelling-suggestions to turn the suggestions off
> globally, btw?

There isn't one.


Dave
Manuel López-Ibáñez May 10, 2016, 11:56 p.m. UTC | #3
On 10/05/16 22:03, David Malcolm wrote:
> On Tue, 2016-05-10@22:09 +0200, Bernhard Reutner-Fischer wrote:
>> On Mon, May 09, 2016@08:14:47PM -0400, David Malcolm wrote:
>>
>>> -      inform (loc, "valid arguments to %qs are: %s", option
>>> ->opt_text, s);
>>> +      const char *hint = find_closest_string (arg, &candidates);
>>> +      if (hint)
>>> +	inform (loc, "valid arguments to %qs are: %s; did you mean
>>> %qs?",
>>> +		option->opt_text, s, hint);
>>> +      else
>>> +	inform (loc, "valid arguments to %qs are: %s", option
>>> ->opt_text, s);
>>> +
>>
>> btw.. did you consider a format specifier for this ";did you mean
>> %qs"
>> common stanza?
>>
>> inform (loc, "valid arguments to %qs are: %s%qhs", x, y, hint);
>> (let's say) where the specifier would end up empty if hint was NULL.
>
> It would save a conditional, but it seems a bit too much like "magic"
> to me; I prefer the explicitness of the existing approach.

Also, I'm pretty sure that %qX will print `; did you mean hint?', that is, with 
the quotes all messed up. GCC has the same issue when printing typedefs (aka). 
GCC prints:

error: member reference base type 'pid_t {aka int}' is not a structure or union

instead of the more correct quoting used by Clang:

error: member reference base type 'pid_t' (aka 'int') is not a structure or union

But perhaps a wrapper or a dedicated overload can hide some of the details 
within diagnostic.c without requiring a new format specifier:

inform_with_hint (const char *target, const auto_vec<const char *> *candidates,
                   location_t, const char *gmsgid, ...)

Eventually, we should move to an API like the rich_location one, where 
diagnostics can be explicitly constructed, properties can be dynamically added 
and then the diagnostic is explicitly emitted.

Perhaps even an API that uses the Named Parameter Idiom?

diagnostic.at(rich_loc)
	  .with_hint(target, candidates)
	  .inform("something").print()

Cheers,
	Manuel.
Jeff Law May 18, 2016, 10:09 p.m. UTC | #4
On 05/09/2016 06:14 PM, David Malcolm wrote:
> opts-common.c's cmdline_handle_error handles invalid arguments
> for options with CL_ERR_ENUM_ARG by building a strings listing the
> valid arguments.  By also building a vec of valid arguments, we
> can use find_closest_string and provide a hint if we see a close
> misspelling.
>
> Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?
>
> gcc/ChangeLog:
> 	PR driver/69265
> 	* Makefile.in (GCC_OBJS): Move spellcheck.o to...
> 	(OBJS-libcommon-target): ...here.
> 	* opts-common.c: Include spellcheck.h.
> 	(cmdline_handle_error): Build a vec of valid options and use it
> 	to suggest provide hints for misspelled arguments.
>
> gcc/testsuite/ChangeLog:
> 	PR driver/69265
> 	* gcc.dg/spellcheck-options-11.c: New test case.
OK.
jeff
diff mbox

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 6c5adc0..525482f 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1159,7 +1159,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 spellcheck.o
+GCC_OBJS = gcc.o gcc-main.o ggc-none.o
 
 c-family-warn = $(STRICT_WARN)
 
@@ -1548,7 +1548,7 @@  OBJS-libcommon = diagnostic.o diagnostic-color.o diagnostic-show-locus.o \
 # compiler and containing target-dependent code.
 OBJS-libcommon-target = $(common_out_object_file) prefix.o params.o \
 	opts.o opts-common.o options.o vec.o hooks.o common/common-targhooks.o \
-	hash-table.o file-find.o
+	hash-table.o file-find.o spellcheck.o
 
 # This lists all host objects for the front ends.
 ALL_HOST_FRONTEND_OBJS = $(foreach v,$(CONFIG_LANGUAGES),$($(v)_OBJS))
diff --git a/gcc/opts-common.c b/gcc/opts-common.c
index bb68982..4e1ef49 100644
--- a/gcc/opts-common.c
+++ b/gcc/opts-common.c
@@ -24,6 +24,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "opts.h"
 #include "options.h"
 #include "diagnostic.h"
+#include "spellcheck.h"
 
 static void prune_options (struct cl_decoded_option **, unsigned int *);
 
@@ -1113,6 +1114,7 @@  cmdline_handle_error (location_t loc, const struct cl_option *option,
       for (i = 0; e->values[i].arg != NULL; i++)
 	len += strlen (e->values[i].arg) + 1;
 
+      auto_vec <const char *> candidates;
       s = XALLOCAVEC (char, len);
       p = s;
       for (i = 0; e->values[i].arg != NULL; i++)
@@ -1123,9 +1125,16 @@  cmdline_handle_error (location_t loc, const struct cl_option *option,
 	  memcpy (p, e->values[i].arg, arglen);
 	  p[arglen] = ' ';
 	  p += arglen + 1;
+	  candidates.safe_push (e->values[i].arg);
 	}
       p[-1] = 0;
-      inform (loc, "valid arguments to %qs are: %s", option->opt_text, s);
+      const char *hint = find_closest_string (arg, &candidates);
+      if (hint)
+	inform (loc, "valid arguments to %qs are: %s; did you mean %qs?",
+		option->opt_text, s, hint);
+      else
+	inform (loc, "valid arguments to %qs are: %s", option->opt_text, s);
+
       return true;
     }
 
diff --git a/gcc/testsuite/gcc.dg/spellcheck-options-11.c b/gcc/testsuite/gcc.dg/spellcheck-options-11.c
new file mode 100644
index 0000000..8e27141
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-options-11.c
@@ -0,0 +1,7 @@ 
+/* Verify that we provide a hint if the user misspells an option argument
+   (PR driver/69265).  */
+
+/* { dg-do compile } */
+/* { dg-options "-ftls-model=global-dinamic" } */
+/* { dg-error "unknown TLS model 'global-dinamic'"  "" { target *-*-* } 0 } */
+/* { dg-message "valid arguments to '-ftls-model=' are: global-dynamic initial-exec local-dynamic local-exec; did you mean 'global-dynamic'?"  "" { target *-*-* } 0 } */