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