diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 0255d8a3744b..1f8417911915 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2017-11-20 David Malcolm + + PR c/81404 + * Makefile.in (C_COMMON_OBJS): Add c-family/known-headers.o. + 2017-11-20 David Malcolm PR c++/72786 diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 38ab4e810268..f2a897c59623 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1191,7 +1191,7 @@ C_COMMON_OBJS = c-family/c-common.o c-family/c-cppbuiltin.o c-family/c-dump.o \ c-family/c-semantics.o c-family/c-ada-spec.o \ c-family/c-cilkplus.o \ c-family/array-notation-common.o c-family/cilk.o c-family/c-ubsan.o \ - c-family/c-attribs.o c-family/c-warn.o + c-family/c-attribs.o c-family/c-warn.o c-family/known-headers.o # Language-independent object files. # We put the *-match.o and insn-*.o files first so that a parallel make diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 9d49eb934e47..1e5c3d3e1872 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,13 @@ +2017-11-20 David Malcolm + + PR c/81404 + * known-headers.cc: New file, based on material from c/c-decl.c. + (suggest_missing_header): Copied as-is. + (get_stdlib_header_for_name): New, based on get_c_name_hint but + heavily edited to add C++ support. Add some knowledge about + , , and . + * known-headers.h: Likewise. + 2017-11-20 David Malcolm * c-common.h (enum lookup_name_fuzzy_kind): Move to name-hint.h. diff --git a/gcc/c-family/known-headers.cc b/gcc/c-family/known-headers.cc new file mode 100644 index 000000000000..d4cd39b10631 --- /dev/null +++ b/gcc/c-family/known-headers.cc @@ -0,0 +1,169 @@ +/* Support for suggestions about missing #include directives. + Copyright (C) 2017 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#define INCLUDE_UNIQUE_PTR +#include "system.h" +#include "coretypes.h" +#include "c-family/c-common.h" +#include "c-family/name-hint.h" +#include "c-family/known-headers.h" +#include "gcc-rich-location.h" + +/* An enum for distinguishing between the C and C++ stdlibs. */ + +enum stdlib +{ + STDLIB_C, + STDLIB_CPLUSPLUS, + + NUM_STDLIBS +}; + +/* A struct for associating names in a standard library with the header + that should be included to locate them, for each of the C and C++ stdlibs + (or NULL, for names that aren't in a header for a particular stdlib). */ + +struct stdlib_hint +{ + const char *name; + const char *header[NUM_STDLIBS]; +}; + +/* Given non-NULL NAME, return the header name defining it within either + the standard library (with '<' and '>'), or NULL. + Only handles a subset of the most common names within the stdlibs. */ + +static const char * +get_stdlib_header_for_name (const char *name, enum stdlib lib) +{ + gcc_assert (name); + gcc_assert (lib < NUM_STDLIBS); + + static const stdlib_hint hints[] = { + /* and . */ + {"errno", {"", ""} }, + + /* and . */ + {"CHAR_BIT", {"", ""} }, + {"CHAR_MAX", {"", ""} }, + {"CHAR_MIN", {"", ""} }, + {"INT_MAX", {"", ""} }, + {"INT_MIN", {"", ""} }, + {"LLONG_MAX", {"", ""} }, + {"LLONG_MIN", {"", ""} }, + {"LONG_MAX", {"", ""} }, + {"LONG_MIN", {"", ""} }, + {"MB_LEN_MAX", {"", ""} }, + {"SCHAR_MAX", {"", ""} }, + {"SCHAR_MIN", {"", ""} }, + {"SHRT_MAX", {"", ""} }, + {"SHRT_MIN", {"", ""} }, + {"UCHAR_MAX", {"", ""} }, + {"UINT_MAX", {"", ""} }, + {"ULLONG_MAX", {"", ""} }, + {"ULONG_MAX", {"", ""} }, + {"USHRT_MAX", {"", ""} }, + + /* and . */ + {"va_list", {"", ""} }, + + /* and . */ + {"NULL", {"", ""} }, + {"nullptr_t", {NULL, ""} }, + {"offsetof", {"", ""} }, + {"ptrdiff_t", {"", ""} }, + {"size_t", {"", ""} }, + {"wchar_t", {"", NULL /* a keyword in C++ */} }, + + /* . */ + {"BUFSIZ", {"", ""} }, + {"EOF", {"", ""} }, + {"FILE", {"", ""} }, + {"FILENAME_MAX", {"", ""} }, + {"fpos_t", {"", ""} }, + {"stderr", {"", ""} }, + {"stdin", {"", ""} }, + {"stdout", {"", ""} }, + + /* . */ + {"PTRDIFF_MAX", {"", ""} }, + {"PTRDIFF_MIN", {"", ""} }, + {"SIG_ATOMIC_MAX", {"", ""} }, + {"SIG_ATOMIC_MIN", {"", ""} }, + {"SIZE_MAX", {"", ""} }, + {"WINT_MAX", {"", ""} }, + {"WINT_MIN", {"", ""} }, + + /* . */ + {"WCHAR_MAX", {"", ""} }, + {"WCHAR_MIN", {"", ""} } + }; + 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[lib]; + return NULL; +} + +/* Given non-NULL NAME, return the header name defining it within the C + standard library (with '<' and '>'), or NULL. */ + +const char * +get_c_stdlib_header_for_name (const char *name) +{ + return get_stdlib_header_for_name (name, STDLIB_C); +} + +/* Given non-NULL NAME, return the header name defining it within the C++ + standard library (with '<' and '>'), or NULL. */ + +const char * +get_cp_stdlib_header_for_name (const char *name) +{ + return get_stdlib_header_for_name (name, STDLIB_CPLUSPLUS); +} + +/* Implementation of class suggest_missing_header. */ + +/* suggest_missing_header's ctor. */ + +suggest_missing_header::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's dtor. */ + +suggest_missing_header::~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); +} diff --git a/gcc/c-family/known-headers.h b/gcc/c-family/known-headers.h new file mode 100644 index 000000000000..328100f40409 --- /dev/null +++ b/gcc/c-family/known-headers.h @@ -0,0 +1,41 @@ +/* Support for suggestions about missing #include directives. + Copyright (C) 2017 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_KNOWN_HEADERS_H +#define GCC_KNOWN_HEADERS_H + +extern const char *get_c_stdlib_header_for_name (const char *name); +extern const char *get_cp_stdlib_header_for_name (const char *name); + +/* 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); + ~suggest_missing_header (); + + private: + const char *m_name_str; + const char *m_header_hint; +}; + +#endif /* GCC_KNOWN_HEADERS_H */ diff --git a/gcc/c/ChangeLog b/gcc/c/ChangeLog index b60a8e70d48e..95b7718791aa 100644 --- a/gcc/c/ChangeLog +++ b/gcc/c/ChangeLog @@ -1,3 +1,13 @@ +2017-11-20 David Malcolm + + PR c/81404 + * c-decl.c: Include "c-family/known-headers.h". + (get_c_name_hint): Rename to get_stdlib_header_for_name and move + to known-headers.cc. + (class suggest_missing_header): Move to known-header.h. + (lookup_name_fuzzy): Call get_c_stdlib_header_for_name rather + than get_c_name_hint. + 2017-11-20 David Malcolm * c-decl.c (get_c_name_hint): New function. diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index ae45e9a88f87..e0a4dd1661af 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -56,6 +56,7 @@ along with GCC; see the file COPYING3. If not see #include "gcc-rich-location.h" #include "asan.h" #include "c-family/name-hint.h" +#include "c-family/known-headers.h" /* In grokdeclarator, distinguish syntactic contexts of declarators. */ enum decl_context @@ -3992,83 +3993,6 @@ 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", ""}, - - /* . */ - {"va_list", ""}, - - /* . */ - {"NULL", ""}, - {"ptrdiff_t", ""}, - {"wchar_t", ""}, - {"size_t", ""}, - - /* . */ - {"BUFSIZ", ""}, - {"EOF", ""}, - {"FILE", ""}, - {"FILENAME_MAX", ""}, - {"fpos_t", ""}, - {"stderr", ""}, - {"stdin", ""}, - {"stdout", ""} - }; - 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. @@ -4094,7 +4018,9 @@ lookup_name_fuzzy (tree name, enum lookup_name_fuzzy_kind kind, location_t loc) /* 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)); + const char *header_hint + = get_c_stdlib_header_for_name (IDENTIFIER_POINTER (name)); + if (header_hint) return name_hint (NULL, new suggest_missing_header (loc, diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 908b48ef09bc..56d466dfb4c5 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2017-11-20 David Malcolm + + PR c/81404 + * name-lookup.c: Include "c-family/known-headers.h" + (lookup_name_fuzzy): Call get_cp_stdlib_header_for_name and + potentially return a new suggest_missing_header hint. + 2017-11-20 David Malcolm PR c++/72786 diff --git a/gcc/cp/name-lookup.c b/gcc/cp/name-lookup.c index fc317b175fb0..7c363b0f935e 100644 --- a/gcc/cp/name-lookup.c +++ b/gcc/cp/name-lookup.c @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see #include "spellcheck-tree.h" #include "parser.h" #include "c-family/name-hint.h" +#include "c-family/known-headers.h" static cxx_binding *cxx_binding_make (tree value, tree type); static cp_binding_level *innermost_nonclass_level (void); @@ -5682,6 +5683,16 @@ 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_cp_stdlib_header_for_name (IDENTIFIER_POINTER (name)); + if (header_hint) + return name_hint (NULL, + new suggest_missing_header (loc, + IDENTIFIER_POINTER (name), + header_hint)); + best_match bm (name); cp_binding_level *lvl; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d1911dff6c90..8bd2101486a8 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2017-11-20 David Malcolm + + PR c/81404 + * g++.dg/spellcheck-stdlib.C: New. + * gcc.dg/spellcheck-stdlib.c (test_INT_MAX): New. + 2017-11-20 David Malcolm * gcc.dg/spellcheck-stdlib.c: New test case. diff --git a/gcc/testsuite/g++.dg/spellcheck-stdlib.C b/gcc/testsuite/g++.dg/spellcheck-stdlib.C new file mode 100644 index 000000000000..6e6ab1dfb0ce --- /dev/null +++ b/gcc/testsuite/g++.dg/spellcheck-stdlib.C @@ -0,0 +1,84 @@ +/* Missing . */ + +void *ptr = NULL; // { dg-error "'NULL' was not declared" } +// { dg-message "'NULL' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + +ptrdiff_t pd; // { dg-error "'ptrdiff_t' does not name a type" } +// { dg-message "'ptrdiff_t' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + +size_t sz; // { dg-error "'size_t' does not name a type" } +// { dg-message "'size_t' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + +/* Missing . */ + +void test_cstdio (void) +{ + FILE *f; // { dg-error "'FILE' was not declared in this scope" } + // { dg-message "'FILE' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + // { dg-error "'f' was not declared in this scope" "" { target *-*-* } .-2 } + // { dg-bogus "suggested alternative: 'if'" "PR c++/80567" { xfail *-*-* } .-3 } + + char buf[BUFSIZ]; // { dg-error "'BUFSIZ' was not declared" } + // { dg-message "'BUFSIZ' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + + char buf2[FILENAME_MAX]; // { dg-error "'FILENAME_MAX' was not declared" } + // { dg-message "'FILENAME_MAX' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + + stderr; // { dg-error "'stderr' was not declared" } + // { dg-message "'stderr' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + + stdin; // { dg-error "'stdin' was not declared" } + // { dg-message "'stdin' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + + stdout; // { dg-error "'stdout' was not declared" } + // { dg-message "'stdout' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } + + EOF; // { dg-error "'EOF' was not declared" } + // { dg-message "'EOF' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } +} + +/* Missing . */ + +int test_cerrno (void) +{ + return errno; // { dg-error "'errno' was not declared" } + // { dg-message "'errno' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } +} + +/* Missing . */ + +void test_cstdarg (void) +{ + va_list ap; // { dg-error "'va_list'" } + // { dg-message "'va_list' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } +} + +/* Missing . */ +int test_INT_MAX (void) +{ + return INT_MAX; // { dg-line INT_MAX_line } + // { dg-error "'INT_MAX' was not declared" "" { target *-*-* } INT_MAX_line } + // { dg-bogus "__INT_MAX__" "" { target *-*-* } INT_MAX_line } + // { dg-message "'INT_MAX' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } INT_MAX_line } +} + +/* Verify that we don't offer suggestions to stdlib globals names when + there's an explicit namespace. */ + +namespace some_ns {} + +int not_within_namespace (void) +{ + return some_ns::stdout; // { dg-error "'stdout' is not a member of 'some_ns'" } + // { dg-bogus "is defined in header" "" { target *-*-* } .-1 } +} + +/* Similarly for when there's an explicit class scope. */ + +class some_class {}; + +int not_within_class (void) +{ + return some_class::stdout; // { dg-error "'stdout' is not a member of 'some_class'" } + // { dg-bogus "is defined in header" "" { target *-*-* } .-1 } +} diff --git a/gcc/testsuite/gcc.dg/spellcheck-stdlib.c b/gcc/testsuite/gcc.dg/spellcheck-stdlib.c index 85a21c384008..7474c9a5b03a 100644 --- a/gcc/testsuite/gcc.dg/spellcheck-stdlib.c +++ b/gcc/testsuite/gcc.dg/spellcheck-stdlib.c @@ -53,3 +53,12 @@ void test_stdarg_h (void) va_list ap; /* { dg-error "unknown type name 'va_list'" } */ /* { dg-message "'va_list' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } .-1 } */ } + +/* Missing . */ +int test_INT_MAX (void) +{ + return INT_MAX; /* { dg-line INT_MAX_line } */ + /* { dg-error "'INT_MAX' undeclared" "" { target *-*-* } INT_MAX_line } */ + /* { dg-bogus "__INT_MAX__" "" { target *-*-* } INT_MAX_line } */ + /* { dg-message "'INT_MAX' is defined in header ''; did you forget to '#include '?" "" { target *-*-* } INT_MAX_line } */ +}