Line data Source code
1 : // rust-diagnostics.cc -- GCC implementation of rust diagnostics interface.
2 : // Copyright (C) 2016-2026 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 15 : mformat_value ()
29 : {
30 15 : 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 55874 : expand_format (const char *fmt)
39 : {
40 55874 : std::stringstream ss;
41 1932614 : for (const char *c = fmt; *c; ++c)
42 : {
43 1820866 : if (*c != '%')
44 : {
45 1779227 : ss << *c;
46 1779227 : continue;
47 : }
48 41639 : c++;
49 41639 : 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 15 : case 'm':
62 15 : {
63 30 : ss << mformat_value ();
64 15 : break;
65 : }
66 349 : case '<':
67 349 : {
68 349 : ss << rust_open_quote ();
69 349 : break;
70 : }
71 349 : case '>':
72 349 : {
73 349 : ss << rust_close_quote ();
74 349 : break;
75 : }
76 40193 : case 'q':
77 40193 : {
78 40193 : ss << rust_open_quote ();
79 40193 : c++;
80 40193 : if (*c == 'm')
81 : {
82 0 : ss << mformat_value ();
83 : }
84 : else
85 : {
86 40193 : ss << "%" << *c;
87 : }
88 40193 : ss << rust_close_quote ();
89 40193 : break;
90 : }
91 733 : default:
92 733 : {
93 733 : ss << "%" << *c;
94 : }
95 : }
96 : }
97 55874 : return ss.str ();
98 55874 : }
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 55874 : expand_message (const char *fmt, va_list ap)
122 : {
123 55874 : char *mbuf = 0;
124 55874 : std::string expanded_fmt = expand_format (fmt);
125 55874 : int nwr = vasprintf (&mbuf, expanded_fmt.c_str (), ap);
126 55874 : 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 55874 : std::string rval = std::string (mbuf);
134 55874 : free (mbuf);
135 55874 : return rval;
136 55874 : }
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 1401 : rust_be_get_quotechars (const char **open_qu, const char **close_qu)
145 : {
146 1401 : *open_qu = open_quote;
147 1401 : *close_qu = close_quote;
148 1401 : }
149 :
150 : const char *
151 40547 : rust_open_quote ()
152 : {
153 40547 : if (cached_open_quote == NULL)
154 1401 : rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
155 40547 : return cached_open_quote;
156 : }
157 :
158 : const char *
159 40547 : rust_close_quote ()
160 : {
161 40547 : if (cached_close_quote == NULL)
162 0 : rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
163 40547 : return cached_close_quote;
164 : }
165 :
166 : void
167 2 : rust_be_internal_error_at (const location_t location, const std::string &errmsg)
168 : {
169 2 : std::string loc_str = Linemap::location_to_string (location);
170 2 : if (loc_str.empty ())
171 0 : internal_error ("%s", errmsg.c_str ());
172 : else
173 2 : internal_error ("at %s, %s", loc_str.c_str (), errmsg.c_str ());
174 : }
175 :
176 : void
177 2 : rust_internal_error_at (const location_t location, const char *fmt, ...)
178 : {
179 2 : va_list ap;
180 :
181 2 : va_start (ap, fmt);
182 2 : rust_be_internal_error_at (location, expand_message (fmt, ap));
183 : va_end (ap);
184 : }
185 :
186 : void
187 704 : rust_be_error_at (const location_t location, const std::string &errmsg)
188 : {
189 704 : error_at (location, "%s", errmsg.c_str ());
190 704 : }
191 :
192 : void
193 704 : rust_error_at (const location_t location, const char *fmt, ...)
194 : {
195 704 : va_list ap;
196 :
197 704 : va_start (ap, fmt);
198 704 : rust_be_error_at (location, expand_message (fmt, ap));
199 704 : va_end (ap);
200 704 : }
201 :
202 : class rust_error_code_rule : public diagnostics::metadata::rule
203 : {
204 : public:
205 534 : rust_error_code_rule (const ErrorCode code) : m_code (code) {}
206 :
207 534 : 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 534 : 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 534 : snprintf (buffer, 6, "E%04u",
217 534 : (std::underlying_type<ErrorCode>::type) m_code);
218 : }
219 :
220 534 : char *make_description () const final override
221 : {
222 : // 'E' + 4 characters + \0
223 534 : char *buffer = static_cast<char *> (xcalloc (6, sizeof (char)));
224 :
225 534 : format_error_code (buffer);
226 :
227 534 : 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 287 : rust_be_error_at (const location_t location, const ErrorCode code,
244 : const std::string &errmsg)
245 : {
246 287 : rich_location gcc_loc (line_table, location);
247 287 : diagnostics::metadata m;
248 287 : rust_error_code_rule rule (code);
249 287 : m.add_rule (rule);
250 287 : error_meta (&gcc_loc, m, "%s", errmsg.c_str ());
251 287 : }
252 :
253 : void
254 287 : rust_error_at (const location_t location, const ErrorCode code, const char *fmt,
255 : ...)
256 : {
257 287 : va_list ap;
258 :
259 287 : va_start (ap, fmt);
260 287 : rust_be_error_at (location, code, expand_message (fmt, ap));
261 287 : va_end (ap);
262 287 : }
263 :
264 : void
265 247 : 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 247 : rich_location &gcc_loc = const_cast<rich_location &> (location);
270 247 : diagnostics::metadata m;
271 247 : rust_error_code_rule rule (code);
272 247 : m.add_rule (rule);
273 247 : error_meta (&gcc_loc, m, "%s", errmsg.c_str ());
274 247 : }
275 :
276 : void
277 247 : rust_error_at (const rich_location &location, const ErrorCode code,
278 : const char *fmt, ...)
279 : {
280 247 : va_list ap;
281 :
282 247 : va_start (ap, fmt);
283 247 : rust_be_error_at (location, code, expand_message (fmt, ap));
284 247 : va_end (ap);
285 247 : }
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 1267 : rust_be_warning_at (const location_t location, int opt,
311 : const std::string &warningmsg)
312 : {
313 1267 : warning_at (location, opt, "%s", warningmsg.c_str ());
314 1267 : }
315 :
316 : void
317 1267 : rust_warning_at (const location_t location, int opt, const char *fmt, ...)
318 : {
319 1267 : va_list ap;
320 :
321 1267 : va_start (ap, fmt);
322 1267 : rust_be_warning_at (location, opt, expand_message (fmt, ap));
323 1267 : va_end (ap);
324 1267 : }
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 216 : rust_be_inform (const location_t location, const std::string &infomsg)
344 : {
345 216 : inform (location, "%s", infomsg.c_str ());
346 216 : }
347 :
348 : void
349 65 : rust_inform (const location_t location, const char *fmt, ...)
350 : {
351 65 : va_list ap;
352 :
353 65 : va_start (ap, fmt);
354 65 : rust_be_inform (location, expand_message (fmt, ap));
355 65 : va_end (ap);
356 65 : }
357 :
358 : // Rich Locations
359 : void
360 54 : 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 54 : rich_location &gcc_loc = const_cast<rich_location &> (location);
364 54 : error_at (&gcc_loc, "%s", errmsg.c_str ());
365 54 : }
366 :
367 : void
368 54 : rust_error_at (const rich_location &location, const char *fmt, ...)
369 : {
370 54 : va_list ap;
371 :
372 54 : va_start (ap, fmt);
373 54 : rust_be_error_at (location, expand_message (fmt, ap));
374 54 : va_end (ap);
375 54 : }
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 665846556 : rust_be_debug_p (void)
396 : {
397 665846556 : return !!flag_rust_debug;
398 : }
399 :
400 : void
401 665846507 : rust_debug_loc (const location_t location, const char *fmt, ...)
402 : {
403 665846507 : if (!rust_be_debug_p ())
404 665846356 : return;
405 :
406 151 : va_list ap;
407 :
408 151 : va_start (ap, fmt);
409 151 : char *mbuf = NULL;
410 151 : int nwr = vasprintf (&mbuf, fmt, ap);
411 151 : va_end (ap);
412 151 : 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 151 : std::string rval = std::string (mbuf);
419 151 : free (mbuf);
420 151 : rust_be_inform (location, rval);
421 151 : }
422 :
423 : void
424 2 : rust_debug_fmt_at (const location_t location, const char *fmt, ...)
425 : {
426 2 : if (!rust_be_debug_p ())
427 : return;
428 :
429 0 : va_list ap;
430 :
431 0 : va_start (ap, fmt);
432 0 : rust_be_inform (location, expand_message (fmt, ap));
433 0 : va_end (ap);
434 : }
435 :
436 : namespace Rust {
437 :
438 : /**
439 : * This function takes ownership of `args` and calls `va_end` on it
440 : */
441 :
442 : // simple location
443 : static Error va_constructor (Error::Kind kind, location_t locus,
444 : const char *fmt, va_list args)
445 : RUST_ATTRIBUTE_GCC_DIAG (3, 0);
446 :
447 : // simple location + error code
448 : static Error va_constructor (Error::Kind kind, location_t locus,
449 : const ErrorCode code, const char *fmt,
450 : va_list args) RUST_ATTRIBUTE_GCC_DIAG (4, 0);
451 :
452 : // rich location
453 : static Error va_constructor (Error::Kind kind, rich_location *r_locus,
454 : const char *fmt, va_list args)
455 : RUST_ATTRIBUTE_GCC_DIAG (3, 0);
456 :
457 : // rich location + error code
458 : static Error va_constructor (Error::Kind kind, rich_location *r_locus,
459 : const ErrorCode code, const char *fmt,
460 : va_list args) RUST_ATTRIBUTE_GCC_DIAG (4, 0);
461 :
462 : // simple location
463 : static Error
464 52852 : va_constructor (Error::Kind kind, location_t locus, const char *fmt,
465 : va_list args)
466 : {
467 52852 : std::string message = expand_message (fmt, args);
468 52852 : message.shrink_to_fit ();
469 52852 : va_end (args);
470 :
471 158556 : return Error (kind, locus, message);
472 52852 : }
473 :
474 : // simple location + error code
475 : static Error
476 395 : va_constructor (Error::Kind kind, location_t locus, const ErrorCode code,
477 : const char *fmt, va_list args)
478 : {
479 395 : std::string message = expand_message (fmt, args);
480 395 : message.shrink_to_fit ();
481 395 : va_end (args);
482 :
483 1185 : return Error (kind, locus, code, message);
484 395 : }
485 :
486 : // rich location
487 : static Error
488 0 : va_constructor (Error::Kind kind, rich_location *r_locus, const char *fmt,
489 : va_list args)
490 : {
491 0 : std::string message = expand_message (fmt, args);
492 0 : message.shrink_to_fit ();
493 0 : va_end (args);
494 :
495 0 : return Error (kind, r_locus, message);
496 0 : }
497 :
498 : // rich location + error code
499 : static Error
500 0 : va_constructor (Error::Kind kind, rich_location *r_locus, const ErrorCode code,
501 : const char *fmt, va_list args)
502 : {
503 0 : std::string message = expand_message (fmt, args);
504 0 : message.shrink_to_fit ();
505 0 : va_end (args);
506 :
507 0 : return Error (kind, r_locus, code, message);
508 0 : }
509 :
510 : // simple location
511 52846 : Error::Error (const location_t location, const char *fmt, ...)
512 52846 : : kind (Kind::Err), locus (location)
513 : {
514 52846 : va_list ap;
515 52846 : va_start (ap, fmt);
516 :
517 52846 : *this = va_constructor (Kind::Err, location, fmt, ap);
518 52846 : }
519 :
520 : // simple location + error code
521 395 : Error::Error (const location_t location, const ErrorCode code, const char *fmt,
522 : ...)
523 395 : : kind (Kind::Err), locus (location), errorcode (code)
524 : {
525 395 : va_list ap;
526 395 : va_start (ap, fmt);
527 :
528 395 : *this = va_constructor (Kind::Err, location, code, fmt, ap);
529 395 : }
530 :
531 : // rich location
532 0 : Error::Error (rich_location *r_locus, const char *fmt, ...)
533 0 : : kind (Kind::Err), richlocus (r_locus)
534 : {
535 0 : va_list ap;
536 0 : va_start (ap, fmt);
537 :
538 0 : *this = va_constructor (Kind::Err, r_locus, fmt, ap);
539 0 : }
540 :
541 : // rich location + error code
542 0 : Error::Error (rich_location *r_locus, const ErrorCode code, const char *fmt,
543 : ...)
544 0 : : kind (Kind::Err), richlocus (r_locus), errorcode (code)
545 : {
546 0 : va_list ap;
547 0 : va_start (ap, fmt);
548 :
549 0 : *this = va_constructor (Kind::Err, r_locus, code, fmt, ap);
550 0 : }
551 :
552 : Error
553 6 : Error::Hint (const location_t location, const char *fmt, ...)
554 : {
555 6 : va_list ap;
556 6 : va_start (ap, fmt);
557 :
558 6 : return va_constructor (Kind::Hint, location, fmt, ap);
559 : }
560 :
561 : Error
562 0 : Error::Fatal (const location_t location, const char *fmt, ...)
563 : {
564 0 : va_list ap;
565 0 : va_start (ap, fmt);
566 :
567 0 : return va_constructor (Kind::FatalErr, location, fmt, ap);
568 : }
569 :
570 : } // namespace Rust
|