C++: simplify output from suggest_alternatives_for
commitd42760aad21210643e00f090bdb52a93066e18a7
authordmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 29 Oct 2018 23:53:50 +0000 (29 23:53 +0000)
committerdmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>
Mon, 29 Oct 2018 23:53:50 +0000 (29 23:53 +0000)
treead3bc9e9751e07705061fb4f0700df96b7158e4a
parent8e415b302e3160e85f0ff18fd34d930f16e7409b
C++: simplify output from suggest_alternatives_for

In the C++ FE, after emitting various errors about unrecognized names,
the parser can call
  suggest_alternatives_for
and/or
  suggest_alternative_in_explicit_scope.
These can issue zero or more suggestions for the unrecognized name,
or various other "note" diagnostics suggesting how to fix the problem.

For example, currently g++ emits:

t.cc:12:3: error: 'gtk_widget_showall' was not declared in this scope
12 |   gtk_widget_showall (w);
   |   ^~~~~~~~~~~~~~~~~~
t.cc:12:3: note: suggested alternative: 'gtk_widget_show_all'
12 |   gtk_widget_showall (w);
   |   ^~~~~~~~~~~~~~~~~~
   |   gtk_widget_show_all

This patch consolidates the common case when there is a single
candidate, so that the error can issue a fix-it hint directly.

This simplifies the above to:

t.cc:12:3: error: 'gtk_widget_showall' was not declared in this scope;
 did you mean 'gtk_widget_show_all'?
12 |   gtk_widget_showall (w);
   |   ^~~~~~~~~~~~~~~~~~
   |   gtk_widget_show_all

omitting the second "note" diagnostic.

Doing so requires changing the above "suggest_" functions so that
rather than being called after "error" and emitting a note directly,
they are called before the "error", and return a name_hint, which
can contain a suggestion and/or a deferred diagnostic.  The "single
candidate" case is handled via a suggestion, and the "multiple
candidates" case via a new subclass of deferred_diagnostic.

There was some complication due to the fact that we don't always have
enough location information to issue a fix-it hint.  Specifically,
for the case in qualified_name_lookup_error, the location is that of
the name, but the location of the qualifier prefix isn't reliably
available.  For some hints, e.g. spell-corrections, the replacement
is of the name, and for others, e.g. parent namespaces, it's for the
qualified name.  The patch addresses this by splitting this case out
into a new "suggest_alternatives_in_other_namespaces" function, for
which fix-it hints aren't issued.

Another complication is that of emitting a note when
  --param cxx-max-namespaces-for-diagnostic-help
is reached.  The patch emulates the existing behavior by emitting
the note from a deferred_diagnostic.  This potentially needs to
co-exist with another deferred_diagnostic, so it works as a decorator
around any other such deferred_diagnostic.  Doing so requires slightly
extending class name_hint.

On adding test coverage for the various cases, I discovered that
after emitting a "FOO is not a namespace-name" error, we also emit
a "expected namespace-name before" error.  The patch removes this
second error for the case where it's redundant, simplifying this case
from e.g.:

spellcheck-ns.C:10:24: error: 'inner_ms' is not a namespace-name
10 | using namespace outer::inner_ms;
   |                        ^~~~~~~~
spellcheck-ns.C:10:24: note: suggested alternative: 'inner_ns'
10 | using namespace outer::inner_ms;
   |                        ^~~~~~~~
   |                        inner_ns
spellcheck-ns.C:10:32: error: expected namespace-name before ';' token
10 | using namespace outer::inner_ms;
   |                                ^

to:

spellcheck-ns.C:10:24: error: 'inner_ms' is not a namespace-name;
 did you mean 'inner_ns'?
10 | using namespace outer::inner_ms;
   |                        ^~~~~~~~
   |                        inner_ns

include/ChangeLog:
* unique-ptr.h (gnu::move): Generalize so it applies to all
lvalue references, rather than just to unique_ptr values.

gcc/c-family/ChangeLog:
* name-hint.h (name_hint::take_deferred): New member function.

gcc/c/ChangeLog:
* c-decl.c (implicit_decl_warning): Update "is there a suggestion"
logic for change to name_hint::operator bool.
(undeclared_variable): Likewise.
* c-parser.c (c_parser_declaration_or_fndef): Likewise.
(c_parser_parameter_declaration): Likewise.

gcc/cp/ChangeLog:
* cp-name-hint.h: New file.
* cp-tree.h (expr_to_string): New decl.
(suggest_alternatives_for): Move to cp-name-hint.h, changing
return type from bool to name_hint.
(suggest_alternative_in_explicit_scope): Likewise.
* error.c: Define INCLUDE_UNIQUE_PTR.  Include "cp-name-hint.h".
(expr_to_string): Make non-static.
(qualified_name_lookup_error): For the non-"::" case, take
responsibity for issuing any suggestion from
suggest_alternative_in_explicit_scope, as it changes from
returning a bool to returning a name_hint.  Replace fallback call
to suggest_alternatives_for to a call to
suggest_alternatives_in_other_namespaces, capturing the fact that
we don't have enough location information to issue a fix-it hint
for this case.  Update the error to support emitting a fix-it hint
where appropriate.  For the "::" case, take responsibility for
issuing any suggestion from suggest_alternatives_for, supporting
emitting a fix-it hint.
* lex.c: Define INCLUDE_UNIQUE_PTR.  Include "gcc-rich-location.h"
and "cp-name-hint.h".
(unqualified_name_lookup_error): Take responsibility for issuing
any suggestion from suggest_alternatives_for, supporting emitting
a fix-it hint.
* name-lookup.c (class namespace_limit_reached): New subclass of
deferred_diagnostic.
(class show_candidate_location): Likewise.
(class suggest_alternatives): Likewise.
(class namespace_hints): New class.
(suggest_alternatives_for): Convert return type from bool to
name_hint, replacing all direct diagnostic emission by setting
suggestions on the return value, or creating deferred diagnostics.
Specifically, split out initial traversal of namespaces into
namespace_hints' ctor, and maybe_decorate_with_limit, and move the
rest of the implementation to
namespace_hints::convert_candidates_to_name_hint and
suggest_alternatives_for_1.
(namespace_hints::namespace_hints): New ctor, adapted from
suggest_alternatives_for's initial namespace traversal, storing
location and name, and converting locals "candidates", "limited"
and "limit" into members.
(namespace_hints::convert_candidates_to_name_hint): New member
function.
(namespace_hints::maybe_decorate_with_limit): New member function.
(suggest_alternatives_for_1): New function, based on second half
of old implementation of suggest_alternatives_for, converting from
immediate emission of suggestions to using name_hint.
(suggest_alternatives_in_other_namespaces): New function.
(maybe_suggest_missing_std_header): Convert from immediate
emission of suggestions to using name_hint, moving emission
implementation to...
(class missing_std_header): New subclass of deferred_diagnostic.
(maybe_suggest_missing_header): Convert return type from bool to
name_hint.
(suggest_alternative_in_explicit_scope): Convert from immediate
emission of suggestions to using name_hint.
* parser.c: Replace include of "c-family/name-hint.h" with
"cp-name-hint.h".
(cp_parser_diagnose_invalid_type_name): Update
"is there a suggestion" logic for change to
name_hint::operator bool.  Take responsibility for emitting
fix-it hints from suggest_alternative_in_explicit_scope.
(cp_parser_namespace_name): Take responsibility for emitting
fix-it hints from suggest_alternative_in_explicit_scope.  Don't
emit the "expected namespace-name" error if we've already emitted
an "is not a namespace-name" error.

gcc/testsuite/ChangeLog:
* c-c++-common/spellcheck-reserved.c: Update expected output for
C++ for merger of "did you mean" suggestions into the error
message.
* g++.dg/ext/builtin3.C: Update expected output for merger of "did
you mean" suggestion into the error.
* g++.dg/lookup/error1.C: Likewise.
* g++.dg/lookup/pr77549.C: Likewise.
* g++.dg/lookup/pr80913.C: Likewise.
* g++.dg/lookup/suggestions1.C: Likewise.
* g++.dg/lookup/suggestions2.C: New test.
* g++.dg/overload/koenig1.C: Update expected output as above.
* g++.dg/spellcheck-identifiers-2.C: Likewise.
* g++.dg/spellcheck-identifiers.C: Likewise.
* g++.dg/spellcheck-ns.C: New test.
* g++.dg/spellcheck-pr77829.C: Update expected output as above.
* g++.dg/spellcheck-pr78656.C: Likewise.
* g++.dg/spellcheck-pr79298.C: Likewise, adding
-fdiagnostics-show-caret to options.
* g++.dg/spellcheck-pr80177.C: Likewise.
* g++.dg/spellcheck-single-vs-multiple.C: New test.
* g++.dg/spellcheck-typenames.C: Update expected output as above.
* g++.dg/template/static10.C: Likewise.
* g++.old-deja/g++.mike/ns5.C: Likewise.
* g++.old-deja/g++.mike/ns7.C: Likewise.
* g++.old-deja/g++.ns/koenig5.C: Likewise.
* g++.old-deja/g++.other/lineno5.C: Likewise.

libstdc++-v3/ChangeLog:
* testsuite/17_intro/using_namespace_std_exp_neg.cc: Remove
"expected namespace-name before" error.
* testsuite/17_intro/using_namespace_std_tr1_neg.cc: Likewise.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@265610 138bc75d-0d04-0410-961f-82ee72b054a4
40 files changed:
gcc/c-family/ChangeLog
gcc/c-family/name-hint.h
gcc/c/ChangeLog
gcc/c/c-decl.c
gcc/c/c-parser.c
gcc/cp/ChangeLog
gcc/cp/cp-name-hint.h [new file with mode: 0644]
gcc/cp/cp-tree.h
gcc/cp/error.c
gcc/cp/lex.c
gcc/cp/name-lookup.c
gcc/cp/parser.c
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/spellcheck-reserved.c
gcc/testsuite/g++.dg/ext/builtin3.C
gcc/testsuite/g++.dg/lookup/error1.C
gcc/testsuite/g++.dg/lookup/pr77549.C
gcc/testsuite/g++.dg/lookup/pr80913.C
gcc/testsuite/g++.dg/lookup/suggestions1.C
gcc/testsuite/g++.dg/lookup/suggestions2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/overload/koenig1.C
gcc/testsuite/g++.dg/spellcheck-identifiers-2.C
gcc/testsuite/g++.dg/spellcheck-identifiers.C
gcc/testsuite/g++.dg/spellcheck-ns.C [new file with mode: 0644]
gcc/testsuite/g++.dg/spellcheck-pr77829.C
gcc/testsuite/g++.dg/spellcheck-pr78656.C
gcc/testsuite/g++.dg/spellcheck-pr79298.C
gcc/testsuite/g++.dg/spellcheck-pr80177.C
gcc/testsuite/g++.dg/spellcheck-single-vs-multiple.C [new file with mode: 0644]
gcc/testsuite/g++.dg/spellcheck-typenames.C
gcc/testsuite/g++.dg/template/static10.C
gcc/testsuite/g++.old-deja/g++.mike/ns5.C
gcc/testsuite/g++.old-deja/g++.mike/ns7.C
gcc/testsuite/g++.old-deja/g++.ns/koenig5.C
gcc/testsuite/g++.old-deja/g++.other/lineno5.C
include/ChangeLog
include/unique-ptr.h
libstdc++-v3/ChangeLog
libstdc++-v3/testsuite/17_intro/using_namespace_std_exp_neg.cc
libstdc++-v3/testsuite/17_intro/using_namespace_std_tr1_neg.cc