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:
David Malcolm 2016-06-22 15:20:41 +00:00 committed by David Malcolm
parent 6f99ef82f1
commit 1a4f11c88a
16 changed files with 634 additions and 67 deletions

View file

@ -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

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -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).

View file

@ -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

View file

@ -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);

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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" } */

View file

@ -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 "" }
*/
}

View file

@ -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" } */
}

View 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 "" } */
}

View 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 "" } */