1 | /* Support for suggestions about missing #include directives. |
2 | Copyright (C) 2017-2023 Free Software Foundation, Inc. |
3 | |
4 | This file is part of GCC. |
5 | |
6 | GCC is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free |
8 | Software Foundation; either version 3, or (at your option) any later |
9 | version. |
10 | |
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
14 | for more details. |
15 | |
16 | You should have received a copy of the GNU General Public License |
17 | along with GCC; see the file COPYING3. If not see |
18 | <http://www.gnu.org/licenses/>. */ |
19 | |
20 | #include "config.h" |
21 | #define INCLUDE_MEMORY |
22 | #include "system.h" |
23 | #include "coretypes.h" |
24 | #include "c-family/c-common.h" |
25 | #include "c-family/name-hint.h" |
26 | #include "c-family/known-headers.h" |
27 | #include "gcc-rich-location.h" |
28 | |
29 | /* An enum for distinguishing between the C and C++ stdlibs. */ |
30 | |
31 | enum stdlib |
32 | { |
33 | STDLIB_C, |
34 | STDLIB_CPLUSPLUS, |
35 | |
36 | NUM_STDLIBS |
37 | }; |
38 | |
39 | /* A struct for associating names in a standard library with the header |
40 | that should be included to locate them, for each of the C and C++ stdlibs |
41 | (or NULL, for names that aren't in a header for a particular stdlib). */ |
42 | |
43 | struct stdlib_hint |
44 | { |
45 | const char *name; |
46 | const char *[NUM_STDLIBS]; |
47 | }; |
48 | |
49 | /* Given non-NULL NAME, return the header name defining it (as literal |
50 | string) within either the standard library (with '<' and '>'), or |
51 | NULL. |
52 | |
53 | Only handle string macros, so that this can be used for |
54 | get_stdlib_header_for_name and |
55 | get_c_stdlib_header_for_string_macro_name. */ |
56 | |
57 | static const char * |
58 | get_string_macro_hint (const char *name, enum stdlib lib) |
59 | { |
60 | /* <inttypes.h> and <cinttypes>. */ |
61 | static const char *c99_cxx11_macros[] = |
62 | { "PRId8" , "PRId16" , "PRId32" , "PRId64" , |
63 | "PRIi8" , "PRIi16" , "PRIi32" , "PRIi64" , |
64 | "PRIo8" , "PRIo16" , "PRIo32" , "PRIo64" , |
65 | "PRIu8" , "PRIu16" , "PRIu32" , "PRIu64" , |
66 | "PRIx8" , "PRIx16" , "PRIx32" , "PRIx64" , |
67 | "PRIX8" , "PRIX16" , "PRIX32" , "PRIX64" , |
68 | |
69 | "PRIdPTR" , "PRIiPTR" , "PRIoPTR" , "PRIuPTR" , "PRIxPTR" , "PRIXPTR" , |
70 | |
71 | "SCNd8" , "SCNd16" , "SCNd32" , "SCNd64" , |
72 | "SCNi8" , "SCNi16" , "SCNi32" , "SCNi64" , |
73 | "SCNo8" , "SCNo16" , "SCNo32" , "SCNo64" , |
74 | "SCNu8" , "SCNu16" , "SCNu32" , "SCNu64" , |
75 | "SCNx8" , "SCNx16" , "SCNx32" , "SCNx64" , |
76 | |
77 | "SCNdPTR" , "SCNiPTR" , "SCNoPTR" , "SCNuPTR" , "SCNxPTR" }; |
78 | |
79 | if ((lib == STDLIB_C && flag_isoc99) |
80 | || (lib == STDLIB_CPLUSPLUS && cxx_dialect >= cxx11 )) |
81 | { |
82 | const size_t num_c99_cxx11_macros = ARRAY_SIZE (c99_cxx11_macros); |
83 | for (size_t i = 0; i < num_c99_cxx11_macros; i++) |
84 | if (strcmp (s1: name, s2: c99_cxx11_macros[i]) == 0) |
85 | return lib == STDLIB_C ? "<inttypes.h>" : "<cinttypes>" ; |
86 | } |
87 | |
88 | return NULL; |
89 | } |
90 | |
91 | /* Given non-NULL NAME, return the header name defining it within either |
92 | the standard library (with '<' and '>'), or NULL. |
93 | Only handles a subset of the most common names within the stdlibs. */ |
94 | |
95 | static const char * |
96 | (const char *name, enum stdlib lib) |
97 | { |
98 | gcc_assert (name); |
99 | gcc_assert (lib < NUM_STDLIBS); |
100 | |
101 | static const stdlib_hint hints[] = { |
102 | /* <assert.h> and <cassert>. */ |
103 | {.name: "assert" , .header: {"<assert.h>" , "<cassert>" } }, |
104 | |
105 | /* <errno.h> and <cerrno>. */ |
106 | {.name: "errno" , .header: {"<errno.h>" , "<cerrno>" } }, |
107 | |
108 | /* <limits.h> and <climits>. */ |
109 | {.name: "CHAR_BIT" , .header: {"<limits.h>" , "<climits>" } }, |
110 | {.name: "CHAR_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
111 | {.name: "CHAR_MIN" , .header: {"<limits.h>" , "<climits>" } }, |
112 | {.name: "INT_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
113 | {.name: "INT_MIN" , .header: {"<limits.h>" , "<climits>" } }, |
114 | {.name: "LLONG_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
115 | {.name: "LLONG_MIN" , .header: {"<limits.h>" , "<climits>" } }, |
116 | {.name: "LONG_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
117 | {.name: "LONG_MIN" , .header: {"<limits.h>" , "<climits>" } }, |
118 | {.name: "MB_LEN_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
119 | {.name: "SCHAR_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
120 | {.name: "SCHAR_MIN" , .header: {"<limits.h>" , "<climits>" } }, |
121 | {.name: "SHRT_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
122 | {.name: "SHRT_MIN" , .header: {"<limits.h>" , "<climits>" } }, |
123 | {.name: "UCHAR_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
124 | {.name: "UINT_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
125 | {.name: "ULLONG_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
126 | {.name: "ULONG_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
127 | {.name: "USHRT_MAX" , .header: {"<limits.h>" , "<climits>" } }, |
128 | |
129 | /* <float.h> and <cfloat>. */ |
130 | {.name: "DBL_MAX" , .header: {"<float.h>" , "<cfloat>" } }, |
131 | {.name: "DBL_MIN" , .header: {"<float.h>" , "<cfloat>" } }, |
132 | {.name: "FLT_MAX" , .header: {"<float.h>" , "<cfloat>" } }, |
133 | {.name: "FLT_MIN" , .header: {"<float.h>" , "<cfloat>" } }, |
134 | {.name: "LDBL_MAX" , .header: {"<float.h>" , "<cfloat>" } }, |
135 | {.name: "LDBL_MIN" , .header: {"<float.h>" , "<cfloat>" } }, |
136 | |
137 | /* <stdarg.h> and <cstdarg>. */ |
138 | {.name: "va_list" , .header: {"<stdarg.h>" , "<cstdarg>" } }, |
139 | |
140 | /* <stddef.h> and <cstddef>. */ |
141 | {.name: "NULL" , .header: {"<stddef.h>" , "<cstddef>" } }, |
142 | {.name: "nullptr_t" , .header: {NULL, "<cstddef>" } }, |
143 | {.name: "offsetof" , .header: {"<stddef.h>" , "<cstddef>" } }, |
144 | {.name: "ptrdiff_t" , .header: {"<stddef.h>" , "<cstddef>" } }, |
145 | {.name: "size_t" , .header: {"<stddef.h>" , "<cstddef>" } }, |
146 | {.name: "wchar_t" , .header: {"<stddef.h>" , NULL /* a keyword in C++ */} }, |
147 | |
148 | /* <stdio.h> and <cstdio>. */ |
149 | {.name: "BUFSIZ" , .header: {"<stdio.h>" , "<cstdio>" } }, |
150 | {.name: "EOF" , .header: {"<stdio.h>" , "<cstdio>" } }, |
151 | {.name: "FILE" , .header: {"<stdio.h>" , "<cstdio>" } }, |
152 | {.name: "FILENAME_MAX" , .header: {"<stdio.h>" , "<cstdio>" } }, |
153 | {.name: "fopen" , .header: {"<stdio.h>" , "<cstdio>" } }, |
154 | {.name: "fpos_t" , .header: {"<stdio.h>" , "<cstdio>" } }, |
155 | {.name: "getchar" , .header: {"<stdio.h>" , "<cstdio>" } }, |
156 | {.name: "printf" , .header: {"<stdio.h>" , "<cstdio>" } }, |
157 | {.name: "snprintf" , .header: {"<stdio.h>" , "<cstdio>" } }, |
158 | {.name: "sprintf" , .header: {"<stdio.h>" , "<cstdio>" } }, |
159 | {.name: "stderr" , .header: {"<stdio.h>" , "<cstdio>" } }, |
160 | {.name: "stdin" , .header: {"<stdio.h>" , "<cstdio>" } }, |
161 | {.name: "stdout" , .header: {"<stdio.h>" , "<cstdio>" } }, |
162 | |
163 | /* <stdlib.h> and <cstdlib>. */ |
164 | {.name: "EXIT_FAILURE" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
165 | {.name: "EXIT_SUCCESS" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
166 | {.name: "abort" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
167 | {.name: "atexit" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
168 | {.name: "calloc" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
169 | {.name: "exit" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
170 | {.name: "free" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
171 | {.name: "getenv" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
172 | {.name: "malloc" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
173 | {.name: "realloc" , .header: {"<stdlib.h>" , "<cstdlib>" } }, |
174 | |
175 | /* <string.h> and <cstring>. */ |
176 | {.name: "memchr" , .header: {"<string.h>" , "<cstring>" } }, |
177 | {.name: "memcmp" , .header: {"<string.h>" , "<cstring>" } }, |
178 | {.name: "memcpy" , .header: {"<string.h>" , "<cstring>" } }, |
179 | {.name: "memmove" , .header: {"<string.h>" , "<cstring>" } }, |
180 | {.name: "memset" , .header: {"<string.h>" , "<cstring>" } }, |
181 | {.name: "strcat" , .header: {"<string.h>" , "<cstring>" } }, |
182 | {.name: "strchr" , .header: {"<string.h>" , "<cstring>" } }, |
183 | {.name: "strcmp" , .header: {"<string.h>" , "<cstring>" } }, |
184 | {.name: "strcpy" , .header: {"<string.h>" , "<cstring>" } }, |
185 | {.name: "strlen" , .header: {"<string.h>" , "<cstring>" } }, |
186 | {.name: "strncat" , .header: {"<string.h>" , "<cstring>" } }, |
187 | {.name: "strncmp" , .header: {"<string.h>" , "<cstring>" } }, |
188 | {.name: "strncpy" , .header: {"<string.h>" , "<cstring>" } }, |
189 | {.name: "strrchr" , .header: {"<string.h>" , "<cstring>" } }, |
190 | {.name: "strspn" , .header: {"<string.h>" , "<cstring>" } }, |
191 | {.name: "strstr" , .header: {"<string.h>" , "<cstring>" } }, |
192 | |
193 | /* <stdint.h>. */ |
194 | {.name: "PTRDIFF_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
195 | {.name: "PTRDIFF_MIN" , .header: {"<stdint.h>" , "<cstdint>" } }, |
196 | {.name: "SIG_ATOMIC_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
197 | {.name: "SIG_ATOMIC_MIN" , .header: {"<stdint.h>" , "<cstdint>" } }, |
198 | {.name: "SIZE_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
199 | {.name: "WINT_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
200 | {.name: "WINT_MIN" , .header: {"<stdint.h>" , "<cstdint>" } }, |
201 | |
202 | /* <time.h>. */ |
203 | {.name: "asctime" , .header: {"<time.h>" , "<ctime>" } }, |
204 | {.name: "clock" , .header: {"<time.h>" , "<ctime>" } }, |
205 | {.name: "clock_t" , .header: {"<time.h>" , "<ctime>" } }, |
206 | {.name: "ctime" , .header: {"<time.h>" , "<ctime>" } }, |
207 | {.name: "difftime" , .header: {"<time.h>" , "<ctime>" } }, |
208 | {.name: "gmtime" , .header: {"<time.h>" , "<ctime>" } }, |
209 | {.name: "localtime" , .header: {"<time.h>" , "<ctime>" } }, |
210 | {.name: "mktime" , .header: {"<time.h>" , "<ctime>" } }, |
211 | {.name: "strftime" , .header: {"<time.h>" , "<ctime>" } }, |
212 | {.name: "time" , .header: {"<time.h>" , "<ctime>" } }, |
213 | {.name: "time_t" , .header: {"<time.h>" , "<ctime>" } }, |
214 | {.name: "tm" , .header: {"<time.h>" , "<ctime>" } }, |
215 | |
216 | /* <wchar.h>. */ |
217 | {.name: "WCHAR_MAX" , .header: {"<wchar.h>" , "<cwchar>" } }, |
218 | {.name: "WCHAR_MIN" , .header: {"<wchar.h>" , "<cwchar>" } } |
219 | }; |
220 | const size_t num_hints = ARRAY_SIZE (hints); |
221 | for (size_t i = 0; i < num_hints; i++) |
222 | if (strcmp (s1: name, s2: hints[i].name) == 0) |
223 | return hints[i].header[lib]; |
224 | |
225 | static const stdlib_hint c99_cxx11_hints[] = { |
226 | /* <stdbool.h>. Defined natively in C++. */ |
227 | {.name: "bool" , .header: {"<stdbool.h>" , NULL} }, |
228 | {.name: "true" , .header: {"<stdbool.h>" , NULL} }, |
229 | {.name: "false" , .header: {"<stdbool.h>" , NULL} }, |
230 | |
231 | /* <stdint.h> and <cstdint>. */ |
232 | {.name: "int8_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
233 | {.name: "uint8_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
234 | {.name: "int16_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
235 | {.name: "uint16_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
236 | {.name: "int32_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
237 | {.name: "uint32_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
238 | {.name: "int64_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
239 | {.name: "uint64_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
240 | {.name: "intptr_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
241 | {.name: "uintptr_t" , .header: {"<stdint.h>" , "<cstdint>" } }, |
242 | {.name: "INT8_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
243 | {.name: "INT16_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
244 | {.name: "INT32_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
245 | {.name: "INT64_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
246 | {.name: "UINT8_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
247 | {.name: "UINT16_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
248 | {.name: "UINT32_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
249 | {.name: "UINT64_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
250 | {.name: "INTPTR_MAX" , .header: {"<stdint.h>" , "<cstdint>" } }, |
251 | {.name: "UINTPTR_MAX" , .header: {"<stdint.h>" , "<cstdint>" } } |
252 | }; |
253 | |
254 | const size_t num_c99_cxx11_hints = sizeof (c99_cxx11_hints) |
255 | / sizeof (c99_cxx11_hints[0]); |
256 | if ((lib == STDLIB_C && flag_isoc99) |
257 | || (lib == STDLIB_CPLUSPLUS && cxx_dialect >= cxx11 )) |
258 | for (size_t i = 0; i < num_c99_cxx11_hints; i++) |
259 | if (strcmp (s1: name, s2: c99_cxx11_hints[i].name) == 0) |
260 | return c99_cxx11_hints[i].header[lib]; |
261 | |
262 | return get_string_macro_hint (name, lib); |
263 | } |
264 | |
265 | /* Given non-NULL NAME, return the header name defining it within the C |
266 | standard library (with '<' and '>'), or NULL. */ |
267 | |
268 | const char * |
269 | (const char *name) |
270 | { |
271 | return get_stdlib_header_for_name (name, lib: STDLIB_C); |
272 | } |
273 | |
274 | /* Given non-NULL NAME, return the header name defining it within the C++ |
275 | standard library (with '<' and '>'), or NULL. */ |
276 | |
277 | const char * |
278 | (const char *name) |
279 | { |
280 | return get_stdlib_header_for_name (name, lib: STDLIB_CPLUSPLUS); |
281 | } |
282 | |
283 | /* Given non-NULL NAME, return the header name defining a string macro |
284 | within the C standard library (with '<' and '>'), or NULL. */ |
285 | const char * |
286 | (const char *name) |
287 | { |
288 | return get_string_macro_hint (name, lib: STDLIB_C); |
289 | } |
290 | |
291 | /* Given non-NULL NAME, return the header name defining a string macro |
292 | within the C++ standard library (with '<' and '>'), or NULL. */ |
293 | const char * |
294 | (const char *name) |
295 | { |
296 | return get_string_macro_hint (name, lib: STDLIB_CPLUSPLUS); |
297 | } |
298 | |
299 | /* Implementation of class suggest_missing_header. */ |
300 | |
301 | /* suggest_missing_header's ctor. */ |
302 | |
303 | suggest_missing_header:: (location_t loc, |
304 | const char *name, |
305 | const char *) |
306 | : deferred_diagnostic (loc), m_name_str (name), m_header_hint (header_hint) |
307 | { |
308 | gcc_assert (name); |
309 | gcc_assert (header_hint); |
310 | } |
311 | |
312 | /* suggest_missing_header's dtor. */ |
313 | |
314 | suggest_missing_header:: () |
315 | { |
316 | if (is_suppressed_p ()) |
317 | return; |
318 | |
319 | gcc_rich_location richloc (get_location ()); |
320 | maybe_add_include_fixit (&richloc, m_header_hint, true); |
321 | inform (&richloc, |
322 | "%qs is defined in header %qs;" |
323 | " this is probably fixable by adding %<#include %s%>" , |
324 | m_name_str, m_header_hint, m_header_hint); |
325 | } |
326 | |