Branch data Line data Source code
1 : : // rust-diagnostics.cc -- GCC implementation of rust diagnostics interface.
2 : : // Copyright (C) 2016-2025 Free Software Foundation, Inc.
3 : : // Contributed by Than McIntosh, Google.
4 : :
5 : : // This file is part of GCC.
6 : :
7 : : // GCC is free software; you can redistribute it and/or modify it under
8 : : // the terms of the GNU General Public License as published by the Free
9 : : // Software Foundation; either version 3, or (at your option) any later
10 : : // version.
11 : :
12 : : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : // for more details.
16 : :
17 : : // You should have received a copy of the GNU General Public License
18 : : // along with GCC; see the file COPYING3. If not see
19 : : // <http://www.gnu.org/licenses/>.
20 : :
21 : : #include "rust-system.h"
22 : : #include "rust-diagnostics.h"
23 : :
24 : : #include "options.h"
25 : : #include "diagnostics/metadata.h"
26 : :
27 : : static std::string
28 : 11 : mformat_value ()
29 : : {
30 : 11 : return std::string (xstrerror (errno));
31 : : }
32 : :
33 : : // Rewrite a format string to expand any extensions not
34 : : // supported by sprintf(). See comments in rust-diagnostics.h
35 : : // for list of supported format specifiers.
36 : :
37 : : static std::string
38 : 4626 : expand_format (const char *fmt)
39 : : {
40 : 4626 : std::stringstream ss;
41 : 134769 : for (const char *c = fmt; *c; ++c)
42 : : {
43 : 130143 : if (*c != '%')
44 : : {
45 : 123734 : ss << *c;
46 : 123734 : continue;
47 : : }
48 : 6409 : c++;
49 : 6409 : switch (*c)
50 : : {
51 : 0 : case '\0':
52 : 0 : {
53 : : // malformed format string
54 : 0 : rust_unreachable ();
55 : : }
56 : 0 : case '%':
57 : 0 : {
58 : 0 : ss << "%";
59 : 0 : break;
60 : : }
61 : 11 : case 'm':
62 : 11 : {
63 : 22 : ss << mformat_value ();
64 : 11 : break;
65 : : }
66 : 268 : case '<':
67 : 268 : {
68 : 268 : ss << rust_open_quote ();
69 : 268 : break;
70 : : }
71 : 268 : case '>':
72 : 268 : {
73 : 268 : ss << rust_close_quote ();
74 : 268 : break;
75 : : }
76 : 5222 : case 'q':
77 : 5222 : {
78 : 5222 : ss << rust_open_quote ();
79 : 5222 : c++;
80 : 5222 : if (*c == 'm')
81 : : {
82 : 0 : ss << mformat_value ();
83 : : }
84 : : else
85 : : {
86 : 5222 : ss << "%" << *c;
87 : : }
88 : 5222 : ss << rust_close_quote ();
89 : 5222 : break;
90 : : }
91 : 640 : default:
92 : 640 : {
93 : 640 : ss << "%" << *c;
94 : : }
95 : : }
96 : : }
97 : 4626 : return ss.str ();
98 : 4626 : }
99 : :
100 : : // Expand message format specifiers, using a combination of
101 : : // expand_format above to handle extensions (ex: %m, %q) and vasprintf()
102 : : // to handle regular printf-style formatting. A pragma is being used here to
103 : : // suppress this warning:
104 : : //
105 : : // warning: function ‘std::__cxx11::string expand_message(const char*,
106 : : // __va_list_tag*)’ might be a candidate for ‘gnu_printf’ format attribute
107 : : // [-Wsuggest-attribute=format]
108 : : //
109 : : // What appears to be happening here is that the checker is deciding that
110 : : // because of the call to vasprintf() (which has attribute gnu_printf), the
111 : : // calling function must need to have attribute gnu_printf as well, even
112 : : // though there is already an attribute declaration for it.
113 : :
114 : : static std::string expand_message (const char *fmt, va_list ap)
115 : : RUST_ATTRIBUTE_GCC_DIAG (1, 0);
116 : :
117 : : #pragma GCC diagnostic push
118 : : #pragma GCC diagnostic ignored "-Wsuggest-attribute=format"
119 : :
120 : : static std::string
121 : 4626 : expand_message (const char *fmt, va_list ap)
122 : : {
123 : 4626 : char *mbuf = 0;
124 : 4626 : std::string expanded_fmt = expand_format (fmt);
125 : 4626 : int nwr = vasprintf (&mbuf, expanded_fmt.c_str (), ap);
126 : 4626 : if (nwr == -1)
127 : : {
128 : : // memory allocation failed
129 : 0 : rust_be_error_at (UNKNOWN_LOCATION,
130 : 0 : "memory allocation failed in vasprintf");
131 : 0 : rust_assert (0);
132 : : }
133 : 4626 : std::string rval = std::string (mbuf);
134 : 4626 : free (mbuf);
135 : 4626 : return rval;
136 : 4626 : }
137 : :
138 : : #pragma GCC diagnostic pop
139 : :
140 : : static const char *cached_open_quote = NULL;
141 : : static const char *cached_close_quote = NULL;
142 : :
143 : : void
144 : 1201 : rust_be_get_quotechars (const char **open_qu, const char **close_qu)
145 : : {
146 : 1201 : *open_qu = open_quote;
147 : 1201 : *close_qu = close_quote;
148 : 1201 : }
149 : :
150 : : const char *
151 : 5490 : rust_open_quote ()
152 : : {
153 : 5490 : if (cached_open_quote == NULL)
154 : 1201 : rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
155 : 5490 : return cached_open_quote;
156 : : }
157 : :
158 : : const char *
159 : 5490 : rust_close_quote ()
160 : : {
161 : 5490 : if (cached_close_quote == NULL)
162 : 0 : rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
163 : 5490 : return cached_close_quote;
164 : : }
165 : :
166 : : void
167 : 0 : rust_be_internal_error_at (const location_t location, const std::string &errmsg)
168 : : {
169 : 0 : std::string loc_str = Linemap::location_to_string (location);
170 : 0 : if (loc_str.empty ())
171 : 0 : internal_error ("%s", errmsg.c_str ());
172 : : else
173 : 0 : internal_error ("at %s, %s", loc_str.c_str (), errmsg.c_str ());
174 : : }
175 : :
176 : : void
177 : 0 : rust_internal_error_at (const location_t location, const char *fmt, ...)
178 : : {
179 : 0 : va_list ap;
180 : :
181 : 0 : va_start (ap, fmt);
182 : 0 : rust_be_internal_error_at (location, expand_message (fmt, ap));
183 : : va_end (ap);
184 : : }
185 : :
186 : : void
187 : 641 : rust_be_error_at (const location_t location, const std::string &errmsg)
188 : : {
189 : 641 : error_at (location, "%s", errmsg.c_str ());
190 : 641 : }
191 : :
192 : : void
193 : 641 : rust_error_at (const location_t location, const char *fmt, ...)
194 : : {
195 : 641 : va_list ap;
196 : :
197 : 641 : va_start (ap, fmt);
198 : 641 : rust_be_error_at (location, expand_message (fmt, ap));
199 : 641 : va_end (ap);
200 : 641 : }
201 : :
202 : : class rust_error_code_rule : public diagnostics::metadata::rule
203 : : {
204 : : public:
205 : 398 : rust_error_code_rule (const ErrorCode code) : m_code (code) {}
206 : :
207 : 398 : void format_error_code (char *buffer) const
208 : : {
209 : : // we can use the `u` format specifier because the `ErrorCode` enum class
210 : : // "inherits" from `unsigned int` - add a static assertion to make sure
211 : : // that's the case before we do the formatting
212 : 398 : static_assert (
213 : : std::is_same<std::underlying_type<ErrorCode>::type, unsigned int>::value,
214 : : "invalid format specifier for ErrorCode's underlying type");
215 : :
216 : 398 : snprintf (buffer, 6, "E%04u",
217 : 398 : (std::underlying_type<ErrorCode>::type) m_code);
218 : : }
219 : :
220 : 398 : char *make_description () const final override
221 : : {
222 : : // 'E' + 4 characters + \0
223 : 398 : char *buffer = static_cast<char *> (xcalloc (6, sizeof (char)));
224 : :
225 : 398 : format_error_code (buffer);
226 : :
227 : 398 : return buffer;
228 : : }
229 : :
230 : 0 : char *make_url () const final override
231 : : {
232 : 0 : char buffer[6] = {0};
233 : 0 : format_error_code (buffer);
234 : :
235 : 0 : return concat ("https://doc.rust-lang.org/error-index.html#", buffer, NULL);
236 : : }
237 : :
238 : : private:
239 : : const ErrorCode m_code;
240 : : };
241 : :
242 : : void
243 : 202 : rust_be_error_at (const location_t location, const ErrorCode code,
244 : : const std::string &errmsg)
245 : : {
246 : 202 : rich_location gcc_loc (line_table, location);
247 : 202 : diagnostics::metadata m;
248 : 202 : rust_error_code_rule rule (code);
249 : 202 : m.add_rule (rule);
250 : 202 : error_meta (&gcc_loc, m, "%s", errmsg.c_str ());
251 : 202 : }
252 : :
253 : : void
254 : 202 : rust_error_at (const location_t location, const ErrorCode code, const char *fmt,
255 : : ...)
256 : : {
257 : 202 : va_list ap;
258 : :
259 : 202 : va_start (ap, fmt);
260 : 202 : rust_be_error_at (location, code, expand_message (fmt, ap));
261 : 202 : va_end (ap);
262 : 202 : }
263 : :
264 : : void
265 : 196 : rust_be_error_at (const rich_location &location, const ErrorCode code,
266 : : const std::string &errmsg)
267 : : {
268 : : /* TODO: 'error_at' would like a non-'const' 'rich_location *'. */
269 : 196 : rich_location &gcc_loc = const_cast<rich_location &> (location);
270 : 196 : diagnostics::metadata m;
271 : 196 : rust_error_code_rule rule (code);
272 : 196 : m.add_rule (rule);
273 : 196 : error_meta (&gcc_loc, m, "%s", errmsg.c_str ());
274 : 196 : }
275 : :
276 : : void
277 : 196 : rust_error_at (const rich_location &location, const ErrorCode code,
278 : : const char *fmt, ...)
279 : : {
280 : 196 : va_list ap;
281 : :
282 : 196 : va_start (ap, fmt);
283 : 196 : rust_be_error_at (location, code, expand_message (fmt, ap));
284 : 196 : va_end (ap);
285 : 196 : }
286 : :
287 : : void
288 : 0 : rust_be_error_at (rich_location *richloc, const ErrorCode code,
289 : : const std::string &errmsg)
290 : : {
291 : 0 : diagnostics::metadata m;
292 : 0 : rust_error_code_rule rule (code);
293 : 0 : m.add_rule (rule);
294 : 0 : error_meta (richloc, m, "%s", errmsg.c_str ());
295 : 0 : }
296 : :
297 : : void
298 : 0 : rust_error_at (rich_location *richloc, const ErrorCode code, const char *fmt,
299 : : ...)
300 : : {
301 : : /* TODO: Refactoring diagnostics to this overload */
302 : 0 : va_list ap;
303 : :
304 : 0 : va_start (ap, fmt);
305 : 0 : rust_be_error_at (richloc, code, expand_message (fmt, ap));
306 : 0 : va_end (ap);
307 : 0 : }
308 : :
309 : : void
310 : 1010 : rust_be_warning_at (const location_t location, int opt,
311 : : const std::string &warningmsg)
312 : : {
313 : 1010 : warning_at (location, opt, "%s", warningmsg.c_str ());
314 : 1010 : }
315 : :
316 : : void
317 : 1010 : rust_warning_at (const location_t location, int opt, const char *fmt, ...)
318 : : {
319 : 1010 : va_list ap;
320 : :
321 : 1010 : va_start (ap, fmt);
322 : 1010 : rust_be_warning_at (location, opt, expand_message (fmt, ap));
323 : 1010 : va_end (ap);
324 : 1010 : }
325 : :
326 : : void
327 : 1 : rust_be_fatal_error (const location_t location, const std::string &fatalmsg)
328 : : {
329 : 1 : fatal_error (location, "%s", fatalmsg.c_str ());
330 : : }
331 : :
332 : : void
333 : 1 : rust_fatal_error (const location_t location, const char *fmt, ...)
334 : : {
335 : 1 : va_list ap;
336 : :
337 : 1 : va_start (ap, fmt);
338 : 1 : rust_be_fatal_error (location, expand_message (fmt, ap));
339 : : va_end (ap);
340 : : }
341 : :
342 : : void
343 : 168 : rust_be_inform (const location_t location, const std::string &infomsg)
344 : : {
345 : 168 : inform (location, "%s", infomsg.c_str ());
346 : 168 : }
347 : :
348 : : void
349 : 41 : rust_inform (const location_t location, const char *fmt, ...)
350 : : {
351 : 41 : va_list ap;
352 : :
353 : 41 : va_start (ap, fmt);
354 : 41 : rust_be_inform (location, expand_message (fmt, ap));
355 : 41 : va_end (ap);
356 : 41 : }
357 : :
358 : : // Rich Locations
359 : : void
360 : 48 : rust_be_error_at (const rich_location &location, const std::string &errmsg)
361 : : {
362 : : /* TODO: 'error_at' would like a non-'const' 'rich_location *'. */
363 : 48 : rich_location &gcc_loc = const_cast<rich_location &> (location);
364 : 48 : error_at (&gcc_loc, "%s", errmsg.c_str ());
365 : 48 : }
366 : :
367 : : void
368 : 48 : rust_error_at (const rich_location &location, const char *fmt, ...)
369 : : {
370 : 48 : va_list ap;
371 : :
372 : 48 : va_start (ap, fmt);
373 : 48 : rust_be_error_at (location, expand_message (fmt, ap));
374 : 48 : va_end (ap);
375 : 48 : }
376 : :
377 : : void
378 : 0 : rust_be_error_at (rich_location *richloc, const std::string &errmsg)
379 : : {
380 : 0 : error_at (richloc, "%s", errmsg.c_str ());
381 : 0 : }
382 : :
383 : : void
384 : 0 : rust_error_at (rich_location *richloc, const char *fmt, ...)
385 : : {
386 : : /* TODO: Refactoring diagnostics to this overload */
387 : 0 : va_list ap;
388 : :
389 : 0 : va_start (ap, fmt);
390 : 0 : rust_be_error_at (richloc, expand_message (fmt, ap));
391 : 0 : va_end (ap);
392 : 0 : }
393 : :
394 : : bool
395 : 6186592 : rust_be_debug_p (void)
396 : : {
397 : 6186592 : return !!flag_rust_debug;
398 : : }
399 : :
400 : : void
401 : 6186556 : rust_debug_loc (const location_t location, const char *fmt, ...)
402 : : {
403 : 6186556 : if (!rust_be_debug_p ())
404 : 6186429 : return;
405 : :
406 : 127 : va_list ap;
407 : :
408 : 127 : va_start (ap, fmt);
409 : 127 : char *mbuf = NULL;
410 : 127 : int nwr = vasprintf (&mbuf, fmt, ap);
411 : 127 : va_end (ap);
412 : 127 : if (nwr == -1)
413 : : {
414 : 0 : rust_be_error_at (UNKNOWN_LOCATION,
415 : 0 : "memory allocation failed in vasprintf");
416 : 0 : rust_assert (0);
417 : : }
418 : 127 : std::string rval = std::string (mbuf);
419 : 127 : free (mbuf);
420 : 127 : rust_be_inform (location, rval);
421 : 127 : }
422 : :
423 : : namespace Rust {
424 : :
425 : : /**
426 : : * This function takes ownership of `args` and calls `va_end` on it
427 : : */
428 : :
429 : : // simple location
430 : : static Error va_constructor (Error::Kind kind, location_t locus,
431 : : const char *fmt, va_list args)
432 : : RUST_ATTRIBUTE_GCC_DIAG (3, 0);
433 : :
434 : : // simple location + error code
435 : : static Error va_constructor (Error::Kind kind, location_t locus,
436 : : const ErrorCode code, const char *fmt,
437 : : va_list args) RUST_ATTRIBUTE_GCC_DIAG (4, 0);
438 : :
439 : : // rich location
440 : : static Error va_constructor (Error::Kind kind, rich_location *r_locus,
441 : : const char *fmt, va_list args)
442 : : RUST_ATTRIBUTE_GCC_DIAG (3, 0);
443 : :
444 : : // rich location + error code
445 : : static Error va_constructor (Error::Kind kind, rich_location *r_locus,
446 : : const ErrorCode code, const char *fmt,
447 : : va_list args) RUST_ATTRIBUTE_GCC_DIAG (4, 0);
448 : :
449 : : // simple location
450 : : static Error
451 : 2438 : va_constructor (Error::Kind kind, location_t locus, const char *fmt,
452 : : va_list args)
453 : : {
454 : 2438 : std::string message = expand_message (fmt, args);
455 : 2438 : message.shrink_to_fit ();
456 : 2438 : va_end (args);
457 : :
458 : 7314 : return Error (kind, locus, message);
459 : 2438 : }
460 : :
461 : : // simple location + error code
462 : : static Error
463 : 49 : va_constructor (Error::Kind kind, location_t locus, const ErrorCode code,
464 : : const char *fmt, va_list args)
465 : : {
466 : 49 : std::string message = expand_message (fmt, args);
467 : 49 : message.shrink_to_fit ();
468 : 49 : va_end (args);
469 : :
470 : 147 : return Error (kind, locus, code, message);
471 : 49 : }
472 : :
473 : : // rich location
474 : : static Error
475 : 0 : va_constructor (Error::Kind kind, rich_location *r_locus, const char *fmt,
476 : : va_list args)
477 : : {
478 : 0 : std::string message = expand_message (fmt, args);
479 : 0 : message.shrink_to_fit ();
480 : 0 : va_end (args);
481 : :
482 : 0 : return Error (kind, r_locus, message);
483 : 0 : }
484 : :
485 : : // rich location + error code
486 : : static Error
487 : 0 : va_constructor (Error::Kind kind, rich_location *r_locus, const ErrorCode code,
488 : : const char *fmt, va_list args)
489 : : {
490 : 0 : std::string message = expand_message (fmt, args);
491 : 0 : message.shrink_to_fit ();
492 : 0 : va_end (args);
493 : :
494 : 0 : return Error (kind, r_locus, code, message);
495 : 0 : }
496 : :
497 : : // simple location
498 : 2432 : Error::Error (const location_t location, const char *fmt, ...)
499 : 2432 : : kind (Kind::Err), locus (location)
500 : : {
501 : 2432 : va_list ap;
502 : 2432 : va_start (ap, fmt);
503 : :
504 : 2432 : *this = va_constructor (Kind::Err, location, fmt, ap);
505 : 2432 : }
506 : :
507 : : // simple location + error code
508 : 49 : Error::Error (const location_t location, const ErrorCode code, const char *fmt,
509 : 49 : ...)
510 : 49 : : kind (Kind::Err), locus (location), errorcode (code)
511 : : {
512 : 49 : va_list ap;
513 : 49 : va_start (ap, fmt);
514 : :
515 : 49 : *this = va_constructor (Kind::Err, location, code, fmt, ap);
516 : 49 : }
517 : :
518 : : // rich location
519 : 0 : Error::Error (rich_location *r_locus, const char *fmt, ...)
520 : 0 : : kind (Kind::Err), richlocus (r_locus)
521 : : {
522 : 0 : va_list ap;
523 : 0 : va_start (ap, fmt);
524 : :
525 : 0 : *this = va_constructor (Kind::Err, r_locus, fmt, ap);
526 : 0 : }
527 : :
528 : : // rich location + error code
529 : 0 : Error::Error (rich_location *r_locus, const ErrorCode code, const char *fmt,
530 : 0 : ...)
531 : 0 : : kind (Kind::Err), richlocus (r_locus), errorcode (code)
532 : : {
533 : 0 : va_list ap;
534 : 0 : va_start (ap, fmt);
535 : :
536 : 0 : *this = va_constructor (Kind::Err, r_locus, code, fmt, ap);
537 : 0 : }
538 : :
539 : : Error
540 : 6 : Error::Hint (const location_t location, const char *fmt, ...)
541 : : {
542 : 6 : va_list ap;
543 : 6 : va_start (ap, fmt);
544 : :
545 : 6 : return va_constructor (Kind::Hint, location, fmt, ap);
546 : : }
547 : :
548 : : Error
549 : 0 : Error::Fatal (const location_t location, const char *fmt, ...)
550 : : {
551 : 0 : va_list ap;
552 : 0 : va_start (ap, fmt);
553 : :
554 : 0 : return va_constructor (Kind::FatalErr, location, fmt, ap);
555 : : }
556 : :
557 : : } // namespace Rust
|