Commit b1212255 by David Malcolm Committed by David Malcolm

C: hints for missing stdlib includes for macros and types

The C frontend already "knows" about many common functions in
the C standard library:

  test.c: In function 'test':
  test.c:3:3: warning: implicit declaration of function 'printf' [-Wimplicit-function-declaration]
     printf ("hello world\n");
     ^~~~~~
  test.c:3:3: warning: incompatible implicit declaration of built-in function 'printf'
  test.c:3:3: note: include '<stdio.h>' or provide a declaration of 'printf'

and which header file they are in.

However it doesn't know about various types and macros:

test.c:1:13: error: 'NULL' undeclared here (not in a function)
 void *ptr = NULL;
             ^~~~

This patch uses the name_hint/deferred_diagnostic machinery to
add hints for missing C standard library headers for some of the
most common type and macro names.

For example, the above becomes:
test.c:1:13: error: 'NULL' undeclared here (not in a function)
 void *ptr = NULL;
             ^~~~
test.c:1:13: note: 'NULL' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?

gcc/c/ChangeLog:
	* c-decl.c (get_c_name_hint): New function.
	(class suggest_missing_header): New class.
	(lookup_name_fuzzy): Call get_c_name_hint and use it to
	suggest missing headers to the user.

gcc/testsuite/ChangeLog:
	* gcc.dg/spellcheck-stdlib.c: New test case.

From-SVN: r254979
parent 01ada121
2017-11-20 David Malcolm <dmalcolm@redhat.com>
* c-decl.c (get_c_name_hint): New function.
(class suggest_missing_header): New class.
(lookup_name_fuzzy): Call get_c_name_hint and use it to
suggest missing headers to the user.
2017-11-20 David Malcolm <dmalcolm@redhat.com>
* c-decl.c: Define INCLUDE_UNIQUE_PTR before including system.h.
Include "c-family/name-hint.h"
(implicit_decl_warning): Convert "hint" from
......
......@@ -3992,6 +3992,83 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
return NULL_TREE;
}
/* Subroutine of lookup_name_fuzzy for handling unrecognized names
for some of the most common names within the C standard library.
Given non-NULL NAME, return the header name defining it within the C
standard library (with '<' and '>'), or NULL. */
static const char *
get_c_name_hint (const char *name)
{
struct std_name_hint
{
const char *name;
const char *header;
};
static const std_name_hint hints[] = {
/* <errno.h>. */
{"errno", "<errno.h>"},
/* <stdarg.h>. */
{"va_list", "<stdarg.h>"},
/* <stddef.h>. */
{"NULL", "<stddef.h>"},
{"ptrdiff_t", "<stddef.h>"},
{"wchar_t", "<stddef.h>"},
{"size_t", "<stddef.h>"},
/* <stdio.h>. */
{"BUFSIZ", "<stdio.h>"},
{"EOF", "<stdio.h>"},
{"FILE", "<stdio.h>"},
{"FILENAME_MAX", "<stdio.h>"},
{"fpos_t", "<stdio.h>"},
{"stderr", "<stdio.h>"},
{"stdin", "<stdio.h>"},
{"stdout", "<stdio.h>"}
};
const size_t num_hints = sizeof (hints) / sizeof (hints[0]);
for (size_t i = 0; i < num_hints; i++)
{
if (0 == strcmp (name, hints[i].name))
return hints[i].header;
}
return NULL;
}
/* Subclass of deferred_diagnostic for suggesting to the user
that they have missed a #include. */
class suggest_missing_header : public deferred_diagnostic
{
public:
suggest_missing_header (location_t loc, const char *name,
const char *header_hint)
: deferred_diagnostic (loc), m_name_str (name), m_header_hint (header_hint)
{
gcc_assert (name);
gcc_assert (header_hint);
}
~suggest_missing_header ()
{
if (is_suppressed_p ())
return;
gcc_rich_location richloc (get_location ());
maybe_add_include_fixit (&richloc, m_header_hint);
inform (&richloc,
"%qs is defined in header %qs;"
" did you forget to %<#include %s%>?",
m_name_str, m_header_hint, m_header_hint);
}
private:
const char *m_name_str;
const char *m_header_hint;
};
/* Look for the closest match for NAME within the currently valid
scopes.
......@@ -4006,13 +4083,24 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
identifier to the C frontend.
It also looks for start_typename keywords, to detect "singed" vs "signed"
typos. */
typos.
Use LOC for any deferred diagnostics. */
name_hint
lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t)
lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t loc)
{
gcc_assert (TREE_CODE (name) == IDENTIFIER_NODE);
/* First, try some well-known names in the C standard library, in case
the user forgot a #include. */
const char *header_hint = get_c_name_hint (IDENTIFIER_POINTER (name));
if (header_hint)
return name_hint (NULL,
new suggest_missing_header (loc,
IDENTIFIER_POINTER (name),
header_hint));
best_match<tree, tree> bm (name);
/* Look within currently valid scopes. */
......
2017-11-20 David Malcolm <dmalcolm@redhat.com>
* gcc.dg/spellcheck-stdlib.c: New test case.
2017-11-20 David Malcolm <dmalcolm@redhat.com>
PR c++/72786
* g++.dg/spellcheck-macro-ordering-2.C: New test case.
* g++.dg/spellcheck-macro-ordering.C: Add dg-message directives
......
/* Missing <stddef.h>. */
void *ptr = NULL; /* { dg-error "'NULL' undeclared here" } */
/* { dg-message "'NULL' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?" "" { target *-*-* } .-1 } */
ptrdiff_t pd; /* { dg-error "unknown type name 'ptrdiff_t'" } */
/* { dg-message "'ptrdiff_t' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?" "" { target *-*-* } .-1 } */
wchar_t wc; /* { dg-error "unknown type name 'wchar_t'" } */
/* { dg-message "'wchar_t' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?" "" { target *-*-* } .-1 } */
size_t sz; /* { dg-error "unknown type name 'size_t'" } */
/* { dg-message "'size_t' is defined in header '<stddef.h>'; did you forget to '#include <stddef.h>'?" "" { target *-*-* } .-1 } */
/* Missing <stdio.h>. */
void test_stdio_h (void)
{
FILE *f; /* { dg-error "unknown type name 'FILE'" } */
/* { dg-message "'FILE' is defined in header '<stdio.h>'; did you forget to '#include <stdio.h>'?" "" { target *-*-* } .-1 } */
char buf[BUFSIZ]; /* { dg-error "'BUFSIZ' undeclared" } */
/* { dg-message "'BUFSIZ' is defined in header '<stdio.h>'; did you forget to '#include <stdio.h>'?" "" { target *-*-* } .-1 } */
char buf2[FILENAME_MAX]; /* { dg-error "'FILENAME_MAX' undeclared" } */
/* { dg-message "'FILENAME_MAX' is defined in header '<stdio.h>'; did you forget to '#include <stdio.h>'?" "" { target *-*-* } .-1 } */
stderr; /* { dg-error "'stderr' undeclared" } */
/* { dg-message "'stderr' is defined in header '<stdio.h>'; did you forget to '#include <stdio.h>'?" "" { target *-*-* } .-1 } */
stdin; /* { dg-error "'stdin' undeclared" } */
/* { dg-message "'stdin' is defined in header '<stdio.h>'; did you forget to '#include <stdio.h>'?" "" { target *-*-* } .-1 } */
stdout; /* { dg-error "'stdout' undeclared" } */
/* { dg-message "'stdout' is defined in header '<stdio.h>'; did you forget to '#include <stdio.h>'?" "" { target *-*-* } .-1 } */
EOF; /* { dg-error "'EOF' undeclared" } */
/* { dg-message "'EOF' is defined in header '<stdio.h>'; did you forget to '#include <stdio.h>'?" "" { target *-*-* } .-1 } */
}
/* Missing <errno.h>. */
int test_errno_h (void)
{
return errno; /* { dg-error "'errno' undeclared" } */
/* { dg-message "'errno' is defined in header '<errno.h>'; did you forget to '#include <errno.h>'?" "" { target *-*-* } .-1 } */
}
/* Missing <stdarg.h>. */
void test_stdarg_h (void)
{
va_list ap; /* { dg-error "unknown type name 'va_list'" } */
/* { dg-message "'va_list' is defined in header '<stdarg.h>'; did you forget to '#include <stdarg.h>'?" "" { target *-*-* } .-1 } */
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment