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 4682 : expand_format (const char *fmt)
39 : {
40 4682 : std::stringstream ss;
41 138480 : for (const char *c = fmt; *c; ++c)
42 : {
43 133798 : if (*c != '%')
44 : {
45 127183 : ss << *c;
46 127183 : continue;
47 : }
48 6615 : c++;
49 6615 : 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 306 : case '<':
67 306 : {
68 306 : ss << rust_open_quote ();
69 306 : break;
70 : }
71 306 : case '>':
72 306 : {
73 306 : ss << rust_close_quote ();
74 306 : break;
75 : }
76 5355 : case 'q':
77 5355 : {
78 5355 : ss << rust_open_quote ();
79 5355 : c++;
80 5355 : if (*c == 'm')
81 : {
82 0 : ss << mformat_value ();
83 : }
84 : else
85 : {
86 5355 : ss << "%" << *c;
87 : }
88 5355 : ss << rust_close_quote ();
89 5355 : break;
90 : }
91 633 : default:
92 633 : {
93 633 : ss << "%" << *c;
94 : }
95 : }
96 : }
97 4682 : return ss.str ();
98 4682 : }
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 4682 : expand_message (const char *fmt, va_list ap)
122 : {
123 4682 : char *mbuf = 0;
124 4682 : std::string expanded_fmt = expand_format (fmt);
125 4682 : int nwr = vasprintf (&mbuf, expanded_fmt.c_str (), ap);
126 4682 : 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 4682 : std::string rval = std::string (mbuf);
134 4682 : free (mbuf);
135 4682 : return rval;
136 4682 : }
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 1264 : rust_be_get_quotechars (const char **open_qu, const char **close_qu)
145 : {
146 1264 : *open_qu = open_quote;
147 1264 : *close_qu = close_quote;
148 1264 : }
149 :
150 : const char *
151 5666 : rust_open_quote ()
152 : {
153 5666 : if (cached_open_quote == NULL)
154 1264 : rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
155 5666 : return cached_open_quote;
156 : }
157 :
158 : const char *
159 5666 : rust_close_quote ()
160 : {
161 5666 : if (cached_close_quote == NULL)
162 0 : rust_be_get_quotechars (&cached_open_quote, &cached_close_quote);
163 5666 : 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 631 : rust_be_error_at (const location_t location, const std::string &errmsg)
188 : {
189 631 : error_at (location, "%s", errmsg.c_str ());
190 631 : }
191 :
192 : void
193 631 : rust_error_at (const location_t location, const char *fmt, ...)
194 : {
195 631 : va_list ap;
196 :
197 631 : va_start (ap, fmt);
198 631 : rust_be_error_at (location, expand_message (fmt, ap));
199 631 : va_end (ap);
200 631 : }
201 :
202 : class rust_error_code_rule : public diagnostics::metadata::rule
203 : {
204 : public:
205 443 : rust_error_code_rule (const ErrorCode code) : m_code (code) {}
206 :
207 443 : 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 443 : 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 443 : snprintf (buffer, 6, "E%04u",
217 443 : (std::underlying_type<ErrorCode>::type) m_code);
218 : }
219 :
220 443 : char *make_description () const final override
221 : {
222 : // 'E' + 4 characters + \0
223 443 : char *buffer = static_cast<char *> (xcalloc (6, sizeof (char)));
224 :
225 443 : format_error_code (buffer);
226 :
227 443 : 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 227 : rust_be_error_at (const location_t location, const ErrorCode code,
244 : const std::string &errmsg)
245 : {
246 227 : rich_location gcc_loc (line_table, location);
247 227 : diagnostics::metadata m;
248 227 : rust_error_code_rule rule (code);
249 227 : m.add_rule (rule);
250 227 : error_meta (&gcc_loc, m, "%s", errmsg.c_str ());
251 227 : }
252 :
253 : void
254 227 : rust_error_at (const location_t location, const ErrorCode code, const char *fmt,
255 : ...)
256 : {
257 227 : va_list ap;
258 :
259 227 : va_start (ap, fmt);
260 227 : rust_be_error_at (location, code, expand_message (fmt, ap));
261 227 : va_end (ap);
262 227 : }
263 :
264 : void
265 216 : 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 216 : rich_location &gcc_loc = const_cast<rich_location &> (location);
270 216 : diagnostics::metadata m;
271 216 : rust_error_code_rule rule (code);
272 216 : m.add_rule (rule);
273 216 : error_meta (&gcc_loc, m, "%s", errmsg.c_str ());
274 216 : }
275 :
276 : void
277 216 : rust_error_at (const rich_location &location, const ErrorCode code,
278 : const char *fmt, ...)
279 : {
280 216 : va_list ap;
281 :
282 216 : va_start (ap, fmt);
283 216 : rust_be_error_at (location, code, expand_message (fmt, ap));
284 216 : va_end (ap);
285 216 : }
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 1038 : rust_be_warning_at (const location_t location, int opt,
311 : const std::string &warningmsg)
312 : {
313 1038 : warning_at (location, opt, "%s", warningmsg.c_str ());
314 1038 : }
315 :
316 : void
317 1038 : rust_warning_at (const location_t location, int opt, const char *fmt, ...)
318 : {
319 1038 : va_list ap;
320 :
321 1038 : va_start (ap, fmt);
322 1038 : rust_be_warning_at (location, opt, expand_message (fmt, ap));
323 1038 : va_end (ap);
324 1038 : }
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 178 : rust_be_inform (const location_t location, const std::string &infomsg)
344 : {
345 178 : inform (location, "%s", infomsg.c_str ());
346 178 : }
347 :
348 : void
349 52 : rust_inform (const location_t location, const char *fmt, ...)
350 : {
351 52 : va_list ap;
352 :
353 52 : va_start (ap, fmt);
354 52 : rust_be_inform (location, expand_message (fmt, ap));
355 52 : va_end (ap);
356 52 : }
357 :
358 : // Rich Locations
359 : void
360 51 : 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 51 : rich_location &gcc_loc = const_cast<rich_location &> (location);
364 51 : error_at (&gcc_loc, "%s", errmsg.c_str ());
365 51 : }
366 :
367 : void
368 51 : rust_error_at (const rich_location &location, const char *fmt, ...)
369 : {
370 51 : va_list ap;
371 :
372 51 : va_start (ap, fmt);
373 51 : rust_be_error_at (location, expand_message (fmt, ap));
374 51 : va_end (ap);
375 51 : }
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 11946070 : rust_be_debug_p (void)
396 : {
397 11946070 : return !!flag_rust_debug;
398 : }
399 :
400 : void
401 11946034 : rust_debug_loc (const location_t location, const char *fmt, ...)
402 : {
403 11946034 : if (!rust_be_debug_p ())
404 11945908 : return;
405 :
406 126 : va_list ap;
407 :
408 126 : va_start (ap, fmt);
409 126 : char *mbuf = NULL;
410 126 : int nwr = vasprintf (&mbuf, fmt, ap);
411 126 : va_end (ap);
412 126 : 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 126 : std::string rval = std::string (mbuf);
419 126 : free (mbuf);
420 126 : rust_be_inform (location, rval);
421 126 : }
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 2410 : va_constructor (Error::Kind kind, location_t locus, const char *fmt,
452 : va_list args)
453 : {
454 2410 : std::string message = expand_message (fmt, args);
455 2410 : message.shrink_to_fit ();
456 2410 : va_end (args);
457 :
458 7230 : return Error (kind, locus, message);
459 2410 : }
460 :
461 : // simple location + error code
462 : static Error
463 56 : va_constructor (Error::Kind kind, location_t locus, const ErrorCode code,
464 : const char *fmt, va_list args)
465 : {
466 56 : std::string message = expand_message (fmt, args);
467 56 : message.shrink_to_fit ();
468 56 : va_end (args);
469 :
470 168 : return Error (kind, locus, code, message);
471 56 : }
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 2404 : Error::Error (const location_t location, const char *fmt, ...)
499 2404 : : kind (Kind::Err), locus (location)
500 : {
501 2404 : va_list ap;
502 2404 : va_start (ap, fmt);
503 :
504 2404 : *this = va_constructor (Kind::Err, location, fmt, ap);
505 2404 : }
506 :
507 : // simple location + error code
508 56 : Error::Error (const location_t location, const ErrorCode code, const char *fmt,
509 56 : ...)
510 56 : : kind (Kind::Err), locus (location), errorcode (code)
511 : {
512 56 : va_list ap;
513 56 : va_start (ap, fmt);
514 :
515 56 : *this = va_constructor (Kind::Err, location, code, fmt, ap);
516 56 : }
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
|