C++: fix fix-it hints for misspellings within explicit namespaces
authorDavid Malcolm <dmalcolm@redhat.com>
Fri, 20 Jan 2017 14:36:46 +0000 (14:36 +0000)
committerDavid Malcolm <dmalcolm@gcc.gnu.org>
Fri, 20 Jan 2017 14:36:46 +0000 (14:36 +0000)
gcc/cp/ChangeLog:
PR c++/77829
PR c++/78656
* cp-tree.h (suggest_alternatives_for): Add bool param.
(suggest_alternative_in_explicit_scope): New decl.
* error.c (qualified_name_lookup_error): When SCOPE is a namespace
that isn't the global one, call new function
suggest_alternative_in_explicit_scope, only calling
suggest_alternatives_for if it fails, and disabling near match
searches fort that case.  When SCOPE is the global namespace,
pass true for new param to suggest_alternatives_for to allow for
fuzzy name lookups.
* lex.c (unqualified_name_lookup_error): Pass true for new param
to suggest_alternatives_for.
* name-lookup.c (consider_binding_level): Add forward decl.
(suggest_alternatives_for): Add "suggest_misspellings" param,
using it to conditionalize the fuzzy name-lookup code.
(suggest_alternative_in_explicit_scope): New function.
* parser.c (cp_parser_primary_expression): When calling
finish_id_expression, pass location of id_expression rather
than that of id_expr_token.
(cp_parser_id_expression): Convert local "unqualified_id" from
tree to cp_expr to avoid implicitly dropping location information.

gcc/testsuite/ChangeLog:
PR c++/77829
PR c++/78656
* g++.dg/spellcheck-pr77829.C: New test case.
* g++.dg/spellcheck-pr78656.C: New test case.

From-SVN: r244715

gcc/cp/ChangeLog
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/g++.dg/spellcheck-pr77829.C [new file with mode: 0644]
gcc/testsuite/g++.dg/spellcheck-pr78656.C [new file with mode: 0644]

index 6554cb7cc753698f6d386afe3ce24097abd9188b..9b71c90f742053a36ef91d45e35ada6057978b3d 100644 (file)
@@ -1,3 +1,28 @@
+2017-01-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/77829
+       PR c++/78656
+       * cp-tree.h (suggest_alternatives_for): Add bool param.
+       (suggest_alternative_in_explicit_scope): New decl.
+       * error.c (qualified_name_lookup_error): When SCOPE is a namespace
+       that isn't the global one, call new function
+       suggest_alternative_in_explicit_scope, only calling
+       suggest_alternatives_for if it fails, and disabling near match
+       searches fort that case.  When SCOPE is the global namespace,
+       pass true for new param to suggest_alternatives_for to allow for
+       fuzzy name lookups.
+       * lex.c (unqualified_name_lookup_error): Pass true for new param
+       to suggest_alternatives_for.
+       * name-lookup.c (consider_binding_level): Add forward decl.
+       (suggest_alternatives_for): Add "suggest_misspellings" param,
+       using it to conditionalize the fuzzy name-lookup code.
+       (suggest_alternative_in_explicit_scope): New function.
+       * parser.c (cp_parser_primary_expression): When calling
+       finish_id_expression, pass location of id_expression rather
+       than that of id_expr_token.
+       (cp_parser_id_expression): Convert local "unqualified_id" from
+       tree to cp_expr to avoid implicitly dropping location information.
+
 2017-01-20  Marek Polacek  <polacek@redhat.com>
 
        PR c/64279
index f7c7a359a48d1690a2dc16b9221518ea0bc60e1e..b4c4dfa2f6b58a1078282ee4942ed2c45b0bcffe 100644 (file)
@@ -6937,7 +6937,8 @@ extern tree cp_fully_fold                 (tree);
 extern void clear_fold_cache                   (void);
 
 /* in name-lookup.c */
-extern void suggest_alternatives_for            (location_t, tree);
+extern void suggest_alternatives_for            (location_t, tree, bool);
+extern bool suggest_alternative_in_explicit_scope (location_t, tree, tree);
 extern tree strip_using_decl                    (tree);
 
 /* in constraint.cc */
index 72044a9013cb13075b62612c1a8e003995d3a42a..4f4c11df570a957c5af6eb1f0c4ccce5efac0884 100644 (file)
@@ -3777,11 +3777,12 @@ qualified_name_lookup_error (tree scope, tree name,
   else if (scope != global_namespace)
     {
       error_at (location, "%qD is not a member of %qD", name, scope);
-      suggest_alternatives_for (location, name);
+      if (!suggest_alternative_in_explicit_scope (location, name, scope))
+       suggest_alternatives_for (location, name, false);
     }
   else
     {
       error_at (location, "%<::%D%> has not been declared", name);
-      suggest_alternatives_for (location, name);
+      suggest_alternatives_for (location, name, true);
     }
 }
index 797dd9656e7d947a6fba8d2ffb4f035bae5494e3..60a70e9b325ab2de16f56981583165f83d24708d 100644 (file)
@@ -441,7 +441,7 @@ unqualified_name_lookup_error (tree name, location_t loc)
       if (!objc_diagnose_private_ivar (name))
        {
          error_at (loc, "%qD was not declared in this scope", name);
-         suggest_alternatives_for (loc, name);
+         suggest_alternatives_for (loc, name, true);
        }
       /* Prevent repeated error messages by creating a VAR_DECL with
         this NAME in the innermost block scope.  */
index 3c7559f33db06d599590df3c545adbbcf0da3f62..4004640a60cbb3596f117d2edc2996848564d981 100644 (file)
@@ -48,6 +48,10 @@ static bool lookup_using_namespace (tree, struct scope_binding *, tree,
                                    tree, int);
 static bool qualified_lookup_using_namespace (tree, tree,
                                              struct scope_binding *, int);
+static void consider_binding_level (tree name, best_match <tree, tree> &bm,
+                                   cp_binding_level *lvl,
+                                   bool look_within_fields,
+                                   enum lookup_name_fuzzy_kind kind);
 static tree lookup_type_current_level (tree);
 static tree push_using_directive (tree);
 static tree lookup_extern_c_fun_in_all_ns (tree);
@@ -4431,10 +4435,13 @@ remove_hidden_names (tree fns)
 
 /* Suggest alternatives for NAME, an IDENTIFIER_NODE for which name
    lookup failed.  Search through all available namespaces and print out
-   possible candidates.  */
+   possible candidates.  If no exact matches are found, and
+   SUGGEST_MISSPELLINGS is true, then also look for near-matches and
+   suggest the best near-match, if there is one.  */
 
 void
-suggest_alternatives_for (location_t location, tree name)
+suggest_alternatives_for (location_t location, tree name,
+                         bool suggest_misspellings)
 {
   vec<tree> candidates = vNULL;
   vec<tree> namespaces_to_search = vNULL;
@@ -4481,13 +4488,16 @@ suggest_alternatives_for (location_t location, tree name)
      or do nothing.  */
   if (candidates.is_empty ())
     {
-      const char *fuzzy_name = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME);
-      if (fuzzy_name)
+      if (suggest_misspellings)
        {
-         gcc_rich_location richloc (location);
-         richloc.add_fixit_replace (fuzzy_name);
-         inform_at_rich_loc (&richloc, "suggested alternative: %qs",
-                             fuzzy_name);
+         const char *fuzzy_name = lookup_name_fuzzy (name, FUZZY_LOOKUP_NAME);
+         if (fuzzy_name)
+           {
+             gcc_rich_location richloc (location);
+             richloc.add_fixit_replace (fuzzy_name);
+             inform_at_rich_loc (&richloc, "suggested alternative: %qs",
+                                 fuzzy_name);
+           }
        }
       return;
     }
@@ -4502,6 +4512,35 @@ suggest_alternatives_for (location_t location, tree name)
   candidates.release ();
 }
 
+/* Look for alternatives for NAME, an IDENTIFIER_NODE for which name
+   lookup failed within the explicitly provided SCOPE.  Suggest the
+   the best meaningful candidates (if any) as a fix-it hint.
+   Return true iff a suggestion was provided.  */
+
+bool
+suggest_alternative_in_explicit_scope (location_t location, tree name,
+                                      tree scope)
+{
+  cp_binding_level *level = NAMESPACE_LEVEL (scope);
+
+  best_match <tree, tree> bm (name);
+  consider_binding_level (name, bm, level, false, FUZZY_LOOKUP_NAME);
+
+  /* See if we have a good suggesion for the user.  */
+  tree best_id = bm.get_best_meaningful_candidate ();
+  if (best_id)
+    {
+      const char *fuzzy_name = IDENTIFIER_POINTER (best_id);
+      gcc_rich_location richloc (location);
+      richloc.add_fixit_replace (fuzzy_name);
+      inform_at_rich_loc (&richloc, "suggested alternative: %qs",
+                         fuzzy_name);
+      return true;
+    }
+
+  return false;
+}
+
 /* Unscoped lookup of a global: iterate over current namespaces,
    considering using-directives.  */
 
index 29dcfea283fa3f6aa2b51db50a4afad4cc80f9ce..4ab0b6974d624a791c69ad33671ee5f5a0791612 100644 (file)
@@ -5332,7 +5332,7 @@ cp_parser_primary_expression (cp_parser *parser,
                 template_p, done, address_p,
                 template_arg_p,
                 &error_msg,
-                 id_expr_token->location));
+                id_expression.get_location ()));
        if (error_msg)
          cp_parser_error (parser, error_msg);
        decl.set_location (id_expr_token->location);
@@ -5425,7 +5425,7 @@ cp_parser_id_expression (cp_parser *parser,
       tree saved_scope;
       tree saved_object_scope;
       tree saved_qualifying_scope;
-      tree unqualified_id;
+      cp_expr unqualified_id;
       bool is_template;
 
       /* See if the next token is the `template' keyword.  */
index 201395d9e55a91c47b7d89ec9d3d1030feb52278..fde93280263b330a79dc25b59ff2f3a0497706e9 100644 (file)
@@ -1,3 +1,10 @@
+2017-01-20  David Malcolm  <dmalcolm@redhat.com>
+
+       PR c++/77829
+       PR c++/78656
+       * g++.dg/spellcheck-pr77829.C: New test case.
+       * g++.dg/spellcheck-pr78656.C: New test case.
+
 2017-01-20  Marek Polacek  <polacek@redhat.com>
 
        PR c/64279
diff --git a/gcc/testsuite/g++.dg/spellcheck-pr77829.C b/gcc/testsuite/g++.dg/spellcheck-pr77829.C
new file mode 100644 (file)
index 0000000..2f75779
--- /dev/null
@@ -0,0 +1,167 @@
+// { dg-options "-fdiagnostics-show-caret" }
+
+/* Various tests of name lookup within a namespace, both within an explicitly
+   given namespace, or implicitly.  */
+
+namespace detail {
+  /* Various things to look for.  */
+
+  typedef int some_typedef;
+
+  int _foo(int i) { return i; }
+
+  template <typename T>
+  T something_else (T i) { return i; }
+}
+
+/* Tests of lookup of a typedef.  */
+
+void fn_1_explicit ()
+{
+  detail::some_type i; // { dg-error ".some_type. is not a member of .detail." }
+  // { dg-message "suggested alternative: .some_typedef." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   detail::some_type i;
+           ^~~~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   detail::some_type i;
+           ^~~~~~~~~
+           some_typedef
+     { dg-end-multiline-output "" } */
+}
+
+namespace detail {
+
+void fn_1_implicit ()
+{
+  some_type i; // { dg-error ".some_type. was not declared in this scope" }
+  // { dg-message "suggested alternative: .some_typedef." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   some_type i;
+   ^~~~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   some_type i;
+   ^~~~~~~~~
+   some_typedef
+     { dg-end-multiline-output "" } */
+}
+
+} // namespace detail
+
+
+/* Tests of lookup of a function.  */
+
+void fn_2_explicit (int i) {
+  detail::foo(i); // { dg-error ".foo. is not a member of .detail." }
+  // { dg-message "suggested alternative: ._foo." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   detail::foo(i);
+           ^~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   detail::foo(i);
+           ^~~
+           _foo
+     { dg-end-multiline-output "" } */
+}
+
+namespace detail {
+
+void fn_2_implicit (int i) {
+  foo(i); // { dg-error ".foo. was not declared in this scope" }
+  // { dg-message "suggested alternative: ._foo." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   foo(i);
+   ^~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   foo(i);
+   ^~~
+   _foo
+     { dg-end-multiline-output "" } */
+}
+
+} // namespace detail
+
+
+/* Examples using a template.  */
+
+void fn_3_explicit (int i) {
+  detail::something_els(i); // { dg-error ".something_els. is not a member of .detail." }
+  // { dg-message "suggested alternative: .something_else." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   detail::something_els(i);
+           ^~~~~~~~~~~~~
+     { dg-end-multiline-output "" } */
+
+  /* { dg-begin-multiline-output "" }
+   detail::something_els(i);
+           ^~~~~~~~~~~~~
+           something_else
+     { dg-end-multiline-output "" } */
+}
+
+namespace detail {
+
+void fn_3_implicit (int i) {
+  something_els(i); // { dg-error ".something_els. was not declared in this scope" }
+  // { dg-message "suggested alternative: .something_else." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   something_els(i);
+   ^~~~~~~~~~~~~
+     { dg-end-multiline-output "" } */
+
+  /* { dg-begin-multiline-output "" }
+   something_els(i);
+   ^~~~~~~~~~~~~
+   something_else
+     { dg-end-multiline-output "" } */
+}
+
+} // namespace detail
+
+
+/* Tests of lookup for which no hint is available.  */
+
+void fn_4_explicit (int i) {
+  detail::not_recognized(i); // { dg-error ".not_recognized. is not a member of .detail." }
+  /* { dg-begin-multiline-output "" }
+   detail::not_recognized(i);
+           ^~~~~~~~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+namespace detail {
+
+void fn_4_implicit (int i)
+{
+  not_recognized(i); // { dg-error ".not_recognized. was not declared in this scope" }
+  /* { dg-begin-multiline-output "" }
+   not_recognized(i);
+   ^~~~~~~~~~~~~~
+     { dg-end-multiline-output "" } */
+}
+
+} // namespace detail
+
+
+/* Test for failed lookup explicitly within global namespace.  */
+
+typedef int another_typedef;
+
+void fn_5 ()
+{
+  ::another_type i; // { dg-error ".::another_type. has not been declared" }
+  // { dg-message "suggested alternative: .another_typedef." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   ::another_type i;
+     ^~~~~~~~~~~~
+     { dg-end-multiline-output "" } */
+  /* { dg-begin-multiline-output "" }
+   ::another_type i;
+     ^~~~~~~~~~~~
+     another_typedef
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/g++.dg/spellcheck-pr78656.C b/gcc/testsuite/g++.dg/spellcheck-pr78656.C
new file mode 100644 (file)
index 0000000..ded4bb6
--- /dev/null
@@ -0,0 +1,39 @@
+// { dg-options "-fdiagnostics-show-caret" }
+
+#include <memory>
+
+void* allocate(std::size_t n)
+{
+  return std::allocate<char>().allocate(n); // { dg-error ".allocate. is not a member of .std." }
+  // { dg-message "suggested alternative: .allocator." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   return std::allocate<char>().allocate(n);
+               ^~~~~~~~
+     { dg-end-multiline-output "" } */ 
+  /* { dg-begin-multiline-output "" }
+   return std::allocate<char>().allocate(n);
+               ^~~~~~~~
+               allocator
+     { dg-end-multiline-output "" } */
+
+  // Various errors follow that we don't care about; suppress them:
+  // { dg-excess-errors "7: " }
+}
+
+void* test_2(std::size_t n)
+{
+  return std::alocator<char>().allocate(n); // { dg-error ".alocator. is not a member of .std." }
+  // { dg-message "suggested alternative: .allocator." "" { target *-*-* } .-1 }
+  /* { dg-begin-multiline-output "" }
+   return std::alocator<char>().allocate(n);
+               ^~~~~~~~
+     { dg-end-multiline-output "" } */ 
+  /* { dg-begin-multiline-output "" }
+   return std::alocator<char>().allocate(n);
+               ^~~~~~~~
+               allocator
+     { dg-end-multiline-output "" } */
+
+  // Various errors follow that we don't care about; suppress them:
+  // { dg-excess-errors "25: " }
+}