This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH 3/3] C: hints for missing stdlib includes for macros and types
- From: David Malcolm <dmalcolm at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Cc: David Malcolm <dmalcolm at redhat dot com>
- Date: Fri, 5 May 2017 13:51:15 -0400
- Subject: [PATCH 3/3] C: hints for missing stdlib includes for macros and types
- Authentication-results: sourceware.org; auth=none
- Authentication-results: ext-mx02.extmail.prod.ext.phx2.redhat.com; dmarc=none (p=none dis=none) header.from=redhat.com
- Authentication-results: ext-mx02.extmail.prod.ext.phx2.redhat.com; spf=pass smtp.mailfrom=dmalcolm at redhat dot com
- Dkim-filter: OpenDKIM Filter v2.11.0 mx1.redhat.com 98BE47F4B8
- Dmarc-filter: OpenDMARC Filter v1.3.2 mx1.redhat.com 98BE47F4B8
- References: <1494006675-28033-1-git-send-email-dmalcolm@redhat.com>
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>'?
If the patch to add fix-it hints for missing #includes is approved:
https://gcc.gnu.org/ml/gcc-patches/2017-05/msg00321.html
then it's trivial to add a fix-it hint to the note.
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.
---
gcc/c/c-decl.c | 87 +++++++++++++++++++++++++++++++-
gcc/testsuite/gcc.dg/spellcheck-stdlib.c | 55 ++++++++++++++++++++
2 files changed, 140 insertions(+), 2 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/spellcheck-stdlib.c
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index 64075f9..d3c2bc5 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -4000,6 +4000,78 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
return NULL_TREE;
}
+/* Subroutine of suggest_missing_header::emit 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);
+ }
+
+ void emit ()
+ {
+ inform (get_location (),
+ "%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.
@@ -4014,13 +4086,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. */
diff --git a/gcc/testsuite/gcc.dg/spellcheck-stdlib.c b/gcc/testsuite/gcc.dg/spellcheck-stdlib.c
new file mode 100644
index 0000000..85a21c3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-stdlib.c
@@ -0,0 +1,55 @@
+/* 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 } */
+}
--
1.8.5.3