@@ -1190,7 +1190,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
new file mode 100644
@@ -0,0 +1,68 @@
+/* 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"
+#include "system.h"
+#include "coretypes.h"
+#include "c-family/c-common.h"
+#include "c-family/known-headers.h"
+#include "gcc-rich-location.h"
+
+/* Given NAME, attempt to locate a header that would declare it,
+ or return NULL if we don't know of such a header. */
+
+const char *
+known_headers::get_header_for_name (const char *name) const
+{
+ gcc_assert (name);
+
+ for (size_t i = 0; i < m_num_hints; i++)
+ if (0 == strcmp (name, m_hints[i].name))
+ return m_hints[i].header;
+
+ return NULL;
+}
+
+/* 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_at_rich_loc (&richloc,
+ "%qs is defined in header %qs;"
+ " did you forget to %<#include %s%>?",
+ m_name_str, m_header_hint, m_header_hint);
+}
new file mode 100644
@@ -0,0 +1,63 @@
+/* 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
+
+/* A struct for associating names in a standard library (perhaps in
+ some namespace) with the header that should be included to locate them. */
+
+struct header_hint
+{
+ const char *name;
+ const char *header;
+};
+
+/* A class for looking up unknown names, to attempt to locate the header
+ that should have been #include-d to declare it. */
+
+class known_headers
+{
+ public:
+ known_headers (const header_hint *hints, size_t num_hints)
+ : m_hints (hints), m_num_hints (num_hints) {}
+
+ const char *get_header_for_name (const char *name) const;
+
+ private:
+ const header_hint *const m_hints;
+ const size_t m_num_hints;
+};
+
+/* 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 */
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3. If not see
#include "spellcheck-tree.h"
#include "gcc-rich-location.h"
#include "asan.h"
+#include "c-family/known-headers.h"
/* In grokdeclarator, distinguish syntactic contexts of declarators. */
enum decl_context
@@ -3991,15 +3992,31 @@ lookup_name_in_scope (tree name, struct c_scope *scope)
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[] = {
+ static const header_hint hints[] = {
/* <errno.h>. */
{"errno", "<errno.h>"},
+ /* <limits.h>. */
+ {"CHAR_BIT", "<limits.h>"},
+ {"CHAR_MAX", "<limits.h>"},
+ {"CHAR_MIN", "<limits.h>"},
+ {"INT_MAX", "<limits.h>"},
+ {"INT_MIN", "<limits.h>"},
+ {"LLONG_MAX", "<limits.h>"},
+ {"LLONG_MIN", "<limits.h>"},
+ {"LONG_MAX", "<limits.h>"},
+ {"LONG_MIN", "<limits.h>"},
+ {"MB_LEN_MAX", "<limits.h>"},
+ {"SCHAR_MAX", "<limits.h>"},
+ {"SCHAR_MIN", "<limits.h>"},
+ {"SHRT_MAX", "<limits.h>"},
+ {"SHRT_MIN", "<limits.h>"},
+ {"UCHAR_MAX", "<limits.h>"},
+ {"UINT_MAX", "<limits.h>"},
+ {"ULLONG_MAX", "<limits.h>"},
+ {"ULONG_MAX", "<limits.h>"},
+ {"USHRT_MAX", "<limits.h>"},
+
/* <stdarg.h>. */
{"va_list", "<stdarg.h>"},
@@ -4017,49 +4034,26 @@ get_c_name_hint (const char *name)
{"fpos_t", "<stdio.h>"},
{"stderr", "<stdio.h>"},
{"stdin", "<stdio.h>"},
- {"stdout", "<stdio.h>"}
+ {"stdout", "<stdio.h>"},
+
+ /* <stdint.h>. */
+ {"PTRDIFF_MAX", "<stdint.h>"},
+ {"PTRDIFF_MIN", "<stdint.h>"},
+ {"SIG_ATOMIC_MAX", "<stdint.h>"},
+ {"SIG_ATOMIC_MIN", "<stdint.h>"},
+ {"SIZE_MAX", "<stdint.h>"},
+
+ /* <wchar.h>. */
+ {"WCHAR_MAX", "<wchar.h>"},
+ {"WCHAR_MIN", "<wchar.h>"},
+ {"WINT_MAX", "<wchar.h>"},
+ {"WINT_MIN", "<wchar.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;
+ known_headers kh (hints, num_hints);
+ return kh.get_header_for_name (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)
- : 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_at_rich_loc (&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.
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "gcc-rich-location.h"
#include "spellcheck-tree.h"
#include "parser.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);
@@ -5392,12 +5393,7 @@ suggest_alternatives_for (location_t location, tree name,
static const char *
get_std_name_hint (const char *name)
{
- struct std_name_hint
- {
- const char *name;
- const char *header;
- };
- static const std_name_hint hints[] = {
+ static const header_hint hints[] = {
/* <array>. */
{"array", "<array>"}, // C++11
/* <deque>. */
@@ -5456,12 +5452,8 @@ get_std_name_hint (const char *name)
{"vector", "<vector>"},
};
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;
+ known_headers kh (hints, num_hints);
+ return kh.get_header_for_name (name);
}
/* If SCOPE is the "std" namespace, then suggest pertinent header
@@ -5660,6 +5652,70 @@ class macro_use_before_def : public deferred_diagnostic
cpp_hashnode *m_macro;
};
+/* Subroutine of lookup_name_fuzzy for handling unrecognized names
+ for some of the 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_stdlib_name_hint (const char *name)
+{
+ static const header_hint hints[] = {
+ /* <cerrno>. */
+ {"errno", "<cerrno>"},
+
+ /* <climits>. */
+ {"CHAR_BIT", "<climits>"},
+ {"CHAR_MAX", "<climits>"},
+ {"CHAR_MIN", "<climits>"},
+ {"INT_MAX", "<climits>"},
+ {"INT_MIN", "<climits>"},
+ {"LLONG_MAX", "<climits>"},
+ {"LLONG_MIN", "<climits>"},
+ {"LONG_MAX", "<climits>"},
+ {"LONG_MIN", "<climits>"},
+ {"MB_LEN_MAX", "<climits>"},
+ {"SCHAR_MAX", "<climits>"},
+ {"SCHAR_MIN", "<climits>"},
+ {"SHRT_MAX", "<climits>"},
+ {"SHRT_MIN", "<climits>"},
+ {"UCHAR_MAX", "<climits>"},
+ {"UINT_MAX", "<climits>"},
+ {"ULLONG_MAX", "<climits>"},
+ {"ULONG_MAX", "<climits>"},
+ {"USHRT_MAX", "<climits>"},
+
+ /* <cstdarg>. */
+ {"va_list", "<cstdarg>"},
+
+ /* <cstddef>. */
+ {"NULL", "<cstddef>"},
+ {"nullptr_t", "<cstddef>"},
+ {"offsetof", "<cstddef>"},
+ {"ptrdiff_t", "<cstddef>"},
+ {"size_t", "<cstddef>"},
+
+ /* <cstdio>. */
+ {"BUFSIZ", "<cstdio>"},
+ {"EOF", "<cstdio>"},
+ {"FILE", "<cstdio>"},
+ {"FILENAME_MAX", "<cstdio>"},
+ {"fpos_t", "<cstdio>"},
+ {"stderr", "<cstdio>"},
+ {"stdin", "<cstdio>"},
+ {"stdout", "<cstdio>"},
+
+ /* <cstdint>. */
+ {"PTRDIFF_MAX", "<cstdint>"},
+ {"PTRDIFF_MIN", "<cstdint>"},
+ {"SIG_ATOMIC_MAX", "<cstdint>"},
+ {"SIG_ATOMIC_MIN", "<cstdint>"},
+ {"SIZE_MAX", "<cstdint>"},
+ };
+ const size_t num_hints = sizeof (hints) / sizeof (hints[0]);
+ known_headers kh (hints, num_hints);
+ return kh.get_header_for_name (name);
+}
/* Search for near-matches for NAME within the current bindings, and within
macro names, returning the best match as a const char *, or NULL if
@@ -5672,6 +5728,15 @@ 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_stdlib_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, const char *> bm (name);
cp_binding_level *lvl;
new file mode 100644
@@ -0,0 +1,84 @@
+/* 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 }
+}
@@ -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 '<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 } */
+}