I noticed when testing the previous patch on the C frontend that
the fix-it hints weren't always showing up:
-Wimplicit-function-declaration wasn't always providing a fix-it
hint, but "incompatible implicit declaration of built-in function"
was, *if* it warned.
Consider e.g.:
$ cat t.c
void test (const char *a, const char *b)
{
if (strcmp (a, b))
printf ("a: %s, b: %b\n", a, b);
}
$ gcc -c t.c
t.c: In function 'test':
t.c:3:7: warning: implicit declaration of function 'strcmp'
[-Wimplicit-function-declaration]
if (strcmp (a, b))
^~~~~~
t.c:4:5: warning: implicit declaration of function 'printf'
[-Wimplicit-function-declaration]
printf ("a: %s, b: %b\n", a, b);
^~~~~~
t.c:4:5: warning: incompatible implicit declaration of built-in function
'printf'
t.c:4:5: note: include '<stdio.h>' or provide a declaration of 'printf'
t.c:1:1:
+#include <stdio.h>
void test (const char *a, const char *b)
t.c:4:5:
printf ("a: %s, b: %b\n", a, b);
^~~~~~
Note how "printf" gets a fix-it hint, but strcmp doesn't: we aren't
suggesting <string.h>.
When implicit_decl_warning complains, but OLDDECL is non-NULL, it
wasn't suggesting missing #includes. If implicitly_declare later
complains about the implicit decl being incompatible, then we get a
fix-it hint, but if implicitly_declare *doesn't* complain (as in
"strcmp" above), then there's no suggestion about which header to use.
This patch tweaks implicit_decl_warning so that it attempts to provide
a fix-it hint for this case. They're idempotent, so it leads to minimal
extra output for the case where implicitly_declare complains a second
time (one extra "note"), for the <stdio.h>/sprintf case below:
t.c: In function 'test':
t.c:3:7: warning: implicit declaration of function 'strcmp'
[-Wimplicit-function-declaration]
if (strcmp (a, b))
^~~~~~
t.c:3:7: note: 'strcmp' is defined in header '<string.h>'; did you forget to
'#include <string.h>'?
t.c:1:1:
+#include <string.h>
void test (const char *a, const char *b)
t.c:3:7:
if (strcmp (a, b))
^~~~~~
t.c:4:5: warning: implicit declaration of function 'printf'
[-Wimplicit-function-declaration]
printf ("a: %s, b: %b\n", a, b);
^~~~~~
t.c:4:5: note: 'printf' is defined in header '<stdio.h>'; did you forget to
'#include <stdio.h>'?
t.c:1:1:
+#include <stdio.h>
void test (const char *a, const char *b)
t.c:4:5:
printf ("a: %s, b: %b\n", a, b);
^~~~~~
t.c:4:5: warning: incompatible implicit declaration of built-in function
'printf'
t.c:4:5: note: include '<stdio.h>' or provide a declaration of 'printf'
Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
OK for trunk?
gcc/c/ChangeLog:
PR c++/84269
* c-decl.c (implicit_decl_warning): When OLDDECL is non-NULL,
potentially provide a missing header hint.
gcc/testsuite/ChangeLog:
PR c++/84269
* gcc.dg/spellcheck-stdlib-2.c: New test.
* gcc.dg/spellcheck-stdlib-3.c: New test.
* gcc.dg/spellcheck-stdlib.c: Add tests for <stdio.h>, <string.h>,
and <stdlib.h>.
FIXME: C fixes: stdlib.h
---
gcc/c/c-decl.c | 14 ++++++++-
gcc/testsuite/gcc.dg/spellcheck-stdlib-2.c | 38 +++++++++++++++++++++++
gcc/testsuite/gcc.dg/spellcheck-stdlib-3.c | 16 ++++++++++
gcc/testsuite/gcc.dg/spellcheck-stdlib.c | 50 ++++++++++++++++++++++++++++++
4 files changed, 117 insertions(+), 1 deletion(-)
create mode 100644 gcc/testsuite/gcc.dg/spellcheck-stdlib-2.c
create mode 100644 gcc/testsuite/gcc.dg/spellcheck-stdlib-3.c
diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c
index f0198ec..89db511 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -3113,7 +3113,19 @@ implicit_decl_warning (location_t loc, tree id, tree
olddecl)
bool warned;
name_hint hint;
- if (!olddecl)
+ if (olddecl)
+ {
+ /* It might be due to a missing header.
+ Provide a hint, without looking for misspellings. */
+ const char *header_hint
+ = get_c_stdlib_header_for_name (IDENTIFIER_POINTER (id));
+ if (header_hint)
+ hint = name_hint (NULL,
+ new suggest_missing_header (loc,
+ IDENTIFIER_POINTER (id),
+ header_hint));
+ }
+ else
hint = lookup_name_fuzzy (id, FUZZY_LOOKUP_FUNCTION_NAME, loc);
if (flag_isoc99)
diff --git a/gcc/testsuite/gcc.dg/spellcheck-stdlib-2.c
b/gcc/testsuite/gcc.dg/spellcheck-stdlib-2.c
new file mode 100644
index 0000000..5086374
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-stdlib-2.c
@@ -0,0 +1,38 @@
+/* Test coverage for header suggestions for common names in the stdlib
+ for which -Wimplicit-function-declaration only emits one message. */
+
+/* { dg-options "-Wimplicit-function-declaration" } */
+
+/* Missing <stdio.h>. */
+
+void test_stdio_h (void)
+{
+ fopen ("test.txt"); /* { dg-warning "implicit declaration" } */
+ /* { dg-message "'#include <stdio.h>'" "" { target *-*-* } .-1 } */
+
+ getchar (); /* { dg-warning "implicit declaration" } */
+ /* { dg-message "'#include <stdio.h>'" "" { target *-*-* } .-1 } */
+}
+
+/* Missing <string.h>. */
+
+void test_string_h (char *dest, char *src)
+{
+ char buf[16];
+ memcmp(dest, src, 4); /* { dg-warning "implicit declaration" } */
+ /* { dg-message "'#include <string.h>'" "" { target *-*-* } .-1 } */
+ strcmp(dest, "test"); /* { dg-warning "implicit declaration" } */
+ /* { dg-message "'#include <string.h>'" "" { target *-*-* } .-1 } */
+ strncmp(dest, "test", 3); /* { dg-warning "implicit declaration" } */
+ /* { dg-message "'#include <string.h>'" "" { target *-*-* } .-1 } */
+ snprintf (buf, 16, "test\n"); /* { dg-warning "implicit declaration" } */
+ /* { dg-message "include '<stdio.h>'" "" { target *-*-* } .-1 } */
+}
+
+/* Missing <assert.h>. */
+
+void test (int a, int b)
+{
+ assert (a == b); /* { dg-warning "implicit declaration" } */
+ // { dg-message "'#include <assert.h>'" "" { target *-*-* } .-1 }
+}
diff --git a/gcc/testsuite/gcc.dg/spellcheck-stdlib-3.c
b/gcc/testsuite/gcc.dg/spellcheck-stdlib-3.c
new file mode 100644
index 0000000..73ec9f7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/spellcheck-stdlib-3.c
@@ -0,0 +1,16 @@
+/* Test coverage for header suggestions for common names in the stdlib
+ for which -Wimplicit-function-declaration leads to two messages
+ being emitted. */
+
+/* { dg-options "-Wimplicit-function-declaration" } */
+
+/* Missing <stdio.h>. */
+
+void test_stdio_h (void)
+{
+ char buf[16];
+ sprintf (buf, "test\n"); /* { dg-warning "implicit declaration of function
'sprintf'" } */
+ /* { dg-message "'sprintf' is defined in header '<stdio.h>'; did you forget
to '#include <stdio.h>'." "" { target *-*-* } .-1 } */
+ /* { dg-warning "incompatible implicit declaration of built-in function
'sprintf'" "" { target *-*-* } .-2 } */
+ /* { dg-message "include '<stdio.h>' or provide a declaration of 'sprintf'"
"" { target *-*-* } .-3 } */
+}
diff --git a/gcc/testsuite/gcc.dg/spellcheck-stdlib.c
b/gcc/testsuite/gcc.dg/spellcheck-stdlib.c
index 7474c9a..e36beac 100644
--- a/gcc/testsuite/gcc.dg/spellcheck-stdlib.c
+++ b/gcc/testsuite/gcc.dg/spellcheck-stdlib.c
@@ -36,6 +36,12 @@ void test_stdio_h (void)
EOF; /* { dg-error "'EOF' undeclared" } */
/* { dg-message "'EOF' is defined in header '<stdio.h>'; did you forget to
'#include <stdio.h>'?" "" { target *-*-* } .-1 } */
+
+ printf ("test\n"); /* { dg-warning "incompatible implicit declaration" } */
+ /* { dg-message "include '<stdio.h>'" "" { target *-*-* } .-1 } */
+
+ sprintf (buf, "test\n"); /* { dg-warning "incompatible implicit
declaration" } */
+ /* { dg-message "include '<stdio.h>'" "" { target *-*-* } .-1 } */
}
/* Missing <errno.h>. */
@@ -62,3 +68,47 @@ int test_INT_MAX (void)
/* { dg-bogus "__INT_MAX__" "" { target *-*-* } INT_MAX_line } */
/* { dg-message "'INT_MAX' is defined in header '<limits.h>'; did you forget
to '#include <limits.h>'?" "" { target *-*-* } INT_MAX_line } */
}
+
+/* Missing <string.h>. */
+
+void test_string_h (char *dest, char *src)
+{
+ memchr(dest, 'a', 4); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ memcpy(dest, src, 4); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ memmove(dest, src, 4); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ memset(dest, 'a', 4); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strcat(dest, "test"); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strchr("test", 'e'); /* { dg-warning "incompatible implicit declaration" } */
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strcpy(dest, "test"); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strlen("test"); /* { dg-warning "incompatible implicit declaration" } */
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strncat(dest, "test", 3); /* { dg-warning "incompatible implicit
declaration" } */
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strncpy(dest, "test", 3); /* { dg-warning "incompatible implicit
declaration" } */
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strrchr("test", 'e'); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strspn(dest, "test"); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+ strstr(dest, "test"); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<string.h>'" "" { target *-*-* } .-1 } */
+}
+
+/* Missing <stdlib.h>. */
+
+void test_stdlib_h (void *q)
+{
+ void *ptr = malloc (64); /* { dg-warning "incompatible implicit declaration"
} */
+ /* { dg-message "include '<stdlib.h>'" "" { target *-*-* } .-1 } */
+ free (ptr); /* { dg-warning "incompatible implicit declaration" } */
+ /* { dg-message "include '<stdlib.h>'" "" { target *-*-* } .-1 } */
+ q = realloc (q, 1024); /* { dg-warning "incompatible implicit declaration" }
*/
+ /* { dg-message "include '<stdlib.h>'" "" { target *-*-* } .-1 } */
+}
--
1.8.5.3