This patch addresses various bugs in the spellcheck code in which
the goal string somehow makes it into the candidate list.
The goal string will always have an edit distance of 0 to itself, and
thus is the "closest" string to the goal, but offering it as a
suggestion will always be nonsensical e.g.
  'constexpr' does not name a type; did you mean 'constexpr'?

Ultimately such suggestions are due to bugs in constructing the
candidate list.

As a band-aid, the patch updates
best_match::get_best_meaningful_candidate so that we no longer
offer suggestions for the case where the edit distance == 0
(where candidate == goal).

Doing so fixes PR c++/72786, PR c++/77922, and PR c++/78313.

I looked at fixing the candidate list in each of these bugs.

PR c++/72786 (macro defined after use): this occurs because we're
using the set of macro names at the end of parsing, rather than
at the point of parsing the site of the would-be macro usage.
A better fix would be to indicate this, but would be somewhat
invasive, needing a new internal API (perhaps too much for
stage 3?) so hopefully the band-aid is good enough for GCC 7.

PR c++/77922: the patch updates C++'s lookup_name_fuzzy to only
suggest reserved words that are matched by "-std=" etc, which
thus eliminating bogus words from the candidate list.

PR c++/78313: I attempted to prune the candidate list here, but it
led to a worse message (see the comment in that bug), hence I'd
prefer to rely on the best_match::get_best_meaningful_candidate
fix for this one.

Successfully bootstrapped&regrtested on x86_64-pc-linux-gnu; adds
26 PASS results to g++.sum.

OK for trunk?

gcc/cp/ChangeLog:
        PR c++/77922
        * name-lookup.c (lookup_name_fuzzy): Filter out reserved words
        that were filtered out by init_reswords.

gcc/ChangeLog:
        PR c++/72774
        PR c++/72786
        PR c++/77922
        PR c++/78313
        * spellcheck.c (selftest::test_find_closest_string): Verify that
        we don't offer the goal string as a suggestion.
        * spellcheck.h (best_match::get_best_meaningful_candidate): Don't
        offer the goal string as a suggestion.

gcc/testsuite/ChangeLog:
        PR c++/72774
        PR c++/72786
        PR c++/77922
        PR c++/78313
        * g++.dg/spellcheck-c++-11-keyword.C: New test case.
        * g++.dg/spellcheck-macro-ordering.C: New test case.
        * g++.dg/spellcheck-pr78313.C: New test case.
---
 gcc/cp/name-lookup.c                             |  6 ++++++
 gcc/spellcheck.c                                 |  5 +++++
 gcc/spellcheck.h                                 | 10 ++++++++++
 gcc/testsuite/g++.dg/spellcheck-c++-11-keyword.C | 15 +++++++++++++++
 gcc/testsuite/g++.dg/spellcheck-macro-ordering.C | 15 +++++++++++++++
 gcc/testsuite/g++.dg/spellcheck-pr78313.C        | 11 +++++++++++
 6 files changed, 62 insertions(+)
 create mode 100644 gcc/testsuite/g++.dg/spellcheck-c++-11-keyword.C
 create mode 100644 gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
 create mode 100644 gcc/testsuite/g++.dg/spellcheck-pr78313.C

diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c
index 7ad65b8..84e064d 100644
--- a/gcc/cp/name-lookup.c
+++ b/gcc/cp/name-lookup.c
@@ -4811,6 +4811,12 @@ lookup_name_fuzzy (tree name, enum 
lookup_name_fuzzy_kind kind)
       if (!resword_identifier)
        continue;
       gcc_assert (TREE_CODE (resword_identifier) == IDENTIFIER_NODE);
+
+      /* Only consider reserved words that survived the
+        filtering in init_reswords (e.g. for -std).  */
+      if (!C_IS_RESERVED_WORD (resword_identifier))
+       continue;
+
       bm.consider (resword_identifier);
     }
 
diff --git a/gcc/spellcheck.c b/gcc/spellcheck.c
index b37b1e4..86cdee1 100644
--- a/gcc/spellcheck.c
+++ b/gcc/spellcheck.c
@@ -210,6 +210,11 @@ test_find_closest_string ()
   ASSERT_STREQ ("banana", find_closest_string ("banyan", &candidates));
   ASSERT_STREQ ("cherry", find_closest_string ("berry", &candidates));
   ASSERT_EQ (NULL, find_closest_string ("not like the others", &candidates));
+
+  /* If the goal string somehow makes it into the candidate list, offering
+     it as a suggestion will be nonsensical.  Verify that we don't offer such
+     suggestions.  */
+  ASSERT_EQ (NULL, find_closest_string ("banana", &candidates));
 }
 
 /* Test data for test_metric_conditions.  */
diff --git a/gcc/spellcheck.h b/gcc/spellcheck.h
index b48cfbc..41c9308 100644
--- a/gcc/spellcheck.h
+++ b/gcc/spellcheck.h
@@ -165,6 +165,16 @@ class best_match
        if (m_best_distance > cutoff)
          return NULL;
     }
+
+    /* If the goal string somehow makes it into the candidate list, offering
+       it as a suggestion will be nonsensical e.g.
+         'constexpr' does not name a type; did you mean 'constexpr'?
+       Ultimately such suggestions are due to bugs in constructing the
+       candidate list, but as a band-aid, do not offer suggestions for
+       distance == 0 (where candidate == goal).  */
+    if (m_best_distance == 0)
+      return NULL;
+
     return m_best_candidate;
   }
 
diff --git a/gcc/testsuite/g++.dg/spellcheck-c++-11-keyword.C 
b/gcc/testsuite/g++.dg/spellcheck-c++-11-keyword.C
new file mode 100644
index 0000000..0984af9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-c++-11-keyword.C
@@ -0,0 +1,15 @@
+/* c++/77922: "constexpr" is only available from C++11 onwards.
+   We shouldn't offer it as a spellcheck suggestion in C++98.  */
+// { dg-options "-std=c++98" }
+
+constexpr int a = 1; // { dg-bogus "did you mean" }
+// { dg-error ".constexpr. does not name a type" "" { target *-*-* } .-1 }
+// { dg-message "C\\+\\+11 .constexpr. only available with -std=c\\+\\+11 or 
-std=gnu\\+\\+11" "" { target *-*-* } .-2 }
+
+/* If the user typos "constexpr" (here as "consexpr"), don't offer it as a
+   spelling suggestion in C++98 mode.  */
+consexpr int a = 1; // { dg-bogus "did you mean" }
+// { dg-error ".consexpr. does not name a type" "" { target *-*-* } .-1 }
+
+decltype i = 0; // { dg-bogus "did you mean" }
+// { dg-error ".decltype. does not name a type" "" { target *-*-* } .-1 }
diff --git a/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C 
b/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
new file mode 100644
index 0000000..3b888c6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-macro-ordering.C
@@ -0,0 +1,15 @@
+// PR c++/72786
+
+/* Example of a macro-ordering issue, where the use is before the defn.  */
+
+class DocTargetDriver {
+  virtual void clone() const OVERRIDE { }
+  /* Offering "OVERRIDE" as a spelling suggestion for "OVERRIDE" would be
+     nonsensical.  */
+  // { dg-bogus "did you mean" "" { target *-*-* } .-3 }
+  // { dg-error "expected .;. at end of member declaration" "" { target *-*-* 
} .-4 }
+  // { dg-error ".OVERRIDE. does not name a type" "" { target *-*-* } .-5 }
+};
+
+#define OVERRIDE override
+
diff --git a/gcc/testsuite/g++.dg/spellcheck-pr78313.C 
b/gcc/testsuite/g++.dg/spellcheck-pr78313.C
new file mode 100644
index 0000000..e34176d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/spellcheck-pr78313.C
@@ -0,0 +1,11 @@
+// PR c++/78313 (see also PR c++/72774)
+// { dg-do compile }
+
+void baz ();
+namespace A { void foo (); }
+void bar ()
+{
+  using A::foo;
+  0 ? static_cast<foo> (0) : baz; // { dg-bogus "did you mean" }
+  // { dg-error "does not name a type" "" { target *-*-* } .-1 }
+}
-- 
1.8.5.3

Reply via email to