+2017-11-20 David Malcolm <dmalcolm@redhat.com>
+
+ PR c/81404
+ * Makefile.in (C_COMMON_OBJS): Add c-family/known-headers.o.
+
2017-11-20 David Malcolm <dmalcolm@redhat.com>
PR c++/72786
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
+2017-11-20 David Malcolm <dmalcolm@redhat.com>
+
+ 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
+ <limits.h>, <stdint.h>, and <wchar.h>.
+ * known-headers.h: Likewise.
+
2017-11-20 David Malcolm <dmalcolm@redhat.com>
* c-common.h (enum lookup_name_fuzzy_kind): Move to name-hint.h.
--- /dev/null
+/* 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
+<http://www.gnu.org/licenses/>. */
+
+#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[] = {
+ /* <errno.h> and <cerrno>. */
+ {"errno", {"<errno.h>", "<cerrno>"} },
+
+ /* <limits.h> and <climits>. */
+ {"CHAR_BIT", {"<limits.h>", "<climits>"} },
+ {"CHAR_MAX", {"<limits.h>", "<climits>"} },
+ {"CHAR_MIN", {"<limits.h>", "<climits>"} },
+ {"INT_MAX", {"<limits.h>", "<climits>"} },
+ {"INT_MIN", {"<limits.h>", "<climits>"} },
+ {"LLONG_MAX", {"<limits.h>", "<climits>"} },
+ {"LLONG_MIN", {"<limits.h>", "<climits>"} },
+ {"LONG_MAX", {"<limits.h>", "<climits>"} },
+ {"LONG_MIN", {"<limits.h>", "<climits>"} },
+ {"MB_LEN_MAX", {"<limits.h>", "<climits>"} },
+ {"SCHAR_MAX", {"<limits.h>", "<climits>"} },
+ {"SCHAR_MIN", {"<limits.h>", "<climits>"} },
+ {"SHRT_MAX", {"<limits.h>", "<climits>"} },
+ {"SHRT_MIN", {"<limits.h>", "<climits>"} },
+ {"UCHAR_MAX", {"<limits.h>", "<climits>"} },
+ {"UINT_MAX", {"<limits.h>", "<climits>"} },
+ {"ULLONG_MAX", {"<limits.h>", "<climits>"} },
+ {"ULONG_MAX", {"<limits.h>", "<climits>"} },
+ {"USHRT_MAX", {"<limits.h>", "<climits>"} },
+
+ /* <stdarg.h> and <cstdarg>. */
+ {"va_list", {"<stdarg.h>", "<cstdarg>"} },
+
+ /* <stddef.h> and <cstddef>. */
+ {"NULL", {"<stddef.h>", "<cstddef>"} },
+ {"nullptr_t", {NULL, "<cstddef>"} },
+ {"offsetof", {"<stddef.h>", "<cstddef>"} },
+ {"ptrdiff_t", {"<stddef.h>", "<cstddef>"} },
+ {"size_t", {"<stddef.h>", "<cstddef>"} },
+ {"wchar_t", {"<stddef.h>", NULL /* a keyword in C++ */} },
+
+ /* <stdio.h>. */
+ {"BUFSIZ", {"<stdio.h>", "<cstdio>"} },
+ {"EOF", {"<stdio.h>", "<cstdio>"} },
+ {"FILE", {"<stdio.h>", "<cstdio>"} },
+ {"FILENAME_MAX", {"<stdio.h>", "<cstdio>"} },
+ {"fpos_t", {"<stdio.h>", "<cstdio>"} },
+ {"stderr", {"<stdio.h>", "<cstdio>"} },
+ {"stdin", {"<stdio.h>", "<cstdio>"} },
+ {"stdout", {"<stdio.h>", "<cstdio>"} },
+
+ /* <stdint.h>. */
+ {"PTRDIFF_MAX", {"<stdint.h>", "<cstdint>"} },
+ {"PTRDIFF_MIN", {"<stdint.h>", "<cstdint>"} },
+ {"SIG_ATOMIC_MAX", {"<stdint.h>", "<cstdint>"} },
+ {"SIG_ATOMIC_MIN", {"<stdint.h>", "<cstdint>"} },
+ {"SIZE_MAX", {"<stdint.h>", "<cstdint>"} },
+ {"WINT_MAX", {"<stdint.h>", "<cstdint>"} },
+ {"WINT_MIN", {"<stdint.h>", "<cstdint>"} },
+
+ /* <wchar.h>. */
+ {"WCHAR_MAX", {"<wchar.h>", "<cwchar>"} },
+ {"WCHAR_MIN", {"<wchar.h>", "<cwchar>"} }
+ };
+ 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);
+}
--- /dev/null
+/* 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
+<http://www.gnu.org/licenses/>. */
+
+#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 */
+2017-11-20 David Malcolm <dmalcolm@redhat.com>
+
+ 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 <dmalcolm@redhat.com>
* c-decl.c (get_c_name_hint): New function.
#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
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.
/* 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,
+2017-11-20 David Malcolm <dmalcolm@redhat.com>
+
+ 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 <dmalcolm@redhat.com>
PR c++/72786
#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);
{
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 <tree, const char *> bm (name);
cp_binding_level *lvl;
+2017-11-20 David Malcolm <dmalcolm@redhat.com>
+
+ PR c/81404
+ * g++.dg/spellcheck-stdlib.C: New.
+ * gcc.dg/spellcheck-stdlib.c (test_INT_MAX): New.
+
2017-11-20 David Malcolm <dmalcolm@redhat.com>
* gcc.dg/spellcheck-stdlib.c: New test case.
--- /dev/null
+/* Missing <cstddef>. */
+
+void *ptr = NULL; // { dg-error "'NULL' was not declared" }
+// { dg-message "'NULL' is defined in header '<cstddef>'; did you forget to '#include <cstddef>'?" "" { target *-*-* } .-1 }
+
+ptrdiff_t pd; // { dg-error "'ptrdiff_t' does not name a type" }
+// { dg-message "'ptrdiff_t' is defined in header '<cstddef>'; did you forget to '#include <cstddef>'?" "" { target *-*-* } .-1 }
+
+size_t sz; // { dg-error "'size_t' does not name a type" }
+// { dg-message "'size_t' is defined in header '<cstddef>'; did you forget to '#include <cstddef>'?" "" { target *-*-* } .-1 }
+
+/* Missing <cstdio>. */
+
+void test_cstdio (void)
+{
+ FILE *f; // { dg-error "'FILE' was not declared in this scope" }
+ // { dg-message "'FILE' is defined in header '<cstdio>'; did you forget to '#include <cstdio>'?" "" { 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 '<cstdio>'; did you forget to '#include <cstdio>'?" "" { target *-*-* } .-1 }
+
+ char buf2[FILENAME_MAX]; // { dg-error "'FILENAME_MAX' was not declared" }
+ // { dg-message "'FILENAME_MAX' is defined in header '<cstdio>'; did you forget to '#include <cstdio>'?" "" { target *-*-* } .-1 }
+
+ stderr; // { dg-error "'stderr' was not declared" }
+ // { dg-message "'stderr' is defined in header '<cstdio>'; did you forget to '#include <cstdio>'?" "" { target *-*-* } .-1 }
+
+ stdin; // { dg-error "'stdin' was not declared" }
+ // { dg-message "'stdin' is defined in header '<cstdio>'; did you forget to '#include <cstdio>'?" "" { target *-*-* } .-1 }
+
+ stdout; // { dg-error "'stdout' was not declared" }
+ // { dg-message "'stdout' is defined in header '<cstdio>'; did you forget to '#include <cstdio>'?" "" { target *-*-* } .-1 }
+
+ EOF; // { dg-error "'EOF' was not declared" }
+ // { dg-message "'EOF' is defined in header '<cstdio>'; did you forget to '#include <cstdio>'?" "" { target *-*-* } .-1 }
+}
+
+/* Missing <cerrno>. */
+
+int test_cerrno (void)
+{
+ return errno; // { dg-error "'errno' was not declared" }
+ // { dg-message "'errno' is defined in header '<cerrno>'; did you forget to '#include <cerrno>'?" "" { target *-*-* } .-1 }
+}
+
+/* Missing <cstdarg>. */
+
+void test_cstdarg (void)
+{
+ va_list ap; // { dg-error "'va_list'" }
+ // { dg-message "'va_list' is defined in header '<cstdarg>'; did you forget to '#include <cstdarg>'?" "" { target *-*-* } .-1 }
+}
+
+/* Missing <climits>. */
+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 '<climits>'; did you forget to '#include <climits>'?" "" { 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 }
+}
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 } */
}
+
+/* Missing <limits.h>. */
+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 '<limits.h>'; did you forget to '#include <limits.h>'?" "" { target *-*-* } INT_MAX_line } */
+}