From 0a52c9209af3458171c3d5ed2ee2bd6656244859 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 21 Mar 2016 13:21:27 -0400
Subject: [PATCH 12/17] FIXME: PR c/70339: "singed" vs "signed" hint+fixit

---
 gcc/c-family/c-common.h                  |  14 ++++-
 gcc/c/c-decl.c                           |  39 +++++++++---
 gcc/c/c-parser.c                         | 101 +++++++++++++++++++------------
 gcc/c/c-tree.h                           |   1 +
 gcc/testsuite/gcc.dg/spellcheck-params.c |  35 +++++++++++
 5 files changed, 142 insertions(+), 48 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/spellcheck-params.c

diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h
index 9616169..9d64824 100644
--- a/gcc/c-family/c-common.h
+++ b/gcc/c-family/c-common.h
@@ -984,9 +984,21 @@ extern tree finish_label_address_expr (tree, location_t);
    different implementations.  Used in c-common.c.  */
 extern tree lookup_label (tree);
 extern tree lookup_name (tree);
-extern tree lookup_name_fuzzy (tree, bool);
 extern bool lvalue_p (const_tree);
 
+enum lookup_name_fuzzy_kind {
+  /* Names of types.  */
+  FUZZY_LOOKUP_TYPENAME,
+
+  /* Identifiers that can start a typename.  Includes typenames, but
+     also reserved words such as "unsigned" etc.  */
+  FUZZY_LOOKUP_START_TYPENAME,
+
+  /* Any name.  */
+  FUZZY_LOOKUP_NAME
+};
+extern tree lookup_name_fuzzy (tree, enum lookup_name_fuzzy_kind);
+
 extern bool vector_targets_convertible_p (const_tree t1, const_tree t2);
 extern bool vector_types_convertible_p (const_tree t1, const_tree t2, bool emit_lax_note);
 extern tree c_build_vec_perm_expr (location_t, tree, tree, tree, bool = true);
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 8df3a4e..fd2dd74 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -3095,7 +3095,7 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl)
       bool warned;
       tree hint = NULL_TREE;
       if (!olddecl)
-	hint = lookup_name_fuzzy (id, false);
+	hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
 
       if (flag_isoc99)
 	if (hint)
@@ -3947,30 +3947,55 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
    benefiting candidates "near" to the current scope.  */
 
 tree
-lookup_name_fuzzy (tree name, bool want_type_p)
+lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
 {
   gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
 
-  c_binding *best_binding = NULL;
+  tree best_id = NULL;
   edit_distance_t best_distance = MAX_EDIT_DISTANCE;
 
+  if (kind == FUZZY_LOOKUP_START_TYPENAME)
+    {
+      /* First, try the "start_typename" keywords to detect
+	 "singed" vs "signed" typos.  */
+
+     for (unsigned i = 0; i < num_c_common_reswords; i++)
+	{
+	  const c_common_resword *resword = &c_common_reswords[i];
+	  if (!c_keyword_starts_typename (resword->rid))
+	    continue;
+	  tree resword_identifier = ridpointers [resword->rid];
+	  if (!resword_identifier)
+	    continue;
+	  gcc_assert (TREE_CODE (resword_identifier) == IDENTIFIER_NODE);
+	  edit_distance_t dist = levenshtein_distance (name,
+						       resword_identifier);
+	  if (dist < best_distance)
+	    {
+	      best_distance = dist;
+	      best_id = resword_identifier;
+	    }
+	}
+    }
+
   for (c_scope *scope = current_scope; scope; scope = scope->outer)
     for (c_binding *binding = scope->bindings; binding; binding = binding->prev)
       {
 	if (!binding->id)
 	  continue;
-	if (want_type_p)
+	if (kind == FUZZY_LOOKUP_TYPENAME
+	    || kind == FUZZY_LOOKUP_START_TYPENAME)
 	  if (TREE_CODE (binding->decl) != TYPE_DECL)
 	    continue;
 	edit_distance_t dist = levenshtein_distance (name, binding->id);
 	if (dist < best_distance)
 	  {
 	    best_distance = dist;
-	    best_binding = binding;
+	    best_id = binding->id;
 	  }
       }
 
-  if (!best_binding)
+  if (!best_id)
     return NULL;
 
   /* If more than half of the letters were misspelled, the suggestion is
@@ -3978,7 +4003,7 @@ lookup_name_fuzzy (tree name, bool want_type_p)
   if (best_distance > IDENTIFIER_LENGTH (name) / 2 )
     return NULL;
 
-  return best_binding->id;
+  return best_id;
 }
 
 
diff --git a/gcc/c/c-parser.c b/gcc/c/c-parser.c
index 22af084..ebeda60 100644
--- a/gcc/c/c-parser.c
+++ b/gcc/c/c-parser.c
@@ -518,6 +518,48 @@ c_parser_peek_nth_token (c_parser *parser, unsigned int n)
   return &parser->tokens[n - 1];
 }
 
+bool
+c_keyword_starts_typename (enum rid keyword)
+{
+  switch (keyword)
+    {
+    case RID_UNSIGNED:
+    case RID_LONG:
+    case RID_SHORT:
+    case RID_SIGNED:
+    case RID_COMPLEX:
+    case RID_INT:
+    case RID_CHAR:
+    case RID_FLOAT:
+    case RID_DOUBLE:
+    case RID_VOID:
+    case RID_DFLOAT32:
+    case RID_DFLOAT64:
+    case RID_DFLOAT128:
+    case RID_BOOL:
+    case RID_ENUM:
+    case RID_STRUCT:
+    case RID_UNION:
+    case RID_TYPEOF:
+    case RID_CONST:
+    case RID_ATOMIC:
+    case RID_VOLATILE:
+    case RID_RESTRICT:
+    case RID_ATTRIBUTE:
+    case RID_FRACT:
+    case RID_ACCUM:
+    case RID_SAT:
+    case RID_AUTO_TYPE:
+      return true;
+    default:
+      if (keyword >= RID_FIRST_INT_N
+	  && keyword < RID_FIRST_INT_N + NUM_INT_N_ENTS
+	  && int_n_enabled_p[keyword - RID_FIRST_INT_N])
+	return true;
+      return false;
+    }
+}
+
 /* Return true if TOKEN can start a type name,
    false otherwise.  */
 static bool
@@ -541,43 +583,7 @@ c_token_starts_typename (c_token *token)
 	  gcc_unreachable ();
 	}
     case CPP_KEYWORD:
-      switch (token->keyword)
-	{
-	case RID_UNSIGNED:
-	case RID_LONG:
-	case RID_SHORT:
-	case RID_SIGNED:
-	case RID_COMPLEX:
-	case RID_INT:
-	case RID_CHAR:
-	case RID_FLOAT:
-	case RID_DOUBLE:
-	case RID_VOID:
-	case RID_DFLOAT32:
-	case RID_DFLOAT64:
-	case RID_DFLOAT128:
-	case RID_BOOL:
-	case RID_ENUM:
-	case RID_STRUCT:
-	case RID_UNION:
-	case RID_TYPEOF:
-	case RID_CONST:
-	case RID_ATOMIC:
-	case RID_VOLATILE:
-	case RID_RESTRICT:
-	case RID_ATTRIBUTE:
-	case RID_FRACT:
-	case RID_ACCUM:
-	case RID_SAT:
-	case RID_AUTO_TYPE:
-	  return true;
-	default:
-	  if (token->keyword >= RID_FIRST_INT_N
-	      && token->keyword < RID_FIRST_INT_N + NUM_INT_N_ENTS
-	      && int_n_enabled_p[token->keyword - RID_FIRST_INT_N])
-	    return true;
-	  return false;
-	}
+      return c_keyword_starts_typename (token->keyword);
     case CPP_LESS:
       if (c_dialect_objc ())
 	return true;
@@ -1687,7 +1693,7 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
 	}
       else
 	{
-	  tree hint = lookup_name_fuzzy (name, true);
+	  tree hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME);
 	  if (hint)
 	    {
 	      richloc.add_fixit_replace (get_range_from_loc (line_table, here),
@@ -3666,7 +3672,8 @@ c_parser_parms_declarator (c_parser *parser, bool id_list_ok, tree attrs)
       && c_parser_peek_2nd_token (parser)->type != CPP_NAME
       && c_parser_peek_2nd_token (parser)->type != CPP_MULT
       && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_PAREN
-      && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_SQUARE)
+      && c_parser_peek_2nd_token (parser)->type != CPP_OPEN_SQUARE
+      && c_parser_peek_2nd_token (parser)->type != CPP_KEYWORD)
     {
       tree list = NULL_TREE, *nextp = &list;
       while (c_parser_next_token_is (parser, CPP_NAME)
@@ -3841,7 +3848,21 @@ c_parser_parameter_declaration (c_parser *parser, tree attrs)
       c_parser_set_source_position_from_token (token);
       if (c_parser_next_tokens_start_typename (parser, cla_prefer_type))
 	{
-	  error_at (token->location, "unknown type name %qE", token->value);
+	  tree hint = lookup_name_fuzzy (token->value,
+					 FUZZY_LOOKUP_START_TYPENAME);
+	  if (hint)
+	    {
+	      gcc_assert (TREE_CODE (hint) == IDENTIFIER_NODE);
+	      rich_location richloc (line_table, token->location);
+	      richloc.add_fixit_replace (get_range_from_loc (line_table,
+							     token->location),
+					 IDENTIFIER_POINTER (hint));
+	      error_at_rich_loc (&richloc,
+				 "unknown type name %qE; did you mean %qE?",
+				 token->value, hint);
+	    }
+	  else
+	    error_at (token->location, "unknown type name %qE", token->value);
 	  parser->error = true;
 	}
       /* ??? In some Objective-C cases '...' isn't applicable so there
diff --git a/gcc/c/c-tree.h b/gcc/c/c-tree.h
index 2c42475..eb3dda2 100644
--- a/gcc/c/c-tree.h
+++ b/gcc/c/c-tree.h
@@ -472,6 +472,7 @@ enum c_inline_static_type {
 
 /* in c-parser.c */
 extern void c_parse_init (void);
+extern bool c_keyword_starts_typename (enum rid keyword);
 
 /* in c-aux-info.c */
 extern void gen_aux_info_record (tree, int, int, int);
diff --git a/gcc/testsuite/gcc.dg/spellcheck-params.c b/gcc/testsuite/gcc.dg/spellcheck-params.c
new file mode 100644
index 0000000..08a85e4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-params.c
@@ -0,0 +1,35 @@
+/* { dg-options "-fdiagnostics-show-caret" } */
+
+void test_1 (signed char e)
+{
+}
+
+/* PR c/70339.  */
+void test_2 (singed char e) /* { dg-error "14: unknown type name .singed.; did you mean .signed.?" } */
+{
+}
+/* { dg-begin-multiline-output "" }
+ void test_2 (singed char e)
+              ^~~~~~
+              signed
+   { dg-end-multiline-output "" } */
+
+
+void test_3 (car e) /* { dg-error "14: unknown type name .car.; did you mean .char.?" } */
+{
+}
+/* { dg-begin-multiline-output "" }
+ void test_3 (car e)
+              ^~~
+              char
+   { dg-end-multiline-output "" } */
+
+/* TODO: this one could be handled better.  */
+
+void test_4 (signed car e) { /* { dg-error "25: before .e." } */
+{
+}
+/* { dg-begin-multiline-output "" }
+ void test_4 (signed car e)
+                         ^
+   { dg-end-multiline-output "" } */
-- 
1.8.5.3

