C FE: suggest corrections for misspelled identifiers and type names
gcc/c-family/ChangeLog: PR c/70339 * c-common.h (enum lookup_name_fuzzy_kind): New enum. (lookup_name_fuzzy): New prototype. gcc/c/ChangeLog: PR c/70339 * c-decl.c: Include spellcheck-tree.h and gcc-rich-location.h. (implicit_decl_warning): When issuing warnings for implicit declarations, attempt to provide a suggestion via lookup_name_fuzzy. (undeclared_variable): Likewise when issuing errors. (lookup_name_in_scope): Likewise. (struct edit_distance_traits<cpp_hashnode *>): New struct. (best_macro_match): New typedef. (find_closest_macro_cpp_cb): New function. (lookup_name_fuzzy): New function. * c-parser.c: Include gcc-rich-location.h. (c_token_starts_typename): Split out case CPP_KEYWORD into... (c_keyword_starts_typename): ...this new function. (c_parser_declaration_or_fndef): When issuing errors about missing "struct" etc, add a fixit. For other kinds of errors, attempt to provide a suggestion via lookup_name_fuzzy. (c_parser_parms_declarator): When looking ahead to detect typos in type names, also reject CPP_KEYWORD. (c_parser_parameter_declaration): When issuing errors about unknown type names, attempt to provide a suggestion via lookup_name_fuzzy. * c-tree.h (c_keyword_starts_typename): New prototype. gcc/ChangeLog: PR c/70339 * diagnostic-core.h (pedwarn_at_rich_loc): New prototype. * diagnostic.c (pedwarn_at_rich_loc): New function. * spellcheck.h (best_match::best_match): Add a "best_distance_so_far" optional parameter. (best_match::set_best_so_far): New method. (best_match::get_best_distance): New accessor. (best_match::get_best_candidate_length): New accessor. gcc/testsuite/ChangeLog: PR c/70339 * c-c++-common/attributes-1.c: Update dg-prune-output to include hint. * gcc.dg/diagnostic-token-ranges.c (undeclared_identifier): Update expected results due to builtin "nanl" now being suggested for "name". * gcc.dg/pr67580.c: Update expected messages. * gcc.dg/spellcheck-identifiers.c: New testcase. * gcc.dg/spellcheck-typenames.c: New testcase. From-SVN: r237714
This commit is contained in:
parent
6f99ef82f1
commit
1a4f11c88a
16 changed files with 634 additions and 67 deletions
|
@ -1,3 +1,14 @@
|
|||
2016-06-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c/70339
|
||||
* diagnostic-core.h (pedwarn_at_rich_loc): New prototype.
|
||||
* diagnostic.c (pedwarn_at_rich_loc): New function.
|
||||
* spellcheck.h (best_match::best_match): Add a
|
||||
"best_distance_so_far" optional parameter.
|
||||
(best_match::set_best_so_far): New method.
|
||||
(best_match::get_best_distance): New accessor.
|
||||
(best_match::get_best_candidate_length): New accessor.
|
||||
|
||||
2016-06-22 Nick Clifton <nickc@redhat.com>
|
||||
|
||||
* dwarf2out.c (scompare_loc_descriptor): Use SCALAR_INT_MODE_P() in
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
2016-06-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c/70339
|
||||
* c-common.h (enum lookup_name_fuzzy_kind): New enum.
|
||||
(lookup_name_fuzzy): New prototype.
|
||||
|
||||
2016-06-21 John David Anglin <danglin@gcc.gnu.org>
|
||||
|
||||
* c-common.c (get_source_date_epoch): Use int64_t instead of long long.
|
||||
|
|
|
@ -990,6 +990,15 @@ extern tree lookup_label (tree);
|
|||
extern tree lookup_name (tree);
|
||||
extern bool lvalue_p (const_tree);
|
||||
|
||||
enum lookup_name_fuzzy_kind {
|
||||
/* Names of types. */
|
||||
FUZZY_LOOKUP_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);
|
||||
|
|
|
@ -1,3 +1,29 @@
|
|||
2016-06-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c/70339
|
||||
* c-decl.c: Include spellcheck-tree.h and gcc-rich-location.h.
|
||||
(implicit_decl_warning): When issuing warnings for implicit
|
||||
declarations, attempt to provide a suggestion via
|
||||
lookup_name_fuzzy.
|
||||
(undeclared_variable): Likewise when issuing errors.
|
||||
(lookup_name_in_scope): Likewise.
|
||||
(struct edit_distance_traits<cpp_hashnode *>): New struct.
|
||||
(best_macro_match): New typedef.
|
||||
(find_closest_macro_cpp_cb): New function.
|
||||
(lookup_name_fuzzy): New function.
|
||||
* c-parser.c: Include gcc-rich-location.h.
|
||||
(c_token_starts_typename): Split out case CPP_KEYWORD into...
|
||||
(c_keyword_starts_typename): ...this new function.
|
||||
(c_parser_declaration_or_fndef): When issuing errors about
|
||||
missing "struct" etc, add a fixit. For other kinds of errors,
|
||||
attempt to provide a suggestion via lookup_name_fuzzy.
|
||||
(c_parser_parms_declarator): When looking ahead to detect typos in
|
||||
type names, also reject CPP_KEYWORD.
|
||||
(c_parser_parameter_declaration): When issuing errors about
|
||||
unknown type names, attempt to provide a suggestion via
|
||||
lookup_name_fuzzy.
|
||||
* c-tree.h (c_keyword_starts_typename): New prototype.
|
||||
|
||||
2016-06-20 Joseph Myers <joseph@codesourcery.com>
|
||||
|
||||
PR c/71601
|
||||
|
|
190
gcc/c/c-decl.c
190
gcc/c/c-decl.c
|
@ -51,6 +51,8 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "c-family/c-ada-spec.h"
|
||||
#include "cilk.h"
|
||||
#include "builtins.h"
|
||||
#include "spellcheck-tree.h"
|
||||
#include "gcc-rich-location.h"
|
||||
|
||||
/* In grokdeclarator, distinguish syntactic contexts of declarators. */
|
||||
enum decl_context
|
||||
|
@ -3086,13 +3088,36 @@ implicit_decl_warning (location_t loc, tree id, tree olddecl)
|
|||
if (warn_implicit_function_declaration)
|
||||
{
|
||||
bool warned;
|
||||
tree hint = NULL_TREE;
|
||||
if (!olddecl)
|
||||
hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
|
||||
|
||||
if (flag_isoc99)
|
||||
warned = pedwarn (loc, OPT_Wimplicit_function_declaration,
|
||||
"implicit declaration of function %qE", id);
|
||||
if (hint)
|
||||
{
|
||||
gcc_rich_location richloc (loc);
|
||||
richloc.add_fixit_misspelled_id (loc, hint);
|
||||
warned = pedwarn_at_rich_loc
|
||||
(&richloc, OPT_Wimplicit_function_declaration,
|
||||
"implicit declaration of function %qE; did you mean %qE?",
|
||||
id, hint);
|
||||
}
|
||||
else
|
||||
warned = pedwarn (loc, OPT_Wimplicit_function_declaration,
|
||||
"implicit declaration of function %qE", id);
|
||||
else
|
||||
warned = warning_at (loc, OPT_Wimplicit_function_declaration,
|
||||
G_("implicit declaration of function %qE"), id);
|
||||
if (hint)
|
||||
{
|
||||
gcc_rich_location richloc (loc);
|
||||
richloc.add_fixit_misspelled_id (loc, hint);
|
||||
warned = warning_at_rich_loc
|
||||
(&richloc, OPT_Wimplicit_function_declaration,
|
||||
G_("implicit declaration of function %qE;did you mean %qE?"),
|
||||
id, hint);
|
||||
}
|
||||
else
|
||||
warned = warning_at (loc, OPT_Wimplicit_function_declaration,
|
||||
G_("implicit declaration of function %qE"), id);
|
||||
if (olddecl && warned)
|
||||
locate_old_decl (olddecl);
|
||||
}
|
||||
|
@ -3408,13 +3433,38 @@ undeclared_variable (location_t loc, tree id)
|
|||
|
||||
if (current_function_decl == 0)
|
||||
{
|
||||
error_at (loc, "%qE undeclared here (not in a function)", id);
|
||||
tree guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
|
||||
if (guessed_id)
|
||||
{
|
||||
gcc_rich_location richloc (loc);
|
||||
richloc.add_fixit_misspelled_id (loc, guessed_id);
|
||||
error_at_rich_loc (&richloc,
|
||||
"%qE undeclared here (not in a function);"
|
||||
" did you mean %qE?",
|
||||
id, guessed_id);
|
||||
}
|
||||
else
|
||||
error_at (loc, "%qE undeclared here (not in a function)", id);
|
||||
scope = current_scope;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!objc_diagnose_private_ivar (id))
|
||||
error_at (loc, "%qE undeclared (first use in this function)", id);
|
||||
{
|
||||
tree guessed_id = lookup_name_fuzzy (id, FUZZY_LOOKUP_NAME);
|
||||
if (guessed_id)
|
||||
{
|
||||
gcc_rich_location richloc (loc);
|
||||
richloc.add_fixit_misspelled_id (loc, guessed_id);
|
||||
error_at_rich_loc
|
||||
(&richloc,
|
||||
"%qE undeclared (first use in this function);"
|
||||
" did you mean %qE?",
|
||||
id, guessed_id);
|
||||
}
|
||||
else
|
||||
error_at (loc, "%qE undeclared (first use in this function)", id);
|
||||
}
|
||||
if (!already)
|
||||
{
|
||||
inform (loc, "each undeclared identifier is reported only"
|
||||
|
@ -3904,6 +3954,134 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
|
|||
return b->decl;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Specialization of edit_distance_traits for preprocessor macros. */
|
||||
|
||||
template <>
|
||||
struct edit_distance_traits<cpp_hashnode *>
|
||||
{
|
||||
static size_t get_length (cpp_hashnode *hashnode)
|
||||
{
|
||||
return hashnode->ident.len;
|
||||
}
|
||||
|
||||
static const char *get_string (cpp_hashnode *hashnode)
|
||||
{
|
||||
return (const char *)hashnode->ident.str;
|
||||
}
|
||||
};
|
||||
|
||||
/* Specialization of best_match<> for finding the closest preprocessor
|
||||
macro to a given identifier. */
|
||||
|
||||
typedef best_match<tree, cpp_hashnode *> best_macro_match;
|
||||
|
||||
/* A callback for cpp_forall_identifiers, for use by lookup_name_fuzzy.
|
||||
Process HASHNODE and update the best_macro_match instance pointed to be
|
||||
USER_DATA. */
|
||||
|
||||
static int
|
||||
find_closest_macro_cpp_cb (cpp_reader *, cpp_hashnode *hashnode,
|
||||
void *user_data)
|
||||
{
|
||||
if (hashnode->type != NT_MACRO)
|
||||
return 1;
|
||||
|
||||
best_macro_match *bmm = (best_macro_match *)user_data;
|
||||
bmm->consider (hashnode);
|
||||
|
||||
/* Keep iterating. */
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Look for the closest match for NAME within the currently valid
|
||||
scopes.
|
||||
|
||||
This finds the identifier with the lowest Levenshtein distance to
|
||||
NAME. If there are multiple candidates with equal minimal distance,
|
||||
the first one found is returned. Scopes are searched from innermost
|
||||
outwards, and within a scope in reverse order of declaration, thus
|
||||
benefiting candidates "near" to the current scope.
|
||||
|
||||
The function also looks for similar macro names to NAME, since a
|
||||
misspelled macro name will not be expanded, and hence looks like an
|
||||
identifier to the C frontend.
|
||||
|
||||
It also looks for start_typename keywords, to detect "singed" vs "signed"
|
||||
typos. */
|
||||
|
||||
tree
|
||||
lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind)
|
||||
{
|
||||
gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
|
||||
|
||||
best_match<tree, tree> bm (name);
|
||||
|
||||
/* Look within currently valid scopes. */
|
||||
for (c_scope *scope = current_scope; scope; scope = scope->outer)
|
||||
for (c_binding *binding = scope->bindings; binding; binding = binding->prev)
|
||||
{
|
||||
if (!binding->id)
|
||||
continue;
|
||||
/* Don't use bindings from implicitly declared functions,
|
||||
as they were likely misspellings themselves. */
|
||||
if (TREE_CODE (binding->decl) == FUNCTION_DECL)
|
||||
if (C_DECL_IMPLICIT (binding->decl))
|
||||
continue;
|
||||
if (kind == FUZZY_LOOKUP_TYPENAME)
|
||||
if (TREE_CODE (binding->decl) != TYPE_DECL)
|
||||
continue;
|
||||
bm.consider (binding->id);
|
||||
}
|
||||
|
||||
/* Consider macros: if the user misspelled a macro name e.g. "SOME_MACRO"
|
||||
as:
|
||||
x = SOME_OTHER_MACRO (y);
|
||||
then "SOME_OTHER_MACRO" will survive to the frontend and show up
|
||||
as a misspelled identifier.
|
||||
|
||||
Use the best distance so far so that a candidate is only set if
|
||||
a macro is better than anything so far. This allows early rejection
|
||||
(without calculating the edit distance) of macro names that must have
|
||||
distance >= bm.get_best_distance (), and means that we only get a
|
||||
non-NULL result for best_macro_match if it's better than any of
|
||||
the identifiers already checked, which avoids needless creation
|
||||
of identifiers for macro hashnodes. */
|
||||
best_macro_match bmm (name, bm.get_best_distance ());
|
||||
cpp_forall_identifiers (parse_in, find_closest_macro_cpp_cb, &bmm);
|
||||
cpp_hashnode *best_macro = bmm.get_best_meaningful_candidate ();
|
||||
/* If a macro is the closest so far to NAME, use it, creating an
|
||||
identifier tree node for it. */
|
||||
if (best_macro)
|
||||
{
|
||||
const char *id = (const char *)best_macro->ident.str;
|
||||
tree macro_as_identifier
|
||||
= get_identifier_with_length (id, best_macro->ident.len);
|
||||
bm.set_best_so_far (macro_as_identifier,
|
||||
bmm.get_best_distance (),
|
||||
bmm.get_best_candidate_length ());
|
||||
}
|
||||
|
||||
/* Try the "start_typename" keywords to detect
|
||||
"singed" vs "signed" typos. */
|
||||
if (kind == FUZZY_LOOKUP_TYPENAME)
|
||||
{
|
||||
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);
|
||||
bm.consider (resword_identifier);
|
||||
}
|
||||
}
|
||||
|
||||
return bm.get_best_meaningful_candidate ();
|
||||
}
|
||||
|
||||
|
||||
/* Create the predefined scalar types of C,
|
||||
and some nodes representing standard constants (0, 1, (void *) 0).
|
||||
|
|
144
gcc/c/c-parser.c
144
gcc/c/c-parser.c
|
@ -58,6 +58,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "c-family/c-indentation.h"
|
||||
#include "gimple-expr.h"
|
||||
#include "context.h"
|
||||
#include "gcc-rich-location.h"
|
||||
|
||||
/* We need to walk over decls with incomplete struct/union/enum types
|
||||
after parsing the whole translation unit.
|
||||
|
@ -518,6 +519,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 +584,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;
|
||||
|
@ -1655,15 +1662,50 @@ c_parser_declaration_or_fndef (c_parser *parser, bool fndef_ok,
|
|||
&& (!nested || !lookup_name (c_parser_peek_token (parser)->value)))
|
||||
{
|
||||
tree name = c_parser_peek_token (parser)->value;
|
||||
error_at (here, "unknown type name %qE", name);
|
||||
/* Give a hint to the user. This is not C++ with its implicit
|
||||
typedef. */
|
||||
|
||||
/* Issue a warning about NAME being an unknown type name, perhaps
|
||||
with some kind of hint.
|
||||
If the user forgot a "struct" etc, suggest inserting
|
||||
it. Otherwise, attempt to look for misspellings. */
|
||||
gcc_rich_location richloc (here);
|
||||
if (tag_exists_p (RECORD_TYPE, name))
|
||||
inform (here, "use %<struct%> keyword to refer to the type");
|
||||
{
|
||||
/* This is not C++ with its implicit typedef. */
|
||||
richloc.add_fixit_insert (here, "struct");
|
||||
error_at_rich_loc (&richloc,
|
||||
"unknown type name %qE;"
|
||||
" use %<struct%> keyword to refer to the type",
|
||||
name);
|
||||
}
|
||||
else if (tag_exists_p (UNION_TYPE, name))
|
||||
inform (here, "use %<union%> keyword to refer to the type");
|
||||
{
|
||||
richloc.add_fixit_insert (here, "union");
|
||||
error_at_rich_loc (&richloc,
|
||||
"unknown type name %qE;"
|
||||
" use %<union%> keyword to refer to the type",
|
||||
name);
|
||||
}
|
||||
else if (tag_exists_p (ENUMERAL_TYPE, name))
|
||||
inform (here, "use %<enum%> keyword to refer to the type");
|
||||
{
|
||||
richloc.add_fixit_insert (here, "enum");
|
||||
error_at_rich_loc (&richloc,
|
||||
"unknown type name %qE;"
|
||||
" use %<enum%> keyword to refer to the type",
|
||||
name);
|
||||
}
|
||||
else
|
||||
{
|
||||
tree hint = lookup_name_fuzzy (name, FUZZY_LOOKUP_TYPENAME);
|
||||
if (hint)
|
||||
{
|
||||
richloc.add_fixit_misspelled_id (here, hint);
|
||||
error_at_rich_loc (&richloc,
|
||||
"unknown type name %qE; did you mean %qE?",
|
||||
name, hint);
|
||||
}
|
||||
else
|
||||
error_at (here, "unknown type name %qE", name);
|
||||
}
|
||||
|
||||
/* Parse declspecs normally to get a correct pointer type, but avoid
|
||||
a further "fails to be a type name" error. Refuse nested functions
|
||||
|
@ -3632,7 +3674,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)
|
||||
|
@ -3807,7 +3850,18 @@ 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_TYPENAME);
|
||||
if (hint)
|
||||
{
|
||||
gcc_assert (TREE_CODE (hint) == IDENTIFIER_NODE);
|
||||
gcc_rich_location richloc (token->location);
|
||||
richloc.add_fixit_misspelled_id (token->location, 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
|
||||
|
|
|
@ -482,6 +482,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);
|
||||
|
|
|
@ -76,6 +76,8 @@ extern void fatal_error (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3)
|
|||
/* Pass one of the OPT_W* from options.h as the second parameter. */
|
||||
extern bool pedwarn (location_t, int, const char *, ...)
|
||||
ATTRIBUTE_GCC_DIAG(3,4);
|
||||
extern bool pedwarn_at_rich_loc (rich_location *, int, const char *, ...)
|
||||
ATTRIBUTE_GCC_DIAG(3,4);
|
||||
extern bool permerror (location_t, const char *, ...) ATTRIBUTE_GCC_DIAG(2,3);
|
||||
extern bool permerror_at_rich_loc (rich_location *, const char *,
|
||||
...) ATTRIBUTE_GCC_DIAG(2,3);
|
||||
|
|
|
@ -1201,6 +1201,18 @@ pedwarn (location_t location, int opt, const char *gmsgid, ...)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Same as pedwarn, but using RICHLOC. */
|
||||
|
||||
bool
|
||||
pedwarn_at_rich_loc (rich_location *richloc, int opt, const char *gmsgid, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start (ap, gmsgid);
|
||||
bool ret = diagnostic_impl (richloc, opt, gmsgid, &ap, DK_PEDWARN);
|
||||
va_end (ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* A "permissive" error at LOCATION: issues an error unless
|
||||
-fpermissive was given on the command line, in which case it issues
|
||||
a warning. Use this for things that really should be errors but we
|
||||
|
|
|
@ -69,11 +69,12 @@ class best_match
|
|||
|
||||
/* Constructor. */
|
||||
|
||||
best_match (goal_t goal)
|
||||
best_match (GOAL_TYPE goal,
|
||||
edit_distance_t best_distance_so_far = MAX_EDIT_DISTANCE)
|
||||
: m_goal (goal_traits::get_string (goal)),
|
||||
m_goal_len (goal_traits::get_length (goal)),
|
||||
m_best_candidate (NULL),
|
||||
m_best_distance (MAX_EDIT_DISTANCE)
|
||||
m_best_distance (best_distance_so_far)
|
||||
{}
|
||||
|
||||
/* Compare the edit distance between CANDIDATE and m_goal,
|
||||
|
@ -118,6 +119,20 @@ class best_match
|
|||
}
|
||||
}
|
||||
|
||||
/* Assuming that BEST_CANDIDATE is known to be better than
|
||||
m_best_candidate, update (without recomputing the edit distance to
|
||||
the goal). */
|
||||
|
||||
void set_best_so_far (CANDIDATE_TYPE best_candidate,
|
||||
edit_distance_t best_distance,
|
||||
size_t best_candidate_len)
|
||||
{
|
||||
gcc_assert (best_distance < m_best_distance);
|
||||
m_best_candidate = best_candidate;
|
||||
m_best_distance = best_distance;
|
||||
m_best_candidate_len = best_candidate_len;
|
||||
}
|
||||
|
||||
/* Get the best candidate so far, but applying a filter to ensure
|
||||
that we return NULL if none of the candidates are close to the goal,
|
||||
to avoid offering nonsensical suggestions to the user. */
|
||||
|
@ -135,6 +150,9 @@ class best_match
|
|||
return m_best_candidate;
|
||||
}
|
||||
|
||||
edit_distance_t get_best_distance () const { return m_best_distance; }
|
||||
size_t get_best_candidate_length () const { return m_best_candidate_len; }
|
||||
|
||||
private:
|
||||
const char *m_goal;
|
||||
size_t m_goal_len;
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
2016-06-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
PR c/70339
|
||||
* c-c++-common/attributes-1.c: Update dg-prune-output to include
|
||||
hint.
|
||||
* gcc.dg/diagnostic-token-ranges.c (undeclared_identifier): Update
|
||||
expected results due to builtin "nanl" now being suggested for
|
||||
"name".
|
||||
* gcc.dg/pr67580.c: Update expected messages.
|
||||
* gcc.dg/spellcheck-identifiers.c: New testcase.
|
||||
* gcc.dg/spellcheck-typenames.c: New testcase.
|
||||
|
||||
2016-06-22 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* gcc.dg/plugin/diagnostic-test-show-locus-parseable-fixits.c: New
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-prune-output "undeclared here \\(not in a function\\)|\[^\n\r\]* was not declared in this scope" } */
|
||||
/* { dg-prune-output "undeclared here \\(not in a function\\); did you mean .carg..|\[^\n\r\]* was not declared in this scope" } */
|
||||
|
||||
void* my_calloc(unsigned, unsigned) __attribute__((alloc_size(1,bar))); /* { dg-warning "outside range" } */
|
||||
void* my_realloc(void*, unsigned) __attribute__((alloc_size(bar))); /* { dg-warning "outside range" } */
|
||||
|
|
|
@ -6,11 +6,12 @@
|
|||
|
||||
void undeclared_identifier (void)
|
||||
{
|
||||
name; /* { dg-error "'name' undeclared" } */
|
||||
name; /* { dg-error "'name' undeclared .first use in this function.; did you mean .nanl." } */
|
||||
/*
|
||||
{ dg-begin-multiline-output "" }
|
||||
name;
|
||||
^~~~
|
||||
nanl
|
||||
{ dg-end-multiline-output "" }
|
||||
*/
|
||||
}
|
||||
|
|
|
@ -8,12 +8,9 @@ enum E { A };
|
|||
void
|
||||
f (void)
|
||||
{
|
||||
S s; /* { dg-error "unknown type name" } */
|
||||
/* { dg-message "use .struct. keyword to refer to the type" "" { target *-*-* } 11 } */
|
||||
U u; /* { dg-error "unknown type name" } */
|
||||
/* { dg-message "use .union. keyword to refer to the type" "" { target *-*-* } 13 } */
|
||||
E e; /* { dg-error "unknown type name" } */
|
||||
/* { dg-message "use .enum. keyword to refer to the type" "" { target *-*-* } 15 } */
|
||||
S s; /* { dg-error "unknown type name .S.; use .struct. keyword to refer to the type" } */
|
||||
U u; /* { dg-error "unknown type name .U.; use .union. keyword to refer to the type" } */
|
||||
E e; /* { dg-error "unknown type name .E.; use .enum. keyword to refer to the type" } */
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -22,10 +19,7 @@ g (void)
|
|||
struct T { int i; };
|
||||
union V { int i; };
|
||||
enum F { J };
|
||||
T t; /* { dg-error "unknown type name" } */
|
||||
/* { dg-message "use .struct. keyword to refer to the type" "" { target *-*-* } 25 } */
|
||||
V v; /* { dg-error "unknown type name" } */
|
||||
/* { dg-message "use .union. keyword to refer to the type" "" { target *-*-* } 27 } */
|
||||
F f; /* { dg-error "unknown type name" } */
|
||||
/* { dg-message "use .enum. keyword to refer to the type" "" { target *-*-* } 29 } */
|
||||
T t; /* { dg-error "unknown type name .T.; use .struct. keyword to refer to the type" } */
|
||||
V v; /* { dg-error "unknown type name .V.; use .union. keyword to refer to the type" } */
|
||||
F f; /* { dg-error "unknown type name .F.; use .enum. keyword to refer to the type" } */
|
||||
}
|
||||
|
|
136
gcc/testsuite/gcc.dg/spellcheck-identifiers.c
Normal file
136
gcc/testsuite/gcc.dg/spellcheck-identifiers.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-Wimplicit-function-declaration -fdiagnostics-show-caret" } */
|
||||
|
||||
typedef struct GtkWidget { int dummy; } GtkWidget;
|
||||
|
||||
extern void gtk_widget_show_all (GtkWidget *w);
|
||||
|
||||
void
|
||||
test_1 (GtkWidget *w)
|
||||
{
|
||||
gtk_widget_showall (w); /* { dg-warning "3: implicit declaration of function .gtk_widget_showall.; did you mean .gtk_widget_show_all.?" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
gtk_widget_showall (w);
|
||||
^~~~~~~~~~~~~~~~~~
|
||||
gtk_widget_show_all
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Ensure we don't try to suggest "gtk_widget_showall" for subsequent
|
||||
corrections. */
|
||||
gtk_widget_showall_ (w); /* { dg-warning "3: implicit declaration of function .gtk_widget_showall_.; did you mean .gtk_widget_show_all.?" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
gtk_widget_showall_ (w);
|
||||
^~~~~~~~~~~~~~~~~~~
|
||||
gtk_widget_show_all
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
GtkWidgetShowAll (w); /* { dg-warning "3: implicit declaration of function .GtkWidgetShowAll.; did you mean .gtk_widget_show_all.?" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
GtkWidgetShowAll (w);
|
||||
^~~~~~~~~~~~~~~~
|
||||
gtk_widget_show_all
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
int
|
||||
test_2 (int param)
|
||||
{
|
||||
return parma * parma; /* { dg-error "10: .parma. undeclared .first use in this function.; did you mean .param." } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return parma * parma;
|
||||
^~~~~
|
||||
param
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
#define MACRO(X) ((X))
|
||||
|
||||
int
|
||||
test_3 (int i)
|
||||
{
|
||||
return MACRAME (i); /* { dg-warning "10: implicit declaration of function .MACRAME.; did you mean .MACRO.?" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return MACRAME (i);
|
||||
^~~~~~~
|
||||
MACRO
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
#define IDENTIFIER_POINTER(X) ((X))
|
||||
|
||||
int
|
||||
test_4 (int node)
|
||||
{
|
||||
return IDENTIFIER_PTR (node); /* { dg-warning "10: implicit declaration of function .IDENTIFIER_PTR.; did you mean .IDENTIFIER_POINTER.?" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return IDENTIFIER_PTR (node);
|
||||
^~~~~~~~~~~~~~
|
||||
IDENTIFIER_POINTER
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
test_5 (void)
|
||||
{
|
||||
return __LINE_; /* { dg-error "10: .__LINE_. undeclared .first use in this function.; did you mean .__LINE__." } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
return __LINE_;
|
||||
^~~~~~~
|
||||
__LINE__
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
||||
|
||||
#define MAX_ITEMS 100
|
||||
int array[MAX_ITEM]; /* { dg-error "11: .MAX_ITEM. undeclared here .not in a function.; did you mean .MAX_ITEMS." } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
int array[MAX_ITEM];
|
||||
^~~~~~~~
|
||||
MAX_ITEMS
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
|
||||
enum foo {
|
||||
FOO_FIRST,
|
||||
FOO_SECOND
|
||||
};
|
||||
|
||||
int
|
||||
test_6 (enum foo f)
|
||||
{
|
||||
switch (f)
|
||||
{
|
||||
case FOO_FURST: /* { dg-error "10: .FOO_FURST. undeclared .first use in this function.; did you mean .FOO_FIRST." } */
|
||||
break;
|
||||
/* { dg-begin-multiline-output "" }
|
||||
case FOO_FURST:
|
||||
^~~~~~~~~
|
||||
FOO_FIRST
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
case FOO_SECCOND: /* { dg-error "10: .FOO_SECCOND. undeclared .first use in this function.; did you mean .FOO_SECOND." } */
|
||||
break;
|
||||
/* { dg-begin-multiline-output "" }
|
||||
case FOO_SECCOND:
|
||||
^~~~~~~~~~~
|
||||
FOO_SECOND
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Verify that we offer names of builtins as suggestions. */
|
||||
|
||||
void
|
||||
test_7 (int i, int j)
|
||||
{
|
||||
int buffer[100];
|
||||
snprint (buffer, 100, "%i of %i", i, j); /* { dg-warning "3: implicit declaration of function .snprint.; did you mean .snprintf.." } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
snprint (buffer, 100, "%i of %i", i, j);
|
||||
^~~~~~~
|
||||
snprintf
|
||||
{ dg-end-multiline-output "" } */
|
||||
}
|
107
gcc/testsuite/gcc.dg/spellcheck-typenames.c
Normal file
107
gcc/testsuite/gcc.dg/spellcheck-typenames.c
Normal file
|
@ -0,0 +1,107 @@
|
|||
/* { dg-do compile } */
|
||||
/* { 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 "" } */
|
||||
|
||||
/* Verify that we handle misspelled typedef names. */
|
||||
|
||||
typedef struct something {} something_t;
|
||||
|
||||
some_thing_t test_5; /* { dg-error "1: unknown type name .some_thing_t.; did you mean .something_t.?" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
some_thing_t test_5;
|
||||
^~~~~~~~~~~~
|
||||
something_t
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* TODO: we don't yet handle misspelled struct names. */
|
||||
struct some_thing test_6; /* { dg-error "storage size of .test_6. isn't known" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
struct some_thing test_6;
|
||||
^~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
typedef long int64_t;
|
||||
int64 i; /* { dg-error "unknown type name 'int64'; did you mean 'int64_t'?" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
int64 i;
|
||||
^~~~~
|
||||
int64_t
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* Verify that gcc doesn't offer nonsensical suggestions. */
|
||||
|
||||
nonsensical_suggestion_t var; /* { dg-bogus "did you mean" } */
|
||||
/* { dg-error "unknown type name" "" { target { *-*-* } } 56 } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
nonsensical_suggestion_t var;
|
||||
^~~~~~~~~~~~~~~~~~~~~~~~
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
|
||||
/* In the following, we should suggest inserting "struct" (rather
|
||||
than "did you mean 'float'") and provide a fixit hint. */
|
||||
struct foo_t {
|
||||
int i;
|
||||
};
|
||||
foo_t *foo_ptr; /* { dg-error "1: unknown type name .foo_t.; use .struct. keyword to refer to the type" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
foo_t *foo_ptr;
|
||||
^~~~~
|
||||
struct
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
|
||||
/* Similarly for unions. */
|
||||
union bar_t {
|
||||
int i;
|
||||
char j;
|
||||
};
|
||||
bar_t *bar_ptr; /* { dg-error "1: unknown type name .bar_t.; use .union. keyword to refer to the type" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
bar_t *bar_ptr;
|
||||
^~~~~
|
||||
union
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
|
||||
/* Similarly for enums. */
|
||||
enum baz {
|
||||
BAZ_FIRST,
|
||||
BAZ_SECOND
|
||||
};
|
||||
baz value; /* { dg-error "1: unknown type name .baz.; use .enum. keyword to refer to the type" } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
baz value;
|
||||
^~~
|
||||
enum
|
||||
{ dg-end-multiline-output "" } */
|
||||
|
||||
/* TODO: it would be better to detect the "singed" vs "signed" typo here. */
|
||||
singed char ch; /* { dg-error "8: before .char." } */
|
||||
/* { dg-begin-multiline-output "" }
|
||||
singed char ch;
|
||||
^~~~
|
||||
{ dg-end-multiline-output "" } */
|
Loading…
Add table
Reference in a new issue