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