OpenMP: Update documentation of metadirective implementation status.
[gcc.git] / gcc / pretty-print.cc
blob79c7bc2b6625212755dbb58d30455c6616051cb2
1 /* Various declarations for language-independent pretty-print subroutines.
2 Copyright (C) 2003-2025 Free Software Foundation, Inc.
3 Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
5 This file is part of GCC.
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.
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.
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/>. */
21 #include "config.h"
22 #define INCLUDE_VECTOR
23 #include "system.h"
24 #include "coretypes.h"
25 #include "intl.h"
26 #include "pretty-print.h"
27 #include "pretty-print-format-impl.h"
28 #include "pretty-print-markup.h"
29 #include "pretty-print-urlifier.h"
30 #include "diagnostic-color.h"
31 #include "diagnostic-event-id.h"
32 #include "diagnostic-highlight-colors.h"
33 #include "make-unique.h"
34 #include "selftest.h"
36 #if HAVE_ICONV
37 #include <iconv.h>
38 #endif
40 #ifdef __MINGW32__
42 /* Replacement for fputs() that handles ANSI escape codes on Windows NT.
43 Contributed by: Liu Hao (lh_mouse at 126 dot com)
45 XXX: This file is compiled into libcommon.a that will be self-contained.
46 It looks like that these functions can be put nowhere else. */
48 #include <io.h>
49 #define WIN32_LEAN_AND_MEAN 1
50 #include <windows.h>
52 /* Write all bytes in [s,s+n) into the specified stream.
53 Errors are ignored. */
54 static void
55 write_all (HANDLE h, const char *s, size_t n)
57 size_t rem = n;
58 DWORD step;
60 while (rem != 0)
62 if (rem <= UINT_MAX)
63 step = rem;
64 else
65 step = UINT_MAX;
66 if (!WriteFile (h, s + n - rem, step, &step, NULL))
67 break;
68 rem -= step;
72 /* Find the beginning of an escape sequence.
73 There are two cases:
74 1. If the sequence begins with an ESC character (0x1B) and a second
75 character X in [0x40,0x5F], returns X and stores a pointer to
76 the third character into *head.
77 2. If the sequence begins with a character X in [0x80,0x9F], returns
78 (X-0x40) and stores a pointer to the second character into *head.
79 Stores the number of ESC character(s) in *prefix_len.
80 Returns 0 if no such sequence can be found. */
81 static int
82 find_esc_head (int *prefix_len, const char **head, const char *str)
84 int c;
85 const char *r = str;
86 int escaped = 0;
88 for (;;)
90 c = (unsigned char) *r;
91 if (c == 0)
93 /* Not found. */
94 return 0;
96 if (escaped && 0x40 <= c && c <= 0x5F)
98 /* Found (case 1). */
99 *prefix_len = 2;
100 *head = r + 1;
101 return c;
103 if (0x80 <= c && c <= 0x9F)
105 /* Found (case 2). */
106 *prefix_len = 1;
107 *head = r + 1;
108 return c - 0x40;
110 ++r;
111 escaped = c == 0x1B;
115 /* Find the terminator of an escape sequence.
116 str should be the value stored in *head by a previous successful
117 call to find_esc_head().
118 Returns 0 if no such sequence can be found. */
119 static int
120 find_esc_terminator (const char **term, const char *str)
122 int c;
123 const char *r = str;
125 for (;;)
127 c = (unsigned char) *r;
128 if (c == 0)
130 /* Not found. */
131 return 0;
133 if (0x40 <= c && c <= 0x7E)
135 /* Found. */
136 *term = r;
137 return c;
139 ++r;
143 /* Handle a sequence of codes. Sequences that are invalid, reserved,
144 unrecognized or unimplemented are ignored silently.
145 There isn't much we can do because of lameness of Windows consoles. */
146 static void
147 eat_esc_sequence (HANDLE h, int esc_code,
148 const char *esc_head, const char *esc_term)
150 /* Numbers in an escape sequence cannot be negative, because
151 a minus sign in the middle of it would have terminated it. */
152 long n1, n2;
153 char *eptr, *delim;
154 CONSOLE_SCREEN_BUFFER_INFO sb;
155 COORD cr;
156 /* ED and EL parameters. */
157 DWORD cnt, step;
158 long rows;
159 /* SGR parameters. */
160 WORD attrib_add, attrib_rm;
161 const char *param;
163 switch (MAKEWORD (esc_code, *esc_term))
165 /* ESC [ n1 'A'
166 Move the cursor up by n1 characters. */
167 case MAKEWORD ('[', 'A'):
168 if (esc_head == esc_term)
169 n1 = 1;
170 else
172 n1 = strtol (esc_head, &eptr, 10);
173 if (eptr != esc_term)
174 break;
177 if (GetConsoleScreenBufferInfo (h, &sb))
179 cr = sb.dwCursorPosition;
180 /* Stop at the topmost boundary. */
181 if (cr.Y > n1)
182 cr.Y -= n1;
183 else
184 cr.Y = 0;
185 SetConsoleCursorPosition (h, cr);
187 break;
189 /* ESC [ n1 'B'
190 Move the cursor down by n1 characters. */
191 case MAKEWORD ('[', 'B'):
192 if (esc_head == esc_term)
193 n1 = 1;
194 else
196 n1 = strtol (esc_head, &eptr, 10);
197 if (eptr != esc_term)
198 break;
201 if (GetConsoleScreenBufferInfo (h, &sb))
203 cr = sb.dwCursorPosition;
204 /* Stop at the bottommost boundary. */
205 if (sb.dwSize.Y - cr.Y > n1)
206 cr.Y += n1;
207 else
208 cr.Y = sb.dwSize.Y;
209 SetConsoleCursorPosition (h, cr);
211 break;
213 /* ESC [ n1 'C'
214 Move the cursor right by n1 characters. */
215 case MAKEWORD ('[', 'C'):
216 if (esc_head == esc_term)
217 n1 = 1;
218 else
220 n1 = strtol (esc_head, &eptr, 10);
221 if (eptr != esc_term)
222 break;
225 if (GetConsoleScreenBufferInfo (h, &sb))
227 cr = sb.dwCursorPosition;
228 /* Stop at the rightmost boundary. */
229 if (sb.dwSize.X - cr.X > n1)
230 cr.X += n1;
231 else
232 cr.X = sb.dwSize.X;
233 SetConsoleCursorPosition (h, cr);
235 break;
237 /* ESC [ n1 'D'
238 Move the cursor left by n1 characters. */
239 case MAKEWORD ('[', 'D'):
240 if (esc_head == esc_term)
241 n1 = 1;
242 else
244 n1 = strtol (esc_head, &eptr, 10);
245 if (eptr != esc_term)
246 break;
249 if (GetConsoleScreenBufferInfo (h, &sb))
251 cr = sb.dwCursorPosition;
252 /* Stop at the leftmost boundary. */
253 if (cr.X > n1)
254 cr.X -= n1;
255 else
256 cr.X = 0;
257 SetConsoleCursorPosition (h, cr);
259 break;
261 /* ESC [ n1 'E'
262 Move the cursor to the beginning of the n1-th line downwards. */
263 case MAKEWORD ('[', 'E'):
264 if (esc_head == esc_term)
265 n1 = 1;
266 else
268 n1 = strtol (esc_head, &eptr, 10);
269 if (eptr != esc_term)
270 break;
273 if (GetConsoleScreenBufferInfo (h, &sb))
275 cr = sb.dwCursorPosition;
276 cr.X = 0;
277 /* Stop at the bottommost boundary. */
278 if (sb.dwSize.Y - cr.Y > n1)
279 cr.Y += n1;
280 else
281 cr.Y = sb.dwSize.Y;
282 SetConsoleCursorPosition (h, cr);
284 break;
286 /* ESC [ n1 'F'
287 Move the cursor to the beginning of the n1-th line upwards. */
288 case MAKEWORD ('[', 'F'):
289 if (esc_head == esc_term)
290 n1 = 1;
291 else
293 n1 = strtol (esc_head, &eptr, 10);
294 if (eptr != esc_term)
295 break;
298 if (GetConsoleScreenBufferInfo (h, &sb))
300 cr = sb.dwCursorPosition;
301 cr.X = 0;
302 /* Stop at the topmost boundary. */
303 if (cr.Y > n1)
304 cr.Y -= n1;
305 else
306 cr.Y = 0;
307 SetConsoleCursorPosition (h, cr);
309 break;
311 /* ESC [ n1 'G'
312 Move the cursor to the (1-based) n1-th column. */
313 case MAKEWORD ('[', 'G'):
314 if (esc_head == esc_term)
315 n1 = 1;
316 else
318 n1 = strtol (esc_head, &eptr, 10);
319 if (eptr != esc_term)
320 break;
323 if (GetConsoleScreenBufferInfo (h, &sb))
325 cr = sb.dwCursorPosition;
326 n1 -= 1;
327 /* Stop at the leftmost or rightmost boundary. */
328 if (n1 < 0)
329 cr.X = 0;
330 else if (n1 > sb.dwSize.X)
331 cr.X = sb.dwSize.X;
332 else
333 cr.X = n1;
334 SetConsoleCursorPosition (h, cr);
336 break;
338 /* ESC [ n1 ';' n2 'H'
339 ESC [ n1 ';' n2 'f'
340 Move the cursor to the (1-based) n1-th row and
341 (also 1-based) n2-th column. */
342 case MAKEWORD ('[', 'H'):
343 case MAKEWORD ('[', 'f'):
344 if (esc_head == esc_term)
346 /* Both parameters are omitted and set to 1 by default. */
347 n1 = 1;
348 n2 = 1;
350 else if (!(delim = (char *) memchr (esc_head, ';',
351 esc_term - esc_head)))
353 /* Only the first parameter is given. The second one is
354 set to 1 by default. */
355 n1 = strtol (esc_head, &eptr, 10);
356 if (eptr != esc_term)
357 break;
358 n2 = 1;
360 else
362 /* Both parameters are given. The first one shall be
363 terminated by the semicolon. */
364 n1 = strtol (esc_head, &eptr, 10);
365 if (eptr != delim)
366 break;
367 n2 = strtol (delim + 1, &eptr, 10);
368 if (eptr != esc_term)
369 break;
372 if (GetConsoleScreenBufferInfo (h, &sb))
374 cr = sb.dwCursorPosition;
375 n1 -= 1;
376 n2 -= 1;
377 /* The cursor position shall be relative to the view coord of
378 the console window, which is usually smaller than the actual
379 buffer. FWIW, the 'appropriate' solution will be shrinking
380 the buffer to match the size of the console window,
381 destroying scrollback in the process. */
382 n1 += sb.srWindow.Top;
383 n2 += sb.srWindow.Left;
384 /* Stop at the topmost or bottommost boundary. */
385 if (n1 < 0)
386 cr.Y = 0;
387 else if (n1 > sb.dwSize.Y)
388 cr.Y = sb.dwSize.Y;
389 else
390 cr.Y = n1;
391 /* Stop at the leftmost or rightmost boundary. */
392 if (n2 < 0)
393 cr.X = 0;
394 else if (n2 > sb.dwSize.X)
395 cr.X = sb.dwSize.X;
396 else
397 cr.X = n2;
398 SetConsoleCursorPosition (h, cr);
400 break;
402 /* ESC [ n1 'J'
403 Erase display. */
404 case MAKEWORD ('[', 'J'):
405 if (esc_head == esc_term)
406 /* This is one of the very few codes whose parameters have
407 a default value of zero. */
408 n1 = 0;
409 else
411 n1 = strtol (esc_head, &eptr, 10);
412 if (eptr != esc_term)
413 break;
416 if (GetConsoleScreenBufferInfo (h, &sb))
418 /* The cursor is not necessarily in the console window, which
419 makes the behavior of this code harder to define. */
420 switch (n1)
422 case 0:
423 /* If the cursor is in or above the window, erase from
424 it to the bottom of the window; otherwise, do nothing. */
425 cr = sb.dwCursorPosition;
426 cnt = sb.dwSize.X - sb.dwCursorPosition.X;
427 rows = sb.srWindow.Bottom - sb.dwCursorPosition.Y;
428 break;
429 case 1:
430 /* If the cursor is in or under the window, erase from
431 it to the top of the window; otherwise, do nothing. */
432 cr.X = 0;
433 cr.Y = sb.srWindow.Top;
434 cnt = sb.dwCursorPosition.X + 1;
435 rows = sb.dwCursorPosition.Y - sb.srWindow.Top;
436 break;
437 case 2:
438 /* Erase the entire window. */
439 cr.X = sb.srWindow.Left;
440 cr.Y = sb.srWindow.Top;
441 cnt = 0;
442 rows = sb.srWindow.Bottom - sb.srWindow.Top + 1;
443 break;
444 default:
445 /* Erase the entire buffer. */
446 cr.X = 0;
447 cr.Y = 0;
448 cnt = 0;
449 rows = sb.dwSize.Y;
450 break;
452 if (rows < 0)
453 break;
454 cnt += rows * sb.dwSize.X;
455 FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
456 FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
458 break;
460 /* ESC [ n1 'K'
461 Erase line. */
462 case MAKEWORD ('[', 'K'):
463 if (esc_head == esc_term)
464 /* This is one of the very few codes whose parameters have
465 a default value of zero. */
466 n1 = 0;
467 else
469 n1 = strtol (esc_head, &eptr, 10);
470 if (eptr != esc_term)
471 break;
474 if (GetConsoleScreenBufferInfo (h, &sb))
476 switch (n1)
478 case 0:
479 /* Erase from the cursor to the end. */
480 cr = sb.dwCursorPosition;
481 cnt = sb.dwSize.X - sb.dwCursorPosition.X;
482 break;
483 case 1:
484 /* Erase from the cursor to the beginning. */
485 cr = sb.dwCursorPosition;
486 cr.X = 0;
487 cnt = sb.dwCursorPosition.X + 1;
488 break;
489 default:
490 /* Erase the entire line. */
491 cr = sb.dwCursorPosition;
492 cr.X = 0;
493 cnt = sb.dwSize.X;
494 break;
496 FillConsoleOutputCharacterW (h, L' ', cnt, cr, &step);
497 FillConsoleOutputAttribute (h, sb.wAttributes, cnt, cr, &step);
499 break;
501 /* ESC [ n1 ';' n2 'm'
502 Set SGR parameters. Zero or more parameters will follow. */
503 case MAKEWORD ('[', 'm'):
504 attrib_add = 0;
505 attrib_rm = 0;
506 if (esc_head == esc_term)
508 /* When no parameter is given, reset the console. */
509 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
510 | FOREGROUND_BLUE);
511 attrib_rm = -1; /* Removes everything. */
512 goto sgr_set_it;
514 param = esc_head;
517 /* Parse a parameter. */
518 n1 = strtol (param, &eptr, 10);
519 if (*eptr != ';' && eptr != esc_term)
520 goto sgr_set_it;
522 switch (n1)
524 case 0:
525 /* Reset. */
526 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
527 | FOREGROUND_BLUE);
528 attrib_rm = -1; /* Removes everything. */
529 break;
530 case 1:
531 /* Bold. */
532 attrib_add |= FOREGROUND_INTENSITY;
533 break;
534 case 4:
535 /* Underline. */
536 attrib_add |= COMMON_LVB_UNDERSCORE;
537 break;
538 case 5:
539 /* Blink. */
540 /* XXX: It is not BLINKING at all! */
541 attrib_add |= BACKGROUND_INTENSITY;
542 break;
543 case 7:
544 /* Reverse. */
545 attrib_add |= COMMON_LVB_REVERSE_VIDEO;
546 break;
547 case 22:
548 /* No bold. */
549 attrib_add &= ~FOREGROUND_INTENSITY;
550 attrib_rm |= FOREGROUND_INTENSITY;
551 break;
552 case 24:
553 /* No underline. */
554 attrib_add &= ~COMMON_LVB_UNDERSCORE;
555 attrib_rm |= COMMON_LVB_UNDERSCORE;
556 break;
557 case 25:
558 /* No blink. */
559 /* XXX: It is not BLINKING at all! */
560 attrib_add &= ~BACKGROUND_INTENSITY;
561 attrib_rm |= BACKGROUND_INTENSITY;
562 break;
563 case 27:
564 /* No reverse. */
565 attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
566 attrib_rm |= COMMON_LVB_REVERSE_VIDEO;
567 break;
568 case 30:
569 case 31:
570 case 32:
571 case 33:
572 case 34:
573 case 35:
574 case 36:
575 case 37:
576 /* Foreground color. */
577 attrib_add &= ~(FOREGROUND_RED | FOREGROUND_GREEN
578 | FOREGROUND_BLUE);
579 n1 -= 30;
580 if (n1 & 1)
581 attrib_add |= FOREGROUND_RED;
582 if (n1 & 2)
583 attrib_add |= FOREGROUND_GREEN;
584 if (n1 & 4)
585 attrib_add |= FOREGROUND_BLUE;
586 attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
587 | FOREGROUND_BLUE);
588 break;
589 case 38:
590 /* Reserved for extended foreground color.
591 Don't know how to handle parameters remaining.
592 Bail out. */
593 goto sgr_set_it;
594 case 39:
595 /* Reset foreground color. */
596 /* Set to grey. */
597 attrib_add |= (FOREGROUND_RED | FOREGROUND_GREEN
598 | FOREGROUND_BLUE);
599 attrib_rm |= (FOREGROUND_RED | FOREGROUND_GREEN
600 | FOREGROUND_BLUE);
601 break;
602 case 40:
603 case 41:
604 case 42:
605 case 43:
606 case 44:
607 case 45:
608 case 46:
609 case 47:
610 /* Background color. */
611 attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
612 | BACKGROUND_BLUE);
613 n1 -= 40;
614 if (n1 & 1)
615 attrib_add |= BACKGROUND_RED;
616 if (n1 & 2)
617 attrib_add |= BACKGROUND_GREEN;
618 if (n1 & 4)
619 attrib_add |= BACKGROUND_BLUE;
620 attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
621 | BACKGROUND_BLUE);
622 break;
623 case 48:
624 /* Reserved for extended background color.
625 Don't know how to handle parameters remaining.
626 Bail out. */
627 goto sgr_set_it;
628 case 49:
629 /* Reset background color. */
630 /* Set to black. */
631 attrib_add &= ~(BACKGROUND_RED | BACKGROUND_GREEN
632 | BACKGROUND_BLUE);
633 attrib_rm |= (BACKGROUND_RED | BACKGROUND_GREEN
634 | BACKGROUND_BLUE);
635 break;
638 /* Prepare the next parameter. */
639 param = eptr + 1;
641 while (param != esc_term);
643 sgr_set_it:
644 /* 0xFFFF removes everything. If it is not the case,
645 care must be taken to preserve old attributes. */
646 if (attrib_rm != 0xFFFF && GetConsoleScreenBufferInfo (h, &sb))
648 attrib_add |= sb.wAttributes & ~attrib_rm;
650 if (attrib_add & COMMON_LVB_REVERSE_VIDEO)
652 /* COMMON_LVB_REVERSE_VIDEO is only effective for DBCS.
653 * Swap foreground and background colors by hand.
655 attrib_add = (attrib_add & 0xFF00)
656 | ((attrib_add & 0x00F0) >> 4)
657 | ((attrib_add & 0x000F) << 4);
658 attrib_add &= ~COMMON_LVB_REVERSE_VIDEO;
660 SetConsoleTextAttribute (h, attrib_add);
661 break;
666 mingw_ansi_fputs (const char *str, FILE *fp)
668 const char *read = str;
669 HANDLE h;
670 DWORD mode;
671 int esc_code, prefix_len;
672 const char *esc_head, *esc_term;
674 h = (HANDLE) _get_osfhandle (_fileno (fp));
675 if (h == INVALID_HANDLE_VALUE)
676 return EOF;
678 /* Don't mess up stdio functions with Windows APIs. */
679 fflush (fp);
681 if (GetConsoleMode (h, &mode)
682 #ifdef ENABLE_VIRTUAL_TERMINAL_PROCESSING
683 && !(mode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)
684 #endif
686 /* If it is a console, and doesn't support ANSI escape codes, translate
687 them as needed. */
688 for (;;)
690 if ((esc_code = find_esc_head (&prefix_len, &esc_head, read)) == 0)
692 /* Write all remaining characters, then exit. */
693 write_all (h, read, strlen (read));
694 break;
696 if (find_esc_terminator (&esc_term, esc_head) == 0)
697 /* Ignore incomplete escape sequences at the moment.
698 FIXME: The escape state shall be cached for further calls
699 to this function. */
700 break;
701 write_all (h, read, esc_head - prefix_len - read);
702 eat_esc_sequence (h, esc_code, esc_head, esc_term);
703 read = esc_term + 1;
705 else
706 /* If it is not a console, write everything as-is. */
707 write_all (h, read, strlen (read));
709 return 1;
712 #endif /* __MINGW32__ */
714 static int
715 decode_utf8_char (const unsigned char *, size_t len, unsigned int *);
716 static void pp_quoted_string (pretty_printer *, const char *, size_t = -1);
718 static void
719 default_token_printer (pretty_printer *pp,
720 const pp_token_list &tokens);
722 /* Overwrite the given location/range within this text_info's rich_location.
723 For use e.g. when implementing "+" in client format decoders. */
725 void
726 text_info::set_location (unsigned int idx, location_t loc,
727 enum range_display_kind range_display_kind)
729 gcc_checking_assert (m_richloc);
730 m_richloc->set_range (idx, loc, range_display_kind);
733 location_t
734 text_info::get_location (unsigned int index_of_location) const
736 gcc_checking_assert (m_richloc);
738 if (index_of_location == 0)
739 return m_richloc->get_loc ();
740 else
741 return UNKNOWN_LOCATION;
744 // Default construct an output buffer.
746 output_buffer::output_buffer ()
747 : m_formatted_obstack (),
748 m_chunk_obstack (),
749 m_obstack (&m_formatted_obstack),
750 m_cur_formatted_chunks (nullptr),
751 m_stream (stderr),
752 m_line_length (),
753 m_digit_buffer (),
754 m_flush_p (true)
756 obstack_init (&m_formatted_obstack);
757 obstack_init (&m_chunk_obstack);
760 // Release resources owned by an output buffer at the end of lifetime.
762 output_buffer::~output_buffer ()
764 obstack_free (&m_chunk_obstack, NULL);
765 obstack_free (&m_formatted_obstack, NULL);
768 /* Allocate a new pp_formatted_chunks from chunk_obstack and push
769 it onto this buffer's stack.
770 This represents the result of phases 1 and 2 of formatting. */
772 pp_formatted_chunks *
773 output_buffer::push_formatted_chunks ()
775 /* Allocate a new chunk structure. */
776 pp_formatted_chunks *new_chunk_array
777 = XOBNEW (&m_chunk_obstack, pp_formatted_chunks);
778 new_chunk_array->m_prev = m_cur_formatted_chunks;
779 m_cur_formatted_chunks = new_chunk_array;
780 return new_chunk_array;
783 /* Deallocate the current pp_formatted_chunks structure and everything after it
784 (i.e. the associated series of formatted strings, pp_token_lists, and
785 pp_tokens). */
787 void
788 output_buffer::pop_formatted_chunks ()
790 pp_formatted_chunks *old_top = m_cur_formatted_chunks;
791 gcc_assert (old_top);
792 m_cur_formatted_chunks = old_top->m_prev;
793 obstack_free (&m_chunk_obstack, old_top);
796 static const int bytes_per_hexdump_line = 16;
798 static void
799 print_hexdump_line (FILE *out, int indent,
800 const void *buf, size_t size, size_t line_start_idx)
802 fprintf (out, "%*s%08lx: ", indent, "", (unsigned long)line_start_idx);
803 for (size_t offset = 0; offset < bytes_per_hexdump_line; ++offset)
805 const size_t idx = line_start_idx + offset;
806 if (idx < size)
807 fprintf (out, "%02x ", ((const unsigned char *)buf)[idx]);
808 else
809 fprintf (out, " ");
811 fprintf (out, "| ");
812 for (size_t offset = 0; offset < bytes_per_hexdump_line; ++offset)
814 const size_t idx = line_start_idx + offset;
815 if (idx < size)
817 unsigned char ch = ((const unsigned char *)buf)[idx];
818 if (!ISPRINT (ch))
819 ch = '.';
820 fputc (ch, out);
822 else
823 break;
825 fprintf (out, "\n");
829 static void
830 print_hexdump (FILE *out, int indent, const void *buf, size_t size)
832 for (size_t idx = 0; idx < size; idx += bytes_per_hexdump_line)
833 print_hexdump_line (out, indent, buf, size, idx);
836 /* Dump state of this output_buffer to OUT, for debugging. */
838 void
839 output_buffer::dump (FILE *out, int indent) const
842 size_t obj_size = obstack_object_size (&m_formatted_obstack);
843 fprintf (out, "%*sm_formatted_obstack current object: length %li:\n",
844 indent, "", (long)obj_size);
845 print_hexdump (out, indent + 2,
846 m_formatted_obstack.object_base, obj_size);
849 size_t obj_size = obstack_object_size (&m_chunk_obstack);
850 fprintf (out, "%*sm_chunk_obstack current object: length %li:\n",
851 indent, "", (long)obj_size);
852 print_hexdump (out, indent + 2,
853 m_chunk_obstack.object_base, obj_size);
856 int depth = 0;
857 for (pp_formatted_chunks *iter = m_cur_formatted_chunks;
858 iter;
859 iter = iter->m_prev, depth++)
861 fprintf (out, "%*spp_formatted_chunks: depth %i\n",
862 indent, "",
863 depth);
864 iter->dump (out, indent + 2);
868 #ifndef PTRDIFF_MAX
869 #define PTRDIFF_MAX INTTYPE_MAXIMUM (ptrdiff_t)
870 #endif
872 /* Format an integer given by va_arg (ARG, type-specifier T) where
873 type-specifier is a precision modifier as indicated by PREC. F is
874 a string used to construct the appropriate format-specifier. */
875 #define pp_integer_with_precision(PP, ARG, PREC, T, F) \
876 do \
877 switch (PREC) \
879 case 0: \
880 pp_scalar (PP, "%" F, va_arg (ARG, T)); \
881 break; \
883 case 1: \
884 pp_scalar (PP, "%l" F, va_arg (ARG, long T)); \
885 break; \
887 case 2: \
888 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
889 va_arg (ARG, long long T)); \
890 break; \
892 case 3: \
893 if (T (-1) < T (0)) \
894 pp_scalar (PP, "%" GCC_PRISZ F, \
895 (fmt_size_t) va_arg (ARG, ssize_t)); \
896 else \
897 pp_scalar (PP, "%" GCC_PRISZ F, \
898 (fmt_size_t) va_arg (ARG, size_t)); \
899 break; \
901 case 4: \
902 if (T (-1) >= T (0)) \
904 unsigned long long a = va_arg (ARG, ptrdiff_t); \
905 unsigned long long m = PTRDIFF_MAX; \
906 m = 2 * m + 1; \
907 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
908 a & m); \
910 else if (sizeof (ptrdiff_t) <= sizeof (int)) \
911 pp_scalar (PP, "%" F, \
912 (int) va_arg (ARG, ptrdiff_t)); \
913 else if (sizeof (ptrdiff_t) <= sizeof (long)) \
914 pp_scalar (PP, "%l" F, \
915 (long int) va_arg (ARG, ptrdiff_t)); \
916 else \
917 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
918 (long long int) \
919 va_arg (ARG, ptrdiff_t)); \
920 break; \
922 default: \
923 break; \
925 while (0)
928 /* Subroutine of pp_set_maximum_length. Set up PRETTY-PRINTER's
929 internal maximum characters per line. */
931 void
932 pretty_printer::set_real_maximum_length ()
934 /* If we're told not to wrap lines then do the obvious thing. In case
935 we'll emit prefix only once per message, it is appropriate
936 not to increase unnecessarily the line-length cut-off. */
937 if (!pp_is_wrapping_line (this)
938 || pp_prefixing_rule (this) == DIAGNOSTICS_SHOW_PREFIX_ONCE
939 || pp_prefixing_rule (this) == DIAGNOSTICS_SHOW_PREFIX_NEVER)
940 m_maximum_length = pp_line_cutoff (this);
941 else
943 int prefix_length = m_prefix ? strlen (m_prefix) : 0;
944 /* If the prefix is ridiculously too long, output at least
945 32 characters. */
946 if (pp_line_cutoff (this) - prefix_length < 32)
947 m_maximum_length = pp_line_cutoff (this) + 32;
948 else
949 m_maximum_length = pp_line_cutoff (this);
953 /* Clear this pretty_printer's output state. */
954 inline void
955 pretty_printer::clear_state ()
957 m_emitted_prefix = false;
958 pp_indentation (this) = 0;
961 /* Print X to PP in decimal. */
962 template<unsigned int N, typename T>
963 void
964 pp_wide_integer (pretty_printer *pp, const poly_int<N, T> &x)
966 if (x.is_constant ())
967 pp_wide_integer (pp, x.coeffs[0]);
968 else
970 pp_left_bracket (pp);
971 for (unsigned int i = 0; i < N; ++i)
973 if (i != 0)
974 pp_comma (pp);
975 pp_wide_integer (pp, x.coeffs[i]);
977 pp_right_bracket (pp);
981 template void pp_wide_integer (pretty_printer *, const poly_uint16 &);
982 template void pp_wide_integer (pretty_printer *, const poly_int64 &);
983 template void pp_wide_integer (pretty_printer *, const poly_uint64 &);
985 /* Flush the formatted text of PRETTY-PRINTER onto the attached stream. */
986 void
987 pp_write_text_to_stream (pretty_printer *pp)
989 const char *text = pp_formatted_text (pp);
990 #ifdef __MINGW32__
991 mingw_ansi_fputs (text, pp_buffer (pp)->m_stream);
992 #else
993 fputs (text, pp_buffer (pp)->m_stream);
994 #endif
995 pp_clear_output_area (pp);
998 /* As pp_write_text_to_stream, but for GraphViz label output.
1000 Flush the formatted text of pretty-printer PP onto the attached stream.
1001 Replace characters in PPF that have special meaning in a GraphViz .dot
1002 file.
1004 This routine is not very fast, but it doesn't have to be as this is only
1005 be used by routines dumping intermediate representations in graph form. */
1007 void
1008 pp_write_text_as_dot_label_to_stream (pretty_printer *pp, bool for_record)
1010 const char *text = pp_formatted_text (pp);
1011 const char *p = text;
1012 FILE *fp = pp_buffer (pp)->m_stream;
1014 for (;*p; p++)
1016 bool escape_char;
1017 switch (*p)
1019 /* Print newlines as a left-aligned newline. */
1020 case '\n':
1021 fputs ("\\l", fp);
1022 escape_char = true;
1023 break;
1025 /* The following characters are only special for record-shape nodes. */
1026 case '|':
1027 case '{':
1028 case '}':
1029 case '<':
1030 case '>':
1031 case ' ':
1032 escape_char = for_record;
1033 break;
1035 /* The following characters always have to be escaped
1036 for use in labels. */
1037 case '\\':
1038 /* There is a bug in some (f.i. 2.36.0) versions of graphiz
1039 ( http://www.graphviz.org/mantisbt/view.php?id=2524 ) related to
1040 backslash as last char in label. Let's avoid triggering it. */
1041 gcc_assert (*(p + 1) != '\0');
1042 /* Fall through. */
1043 case '"':
1044 escape_char = true;
1045 break;
1047 default:
1048 escape_char = false;
1049 break;
1052 if (escape_char)
1053 fputc ('\\', fp);
1055 fputc (*p, fp);
1058 pp_clear_output_area (pp);
1061 /* As pp_write_text_to_stream, but for GraphViz HTML-like strings.
1063 Flush the formatted text of pretty-printer PP onto the attached stream,
1064 escaping these characters
1065 " & < >
1066 using XML escape sequences.
1068 http://www.graphviz.org/doc/info/lang.html#html states:
1069 special XML escape sequences for ", &, <, and > may be necessary in
1070 order to embed these characters in attribute values or raw text
1071 This doesn't list "'" (which would normally be escaped in XML
1072 as "&apos;" or in HTML as "&#39;");.
1074 Experiments show that escaping "'" doesn't seem to be necessary. */
1076 void
1077 pp_write_text_as_html_like_dot_to_stream (pretty_printer *pp)
1079 const char *text = pp_formatted_text (pp);
1080 const char *p = text;
1081 FILE *fp = pp_buffer (pp)->m_stream;
1083 for (;*p; p++)
1085 switch (*p)
1087 case '"':
1088 fputs ("&quot;", fp);
1089 break;
1090 case '&':
1091 fputs ("&amp;", fp);
1092 break;
1093 case '<':
1094 fputs ("&lt;", fp);
1095 break;
1096 case '>':
1097 fputs ("&gt;",fp);
1098 break;
1100 default:
1101 fputc (*p, fp);
1102 break;
1106 pp_clear_output_area (pp);
1109 /* Wrap a text delimited by START and END into PRETTY-PRINTER. */
1110 static void
1111 pp_wrap_text (pretty_printer *pp, const char *start, const char *end)
1113 bool wrapping_line = pp_is_wrapping_line (pp);
1115 while (start != end)
1117 /* Dump anything bordered by whitespaces. */
1119 const char *p = start;
1120 while (p != end && !ISBLANK (*p) && *p != '\n')
1121 ++p;
1122 if (wrapping_line
1123 && p - start >= pp->remaining_character_count_for_line ())
1124 pp_newline (pp);
1125 pp_append_text (pp, start, p);
1126 start = p;
1129 if (start != end && ISBLANK (*start))
1131 pp_space (pp);
1132 ++start;
1134 if (start != end && *start == '\n')
1136 pp_newline (pp);
1137 ++start;
1142 /* Same as pp_wrap_text but wrap text only when in line-wrapping mode. */
1143 static inline void
1144 pp_maybe_wrap_text (pretty_printer *pp, const char *start, const char *end)
1146 if (pp_is_wrapping_line (pp))
1147 pp_wrap_text (pp, start, end);
1148 else
1149 pp_append_text (pp, start, end);
1152 /* Append to the output area of PRETTY-PRINTER a string specified by its
1153 STARTing character and LENGTH. */
1154 static inline void
1155 pp_append_r (pretty_printer *pp, const char *start, int length)
1157 output_buffer_append_r (pp_buffer (pp), start, length);
1160 /* Insert enough spaces into the output area of PRETTY-PRINTER to bring
1161 the column position to the current indentation level, assuming that a
1162 newline has just been written to the buffer. */
1163 void
1164 pp_indent (pretty_printer *pp)
1166 int n = pp_indentation (pp);
1167 int i;
1169 for (i = 0; i < n; ++i)
1170 pp_space (pp);
1173 static const char *get_end_url_string (pretty_printer *);
1175 /* struct pp_token. */
1177 pp_token::pp_token (enum kind k)
1178 : m_kind (k),
1179 m_prev (nullptr),
1180 m_next (nullptr)
1184 void
1185 pp_token::dump (FILE *out) const
1187 switch (m_kind)
1189 default:
1190 gcc_unreachable ();
1191 case kind::text:
1193 const pp_token_text *sub = as_a <const pp_token_text *> (this);
1194 gcc_assert (sub->m_value.get ());
1195 fprintf (out, "TEXT(\"%s\")", sub->m_value.get ());
1197 break;
1198 case kind::begin_color:
1200 const pp_token_begin_color *sub
1201 = as_a <const pp_token_begin_color *> (this);
1202 gcc_assert (sub->m_value.get ());
1203 fprintf (out, "BEGIN_COLOR(\"%s\")", sub->m_value.get ());
1204 break;
1206 case kind::end_color:
1207 fprintf (out, "END_COLOR");
1208 break;
1209 case kind::begin_quote:
1210 fprintf (out, "BEGIN_QUOTE");
1211 break;
1212 case kind::end_quote:
1213 fprintf (out, "END_QUOTE");
1214 break;
1215 case kind::begin_url:
1217 const pp_token_begin_url *sub
1218 = as_a <const pp_token_begin_url *> (this);
1219 gcc_assert (sub->m_value.get ());
1220 fprintf (out, "BEGIN_URL(\"%s\")", sub->m_value.get ());
1222 break;
1223 case kind::end_url:
1224 fprintf (out, "END_URL");
1225 break;
1227 case kind::event_id:
1229 const pp_token_event_id *sub
1230 = as_a <const pp_token_event_id *> (this);
1231 gcc_assert (sub->m_event_id.known_p ());
1232 fprintf (out, "EVENT((%i))", sub->m_event_id.one_based ());
1234 break;
1236 case kind::custom_data:
1238 const pp_token_custom_data *sub
1239 = as_a <const pp_token_custom_data *> (this);
1240 gcc_assert (sub->m_value.get ());
1241 fprintf (out, "CUSTOM(");
1242 sub->m_value->dump (out);
1243 fprintf (out, ")");
1245 break;
1249 /* Allocate SZ bytes within S, which must not be half-way through
1250 building another object. */
1252 static void *
1253 allocate_object (size_t sz, obstack &s)
1255 /* We must not be half-way through an object. */
1256 gcc_assert (obstack_base (&s) == obstack_next_free (&s));
1258 obstack_blank (&s, sz);
1259 void *buf = obstack_finish (&s);
1260 return buf;
1263 /* Make room for a pp_token instance within obstack S. */
1265 void *
1266 pp_token::operator new (size_t sz, obstack &s)
1268 return allocate_object (sz, s);
1271 void
1272 pp_token::operator delete (void *)
1274 /* No-op: pp_tokens are allocated within obstacks, so
1275 the memory will be reclaimed when the obstack is freed. */
1278 /* class pp_token_list. */
1280 /* Make room for a pp_token_list instance within obstack S. */
1282 void *
1283 pp_token_list::operator new (size_t sz, obstack &s)
1285 return allocate_object (sz, s);
1288 void
1289 pp_token_list::operator delete (void *)
1291 /* No-op: pp_token_list allocated within obstacks don't
1292 need their own reclaim the memory will be reclaimed when
1293 the obstack is freed. */
1296 pp_token_list::pp_token_list (obstack &s)
1297 : m_obstack (s),
1298 m_first (nullptr),
1299 m_end (nullptr)
1303 pp_token_list::pp_token_list (pp_token_list &&other)
1304 : m_obstack (other.m_obstack),
1305 m_first (other.m_first),
1306 m_end (other.m_end)
1308 other.m_first = nullptr;
1309 other.m_end = nullptr;
1312 pp_token_list::~pp_token_list ()
1314 for (auto iter = m_first; iter; )
1316 pp_token *next = iter->m_next;
1317 delete iter;
1318 iter = next;
1322 void
1323 pp_token_list::push_back_text (label_text &&text)
1325 if (text.get ()[0] == '\0')
1326 return; // pushing empty string is a no-op
1327 push_back<pp_token_text> (std::move (text));
1330 void
1331 pp_token_list::push_back (std::unique_ptr<pp_token> tok)
1333 if (!m_first)
1335 gcc_assert (m_end == nullptr);
1336 m_first = tok.get ();
1337 m_end = tok.get ();
1339 else
1341 gcc_assert (m_end != nullptr);
1342 m_end->m_next = tok.get ();
1343 tok->m_prev = m_end;
1344 m_end = tok.get ();
1346 tok.release ();
1349 void
1350 pp_token_list::push_back_list (pp_token_list &&list)
1352 while (auto tok = list.pop_front ())
1353 push_back (std::move (tok));
1356 std::unique_ptr<pp_token>
1357 pp_token_list::pop_front ()
1359 pp_token *result = m_first;
1360 if (result == nullptr)
1361 return nullptr;
1363 gcc_assert (result->m_prev == nullptr);
1364 m_first = result->m_next;
1365 if (result->m_next)
1367 gcc_assert (result != m_end);
1368 m_first->m_prev = nullptr;
1370 else
1372 gcc_assert (result == m_end);
1373 m_end = nullptr;
1375 result->m_next = nullptr;
1376 return std::unique_ptr<pp_token> (result);
1379 std::unique_ptr<pp_token>
1380 pp_token_list::remove_token (pp_token *tok)
1382 gcc_assert (tok);
1383 if (tok->m_prev)
1385 gcc_assert (tok != m_first);
1386 tok->m_prev->m_next = tok->m_next;
1388 else
1390 gcc_assert (tok == m_first);
1391 m_first = tok->m_next;
1393 if (tok->m_next)
1395 gcc_assert (tok != m_end);
1396 tok->m_next->m_prev = tok->m_prev;
1398 else
1400 gcc_assert (tok == m_end);
1401 m_end = tok->m_prev;
1403 tok->m_prev = nullptr;
1404 tok->m_next = nullptr;
1405 gcc_assert (m_first != tok);
1406 gcc_assert (m_end != tok);
1407 return std::unique_ptr<pp_token> (tok);
1410 /* Insert NEW_TOK after RELATIVE_TOK. */
1412 void
1413 pp_token_list::insert_after (std::unique_ptr<pp_token> new_tok_up,
1414 pp_token *relative_tok)
1416 pp_token *new_tok = new_tok_up.release ();
1418 gcc_assert (new_tok);
1419 gcc_assert (new_tok->m_prev == nullptr);
1420 gcc_assert (new_tok->m_next == nullptr);
1421 gcc_assert (relative_tok);
1423 if (relative_tok->m_next)
1425 gcc_assert (relative_tok != m_end);
1426 relative_tok->m_next->m_prev = new_tok;
1428 else
1430 gcc_assert (relative_tok == m_end);
1431 m_end = new_tok;
1433 new_tok->m_prev = relative_tok;
1434 new_tok->m_next = relative_tok->m_next;
1435 relative_tok->m_next = new_tok;
1438 void
1439 pp_token_list::replace_custom_tokens ()
1441 pp_token *iter = m_first;
1442 while (iter)
1444 pp_token *next = iter->m_next;
1445 if (iter->m_kind == pp_token::kind::custom_data)
1447 pp_token_list tok_list (m_obstack);
1448 pp_token_custom_data *sub = as_a <pp_token_custom_data *> (iter);
1449 if (sub->m_value->as_standard_tokens (tok_list))
1451 while (auto tok = tok_list.pop_front ())
1453 /* The resulting token list must not contain any
1454 custom data. */
1455 gcc_assert (tok->m_kind != pp_token::kind::custom_data);
1456 insert_after (std::move (tok), iter);
1458 remove_token (iter);
1461 iter = next;
1465 /* Merge any runs of consecutive text tokens within this list
1466 into individual text tokens. */
1468 void
1469 pp_token_list::merge_consecutive_text_tokens ()
1471 pp_token *start_of_run = m_first;
1472 while (start_of_run)
1474 if (start_of_run->m_kind != pp_token::kind::text)
1476 start_of_run = start_of_run->m_next;
1477 continue;
1479 pp_token *end_of_run = start_of_run;
1480 while (end_of_run->m_next
1481 && end_of_run->m_next->m_kind == pp_token::kind::text)
1482 end_of_run = end_of_run->m_next;
1483 if (end_of_run != start_of_run)
1485 /* start_of_run through end_of_run are a run of consecutive
1486 text tokens. */
1488 /* Calculate size of buffer for merged text. */
1489 size_t sz = 0;
1490 for (auto iter = start_of_run; iter != end_of_run->m_next;
1491 iter = iter->m_next)
1493 pp_token_text *iter_text = static_cast<pp_token_text *> (iter);
1494 sz += strlen (iter_text->m_value.get ());
1497 /* Allocate and populate buffer for merged text
1498 (within m_obstack). */
1499 char * const buf = (char *)allocate_object (sz + 1, m_obstack);
1500 char *p = buf;
1501 for (auto iter = start_of_run; iter != end_of_run->m_next;
1502 iter = iter->m_next)
1504 pp_token_text *iter_text = static_cast<pp_token_text *> (iter);
1505 size_t iter_sz = strlen (iter_text->m_value.get ());
1506 memcpy (p, iter_text->m_value.get (), iter_sz);
1507 p += iter_sz;
1509 *p = '\0';
1511 /* Replace start_of_run's buffer pointer with the new buffer. */
1512 static_cast<pp_token_text *> (start_of_run)->m_value
1513 = label_text::borrow (buf);
1515 /* Remove all the other text tokens in the run. */
1516 pp_token * const next = end_of_run->m_next;
1517 while (start_of_run->m_next != next)
1518 remove_token (start_of_run->m_next);
1519 start_of_run = next;
1521 else
1522 start_of_run = end_of_run->m_next;
1526 /* Apply URLIFIER to this token list.
1527 Find BEGIN_QUOTE, TEXT, END_QUOTE triples, and if URLIFIER has a url
1528 for the value of TEXT, then wrap TEXT in a {BEGIN,END}_URL pair. */
1530 void
1531 pp_token_list::apply_urlifier (const urlifier &urlifier)
1533 for (pp_token *iter = m_first; iter; )
1535 if (iter->m_kind == pp_token::kind::begin_quote
1536 && iter->m_next
1537 && iter->m_next->m_kind == pp_token::kind::text
1538 && iter->m_next->m_next
1539 && iter->m_next->m_next->m_kind == pp_token::kind::end_quote)
1541 pp_token *begin_quote = iter;
1542 pp_token_text *text = as_a <pp_token_text *> (begin_quote->m_next);
1543 pp_token *end_quote = text->m_next;
1544 if (char *url = urlifier.get_url_for_quoted_text
1545 (text->m_value.get (),
1546 strlen (text->m_value.get ())))
1548 auto begin_url
1549 = make_token<pp_token_begin_url> (label_text::take (url));
1550 auto end_url = make_token<pp_token_end_url> ();
1551 insert_after (std::move (begin_url), begin_quote);
1552 insert_after (std::move (end_url), text);
1554 iter = end_quote->m_next;
1556 else
1557 iter = iter->m_next;
1561 void
1562 pp_token_list::dump (FILE *out) const
1564 for (auto iter = m_first; iter; iter = iter->m_next)
1566 iter->dump (out);
1567 if (iter->m_next)
1568 fprintf (out, ", ");
1570 fprintf (out, "]\n");
1574 /* Adds a chunk to the end of formatted output, so that it
1575 will be printed by pp_output_formatted_text. */
1577 void
1578 pp_formatted_chunks::append_formatted_chunk (obstack &s, const char *content)
1580 unsigned int chunk_idx;
1581 for (chunk_idx = 0; m_args[chunk_idx]; chunk_idx++)
1583 pp_token_list *tokens = pp_token_list::make (s);
1584 tokens->push_back_text (label_text::borrow (content));
1585 m_args[chunk_idx++] = tokens;
1586 m_args[chunk_idx] = nullptr;
1589 void
1590 pp_formatted_chunks::dump (FILE *out, int indent) const
1592 for (size_t idx = 0; m_args[idx]; ++idx)
1594 fprintf (out, "%*s%i: ",
1595 indent, "",
1596 (int)idx);
1597 m_args[idx]->dump (out);
1601 /* Finish any text accumulating within CUR_OBSTACK,
1602 terminating it.
1603 Push a text pp_token to the end of TOK_LIST containing
1604 a borrowed copy of the text in CUR_OBSTACK. */
1606 static void
1607 push_back_any_text (pp_token_list *tok_list,
1608 obstack *cur_obstack)
1610 obstack_1grow (cur_obstack, '\0');
1611 tok_list->push_back_text
1612 (label_text::borrow (XOBFINISH (cur_obstack,
1613 const char *)));
1616 /* The following format specifiers are recognized as being client independent:
1617 %d, %i: (signed) integer in base ten.
1618 %u: unsigned integer in base ten.
1619 %o: unsigned integer in base eight.
1620 %x: unsigned integer in base sixteen.
1621 %ld, %li, %lo, %lu, %lx: long versions of the above.
1622 %lld, %lli, %llo, %llu, %llx: long long versions.
1623 %wd, %wi, %wo, %wu, %wx: HOST_WIDE_INT versions.
1624 %zd, %zi, %zo, %zu, %zx: size_t versions.
1625 %td, %ti, %to, %tu, %tx: ptrdiff_t versions.
1626 %f: double
1627 %c: character.
1628 %s: string.
1629 %p: pointer (printed in a host-dependent manner).
1630 %r: if pp_show_color(pp), switch to color identified by const char *.
1631 %R: if pp_show_color(pp), reset color.
1632 %m: strerror(text->err_no) - does not consume a value from args_ptr.
1633 %%: '%'.
1634 %<: opening quote.
1635 %>: closing quote.
1636 %{: URL start. Consumes a const char * argument for the URL.
1637 %}: URL end. Does not consume any arguments.
1638 %': apostrophe (should only be used in untranslated messages;
1639 translations should use appropriate punctuation directly).
1640 %@: diagnostic_event_id_ptr, for which event_id->known_p () must be true.
1641 %.*s: a substring the length of which is specified by an argument
1642 integer.
1643 %Ns: likewise, but length specified as constant in the format string.
1644 Flag 'q': quote formatted text (must come immediately after '%').
1645 %Z: Requires two arguments - array of int, and len. Prints elements
1646 of the array.
1648 %e: Consumes a pp_element * argument.
1650 Arguments can be used sequentially, or through %N$ resp. *N$
1651 notation Nth argument after the format string. If %N$ / *N$
1652 notation is used, it must be used for all arguments, except %m, %%,
1653 %<, %>, %} and %', which may not have a number, as they do not consume
1654 an argument. When %M$.*N$s is used, M must be N + 1. (This may
1655 also be written %M$.*s, provided N is not otherwise used.) The
1656 format string must have conversion specifiers with argument numbers
1657 1 up to highest argument; each argument may only be used once.
1658 A format string can have at most 30 arguments. */
1660 /* Implementation of pp_format.
1661 Formatting phases 1 and 2:
1662 - push a pp_formatted_chunks instance.
1663 - render TEXT->format_spec plus text->m_args_ptr into the pp_formatted_chunks
1664 instance as pp_token_lists.
1665 Phase 3 is in pp_output_formatted_text, which pops the pp_formatted_chunks
1666 instance. */
1668 static void
1669 format_phase_1 (const text_info &text,
1670 obstack &chunk_obstack,
1671 pp_token_list **args,
1672 pp_token_list ***formatters);
1674 static void
1675 format_phase_2 (pretty_printer *pp,
1676 text_info &text,
1677 obstack &chunk_obstack,
1678 pp_token_list ***formatters);
1680 void
1681 pretty_printer::format (text_info &text)
1683 pp_formatted_chunks *new_chunk_array = m_buffer->push_formatted_chunks ();
1684 pp_token_list **args = new_chunk_array->m_args;
1686 pp_token_list **formatters[PP_NL_ARGMAX];
1687 memset (formatters, 0, sizeof formatters);
1689 /* Formatting phase 1: split up TEXT->format_spec into chunks in
1690 pp_buffer (PP)->args[]. Even-numbered chunks are to be output
1691 verbatim, odd-numbered chunks are format specifiers.
1692 %m, %%, %<, %>, %} and %' are replaced with the appropriate text at
1693 this point. */
1694 format_phase_1 (text, m_buffer->m_chunk_obstack, args, formatters);
1696 /* Note that you can debug the state of the chunk arrays here using
1697 (gdb) call m_buffer->cur_chunk_array->dump()
1698 which, given e.g. "foo: %s bar: %s" might print:
1699 0: [TEXT("foo: ")]
1700 1: [TEXT("s")]
1701 2: [TEXT(" bar: ")]
1702 3: [TEXT("s")]
1705 /* Set output to the argument obstack, and switch line-wrapping and
1706 prefixing off. */
1707 m_buffer->m_obstack = &m_buffer->m_chunk_obstack;
1708 const int old_line_length = m_buffer->m_line_length;
1709 const pp_wrapping_mode_t old_wrapping_mode = pp_set_verbatim_wrapping (this);
1711 format_phase_2 (this, text, m_buffer->m_chunk_obstack, formatters);
1713 /* If the client supplied a postprocessing object, call its "handle"
1714 hook here. */
1715 if (m_format_postprocessor)
1716 m_format_postprocessor->handle (this);
1718 /* Revert to normal obstack and wrapping mode. */
1719 m_buffer->m_obstack = &m_buffer->m_formatted_obstack;
1720 m_buffer->m_line_length = old_line_length;
1721 pp_wrapping_mode (this) = old_wrapping_mode;
1722 clear_state ();
1725 static void
1726 format_phase_1 (const text_info &text,
1727 obstack &chunk_obstack,
1728 pp_token_list **args,
1729 pp_token_list ***formatters)
1731 unsigned chunk = 0;
1732 unsigned int curarg = 0;
1733 bool any_unnumbered = false, any_numbered = false;
1734 pp_token_list *cur_token_list;
1735 args[chunk++] = cur_token_list = pp_token_list::make (chunk_obstack);
1736 for (const char *p = text.m_format_spec; *p; )
1738 while (*p != '\0' && *p != '%')
1740 obstack_1grow (&chunk_obstack, *p);
1741 p++;
1744 if (*p == '\0')
1745 break;
1747 switch (*++p)
1749 case '\0':
1750 gcc_unreachable ();
1752 case '%':
1753 obstack_1grow (&chunk_obstack, '%');
1754 p++;
1755 continue;
1757 case '<':
1759 push_back_any_text (cur_token_list, &chunk_obstack);
1760 cur_token_list->push_back<pp_token_begin_quote> ();
1761 p++;
1762 continue;
1765 case '>':
1767 push_back_any_text (cur_token_list, &chunk_obstack);
1768 cur_token_list->push_back<pp_token_end_quote> ();
1769 p++;
1770 continue;
1772 case '\'':
1774 push_back_any_text (cur_token_list, &chunk_obstack);
1775 cur_token_list->push_back<pp_token_end_quote> ();
1776 p++;
1778 continue;
1780 case '}':
1782 push_back_any_text (cur_token_list, &chunk_obstack);
1783 cur_token_list->push_back<pp_token_end_url> ();
1784 p++;
1786 continue;
1788 case 'R':
1790 push_back_any_text (cur_token_list, &chunk_obstack);
1791 cur_token_list->push_back<pp_token_end_color> ();
1792 p++;
1793 continue;
1796 case 'm':
1798 const char *errstr = xstrerror (text.m_err_no);
1799 obstack_grow (&chunk_obstack, errstr, strlen (errstr));
1801 p++;
1802 continue;
1804 default:
1805 /* Handled in phase 2. Terminate the plain chunk here. */
1806 push_back_any_text (cur_token_list, &chunk_obstack);
1807 break;
1810 /* Start a new token list for the formatting args. */
1811 args[chunk] = cur_token_list = pp_token_list::make (chunk_obstack);
1813 unsigned argno;
1814 if (ISDIGIT (*p))
1816 char *end;
1817 argno = strtoul (p, &end, 10) - 1;
1818 p = end;
1819 gcc_assert (*p == '$');
1820 p++;
1822 any_numbered = true;
1823 gcc_assert (!any_unnumbered);
1825 else
1827 argno = curarg++;
1828 any_unnumbered = true;
1829 gcc_assert (!any_numbered);
1831 gcc_assert (argno < PP_NL_ARGMAX);
1832 gcc_assert (!formatters[argno]);
1833 formatters[argno] = &args[chunk++];
1836 obstack_1grow (&chunk_obstack, *p);
1837 p++;
1839 while (strchr ("qwlzt+#", p[-1]));
1841 if (p[-1] == '.')
1843 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1844 (where M == N + 1). */
1845 if (ISDIGIT (*p))
1849 obstack_1grow (&chunk_obstack, *p);
1850 p++;
1852 while (ISDIGIT (p[-1]));
1853 gcc_assert (p[-1] == 's');
1855 else
1857 gcc_assert (*p == '*');
1858 obstack_1grow (&chunk_obstack, '*');
1859 p++;
1861 if (ISDIGIT (*p))
1863 char *end;
1864 unsigned int argno2 = strtoul (p, &end, 10) - 1;
1865 p = end;
1866 gcc_assert (argno2 == argno - 1);
1867 gcc_assert (!any_unnumbered);
1868 gcc_assert (*p == '$');
1870 p++;
1871 formatters[argno2] = formatters[argno];
1873 else
1875 gcc_assert (!any_numbered);
1876 formatters[argno+1] = formatters[argno];
1877 curarg++;
1879 gcc_assert (*p == 's');
1880 obstack_1grow (&chunk_obstack, 's');
1881 p++;
1884 if (*p == '\0')
1886 push_back_any_text (cur_token_list, &chunk_obstack);
1887 break;
1890 obstack_1grow (&chunk_obstack, '\0');
1891 push_back_any_text (cur_token_list, &chunk_obstack);
1893 /* Start a new token list for the next (non-formatted) text. */
1894 gcc_assert (chunk < PP_NL_ARGMAX * 2);
1895 args[chunk++] = cur_token_list = pp_token_list::make (chunk_obstack);
1898 obstack_1grow (&chunk_obstack, '\0');
1899 push_back_any_text (cur_token_list, &chunk_obstack);
1900 gcc_assert (chunk < PP_NL_ARGMAX * 2);
1901 args[chunk] = nullptr;
1904 /* Second phase. Replace each formatter with pp_tokens for the formatted
1905 text it corresponds to, consuming va_args from TEXT->m_args_ptr. */
1907 static void
1908 format_phase_2 (pretty_printer *pp,
1909 text_info &text,
1910 obstack &chunk_obstack,
1911 pp_token_list ***formatters)
1913 unsigned argno;
1914 for (argno = 0; formatters[argno]; argno++)
1916 int precision = 0;
1917 bool wide = false;
1918 bool plus = false;
1919 bool hash = false;
1920 bool quote = false;
1922 /* We expect a single text token containing the formatter. */
1923 pp_token_list *tok_list = *(formatters[argno]);
1924 gcc_assert (tok_list);
1925 gcc_assert (tok_list->m_first == tok_list->m_end);
1926 gcc_assert (tok_list->m_first->m_kind == pp_token::kind::text);
1928 /* Accumulate the value of the formatted text into here. */
1929 pp_token_list *formatted_tok_list
1930 = pp_token_list::make (chunk_obstack);
1932 /* We do not attempt to enforce any ordering on the modifier
1933 characters. */
1935 const char *p;
1936 for (p = as_a <pp_token_text *> (tok_list->m_first)->m_value.get ();; p++)
1938 switch (*p)
1940 case 'q':
1941 gcc_assert (!quote);
1942 quote = true;
1943 continue;
1945 case '+':
1946 gcc_assert (!plus);
1947 plus = true;
1948 continue;
1950 case '#':
1951 gcc_assert (!hash);
1952 hash = true;
1953 continue;
1955 case 'w':
1956 gcc_assert (!wide);
1957 wide = true;
1958 continue;
1960 case 'z':
1961 gcc_assert (!precision);
1962 precision = 3;
1963 continue;
1965 case 't':
1966 gcc_assert (!precision);
1967 precision = 4;
1968 continue;
1970 case 'l':
1971 /* We don't support precision beyond that of "long long". */
1972 gcc_assert (precision < 2);
1973 precision++;
1974 continue;
1976 break;
1979 gcc_assert (!wide || precision == 0);
1981 if (quote)
1983 push_back_any_text (formatted_tok_list, &chunk_obstack);
1984 formatted_tok_list->push_back<pp_token_begin_quote> ();
1987 switch (*p)
1989 case 'r':
1991 const char *color = va_arg (*text.m_args_ptr, const char *);
1992 formatted_tok_list->push_back<pp_token_begin_color>
1993 (label_text::borrow (color));
1995 break;
1997 case 'c':
1999 /* When quoting, print alphanumeric, punctuation, and the space
2000 character unchanged, and all others in hexadecimal with the
2001 "\x" prefix. Otherwise print them all unchanged. */
2002 char chr = (char) va_arg (*text.m_args_ptr, int);
2003 if (ISPRINT (chr) || !quote)
2004 pp_character (pp, chr);
2005 else
2007 const char str [2] = { chr, '\0' };
2008 pp_quoted_string (pp, str, 1);
2010 break;
2013 case 'd':
2014 case 'i':
2015 if (wide)
2016 pp_wide_integer (pp, va_arg (*text.m_args_ptr, HOST_WIDE_INT));
2017 else
2018 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2019 int, "d");
2020 break;
2022 case 'o':
2023 if (wide)
2024 pp_scalar (pp, "%" HOST_WIDE_INT_PRINT "o",
2025 va_arg (*text.m_args_ptr, unsigned HOST_WIDE_INT));
2026 else
2027 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2028 unsigned, "o");
2029 break;
2031 case 's':
2032 if (quote)
2033 pp_quoted_string (pp, va_arg (*text.m_args_ptr, const char *));
2034 else
2035 pp_string (pp, va_arg (*text.m_args_ptr, const char *));
2036 break;
2038 case 'p':
2039 pp_pointer (pp, va_arg (*text.m_args_ptr, void *));
2040 break;
2042 case 'u':
2043 if (wide)
2044 pp_scalar (pp, HOST_WIDE_INT_PRINT_UNSIGNED,
2045 va_arg (*text.m_args_ptr, unsigned HOST_WIDE_INT));
2046 else
2047 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2048 unsigned, "u");
2049 break;
2051 case 'f':
2052 pp_double (pp, va_arg (*text.m_args_ptr, double));
2053 break;
2055 case 'Z':
2057 int *v = va_arg (*text.m_args_ptr, int *);
2058 unsigned len = va_arg (*text.m_args_ptr, unsigned);
2060 for (unsigned i = 0; i < len; ++i)
2062 pp_scalar (pp, "%i", v[i]);
2063 if (i < len - 1)
2065 pp_comma (pp);
2066 pp_space (pp);
2069 break;
2072 case 'x':
2073 if (wide)
2074 pp_scalar (pp, HOST_WIDE_INT_PRINT_HEX,
2075 va_arg (*text.m_args_ptr, unsigned HOST_WIDE_INT));
2076 else
2077 pp_integer_with_precision (pp, *text.m_args_ptr, precision,
2078 unsigned, "x");
2079 break;
2081 case '.':
2083 int n;
2084 const char *s;
2086 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
2087 (where M == N + 1). The format string should be verified
2088 already from the first phase. */
2089 p++;
2090 if (ISDIGIT (*p))
2092 char *end;
2093 n = strtoul (p, &end, 10);
2094 p = end;
2095 gcc_assert (*p == 's');
2097 else
2099 gcc_assert (*p == '*');
2100 p++;
2101 gcc_assert (*p == 's');
2102 n = va_arg (*text.m_args_ptr, int);
2104 /* This consumes a second entry in the formatters array. */
2105 gcc_assert (formatters[argno] == formatters[argno+1]);
2106 argno++;
2109 s = va_arg (*text.m_args_ptr, const char *);
2111 /* Append the lesser of precision and strlen (s) characters
2112 from the array (which need not be a nul-terminated string).
2113 Negative precision is treated as if it were omitted. */
2114 size_t len = n < 0 ? strlen (s) : strnlen (s, n);
2116 pp_append_text (pp, s, s + len);
2118 break;
2120 case '@':
2122 /* diagnostic_event_id_t *. */
2123 diagnostic_event_id_ptr event_id
2124 = va_arg (*text.m_args_ptr, diagnostic_event_id_ptr);
2125 gcc_assert (event_id->known_p ());
2126 formatted_tok_list->push_back<pp_token_event_id> (*event_id);
2128 break;
2130 case '{':
2132 const char *url = va_arg (*text.m_args_ptr, const char *);
2133 formatted_tok_list->push_back<pp_token_begin_url>
2134 (label_text::borrow (url));
2136 break;
2138 case 'e':
2140 pp_element *element = va_arg (*text.m_args_ptr, pp_element *);
2141 pp_markup::context ctxt (*pp,
2142 quote, /* by reference */
2143 formatted_tok_list);
2144 element->add_to_phase_2 (ctxt);
2146 break;
2148 default:
2150 /* Call the format decoder.
2151 Pass the address of "quote" so that format decoders can
2152 potentially disable printing of the closing quote
2153 (e.g. when printing "'TYPEDEF' aka 'TYPE'" in the C family
2154 of frontends). */
2155 printer_fn format_decoder = pp_format_decoder (pp);
2156 gcc_assert (format_decoder);
2157 gcc_assert (formatted_tok_list);
2158 bool ok = format_decoder (pp, &text, p,
2159 precision, wide, plus, hash, &quote,
2160 *formatted_tok_list);
2161 gcc_assert (ok);
2165 if (quote)
2167 push_back_any_text (formatted_tok_list, &chunk_obstack);
2168 formatted_tok_list->push_back<pp_token_end_quote> ();
2171 push_back_any_text (formatted_tok_list, &chunk_obstack);
2172 delete *formatters[argno];
2173 *formatters[argno] = formatted_tok_list;
2176 if (CHECKING_P)
2177 for (; argno < PP_NL_ARGMAX; argno++)
2178 gcc_assert (!formatters[argno]);
2181 struct auto_obstack
2183 auto_obstack ()
2185 obstack_init (&m_obstack);
2188 ~auto_obstack ()
2190 obstack_free (&m_obstack, NULL);
2193 operator obstack & () { return m_obstack; }
2195 void grow (const void *src, size_t length)
2197 obstack_grow (&m_obstack, src, length);
2200 void *object_base () const
2202 return m_obstack.object_base;
2205 size_t object_size () const
2207 return obstack_object_size (&m_obstack);
2210 obstack m_obstack;
2213 /* Phase 3 of formatting a message (phases 1 and 2 done by pp_format).
2215 Pop a pp_formatted_chunks from chunk_obstack, collecting all the tokens from
2216 phases 1 and 2 of formatting, and writing into text in formatted_obstack.
2218 If URLIFIER is non-null then use it on any quoted text that was not
2219 handled in phases 1 or 2 to potentially add URLs. */
2221 void
2222 pp_output_formatted_text (pretty_printer *pp,
2223 const urlifier *urlifier)
2225 output_buffer * const buffer = pp_buffer (pp);
2226 gcc_assert (buffer->m_obstack == &buffer->m_formatted_obstack);
2228 pp_formatted_chunks *chunk_array = buffer->m_cur_formatted_chunks;
2229 pp_token_list * const *token_lists = chunk_array->get_token_lists ();
2232 /* Consolidate into one token list. */
2233 pp_token_list tokens (buffer->m_chunk_obstack);
2234 for (unsigned chunk = 0; token_lists[chunk]; chunk++)
2236 tokens.push_back_list (std::move (*token_lists[chunk]));
2237 delete token_lists[chunk];
2240 tokens.replace_custom_tokens ();
2242 tokens.merge_consecutive_text_tokens ();
2244 if (urlifier)
2245 tokens.apply_urlifier (*urlifier);
2247 /* This is a third phase, first 2 phases done in pp_format_args.
2248 Now we actually print it. */
2249 if (pp->m_token_printer)
2250 pp->m_token_printer->print_tokens (pp, tokens);
2251 else
2252 default_token_printer (pp, tokens);
2254 /* Close the scope here to ensure that "tokens" above is fully cleared up
2255 before popping the current pp_formatted_chunks, since that latter will pop
2256 the chunk_obstack, and "tokens" may be using blocks within
2257 the current pp_formatted_chunks's chunk_obstack level. */
2260 buffer->pop_formatted_chunks ();
2263 /* Default implementation of token printing. */
2265 static void
2266 default_token_printer (pretty_printer *pp,
2267 const pp_token_list &tokens)
2269 /* Convert to text, possibly with colorization, URLs, etc. */
2270 for (auto iter = tokens.m_first; iter; iter = iter->m_next)
2271 switch (iter->m_kind)
2273 default:
2274 gcc_unreachable ();
2276 case pp_token::kind::text:
2278 pp_token_text *sub = as_a <pp_token_text *> (iter);
2279 pp_string (pp, sub->m_value.get ());
2281 break;
2283 case pp_token::kind::begin_color:
2285 pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);
2286 pp_string (pp, colorize_start (pp_show_color (pp),
2287 sub->m_value.get ()));
2289 break;
2290 case pp_token::kind::end_color:
2291 pp_string (pp, colorize_stop (pp_show_color (pp)));
2292 break;
2294 case pp_token::kind::begin_quote:
2295 pp_begin_quote (pp, pp_show_color (pp));
2296 break;
2297 case pp_token::kind::end_quote:
2298 pp_end_quote (pp, pp_show_color (pp));
2299 break;
2301 case pp_token::kind::begin_url:
2303 pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
2304 pp_begin_url (pp, sub->m_value.get ());
2306 break;
2307 case pp_token::kind::end_url:
2308 pp_end_url (pp);
2309 break;
2311 case pp_token::kind::event_id:
2313 pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
2314 gcc_assert (sub->m_event_id.known_p ());
2315 pp_string (pp, colorize_start (pp_show_color (pp), "path"));
2316 pp_character (pp, '(');
2317 pp_decimal_int (pp, sub->m_event_id.one_based ());
2318 pp_character (pp, ')');
2319 pp_string (pp, colorize_stop (pp_show_color (pp)));
2321 break;
2323 case pp_token::kind::custom_data:
2324 /* These should have been eliminated by replace_custom_tokens. */
2325 gcc_unreachable ();
2326 break;
2330 /* Helper subroutine of output_verbatim and verbatim. Do the appropriate
2331 settings needed by BUFFER for a verbatim formatting. */
2332 void
2333 pp_format_verbatim (pretty_printer *pp, text_info *text)
2335 /* Set verbatim mode. */
2336 pp_wrapping_mode_t oldmode = pp_set_verbatim_wrapping (pp);
2338 /* Do the actual formatting. */
2339 pp_format (pp, text);
2340 pp_output_formatted_text (pp);
2342 /* Restore previous settings. */
2343 pp_wrapping_mode (pp) = oldmode;
2346 /* Flush the content of BUFFER onto the attached stream. This
2347 function does nothing unless pp->output_buffer->flush_p. */
2348 void
2349 pp_flush (pretty_printer *pp)
2351 pp->clear_state ();
2352 if (!pp_buffer (pp)->m_flush_p)
2353 return;
2354 pp_write_text_to_stream (pp);
2355 fflush (pp_buffer (pp)->m_stream);
2358 /* Flush the content of BUFFER onto the attached stream independently
2359 of the value of pp->output_buffer->flush_p. */
2360 void
2361 pp_really_flush (pretty_printer *pp)
2363 pp->clear_state ();
2364 pp_write_text_to_stream (pp);
2365 fflush (pp_buffer (pp)->m_stream);
2368 /* Sets the number of maximum characters per line PRETTY-PRINTER can
2369 output in line-wrapping mode. A LENGTH value 0 suppresses
2370 line-wrapping. */
2371 void
2372 pp_set_line_maximum_length (pretty_printer *pp, int length)
2374 pp_line_cutoff (pp) = length;
2375 pp->set_real_maximum_length ();
2378 /* Clear PRETTY-PRINTER output area text info. */
2379 void
2380 pp_clear_output_area (pretty_printer *pp)
2382 obstack_free (pp_buffer (pp)->m_obstack,
2383 obstack_base (pp_buffer (pp)->m_obstack));
2384 pp_buffer (pp)->m_line_length = 0;
2387 /* Set PREFIX for PRETTY-PRINTER, taking ownership of PREFIX, which
2388 will eventually be free-ed. */
2390 void
2391 pretty_printer::set_prefix (char *prefix)
2393 free (m_prefix);
2394 m_prefix = prefix;
2395 set_real_maximum_length ();
2396 m_emitted_prefix = false;
2397 pp_indentation (this) = 0;
2400 /* Take ownership of PP's prefix, setting it to NULL.
2401 This allows clients to save, override, and then restore an existing
2402 prefix, without it being free-ed. */
2404 char *
2405 pp_take_prefix (pretty_printer *pp)
2407 char *result = pp->m_prefix;
2408 pp->m_prefix = nullptr;
2409 return result;
2412 /* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */
2413 void
2414 pp_destroy_prefix (pretty_printer *pp)
2416 if (pp->m_prefix)
2418 free (pp->m_prefix);
2419 pp->m_prefix = nullptr;
2423 /* Write out this pretty_printer's prefix. */
2424 void
2425 pretty_printer::emit_prefix ()
2427 if (m_prefix)
2429 switch (pp_prefixing_rule (this))
2431 default:
2432 case DIAGNOSTICS_SHOW_PREFIX_NEVER:
2433 break;
2435 case DIAGNOSTICS_SHOW_PREFIX_ONCE:
2436 if (m_emitted_prefix)
2438 pp_indent (this);
2439 break;
2441 pp_indentation (this) += 3;
2442 /* Fall through. */
2444 case DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE:
2446 int prefix_length = strlen (m_prefix);
2447 pp_append_r (this, m_prefix, prefix_length);
2448 m_emitted_prefix = true;
2450 break;
2455 /* Construct a PRETTY-PRINTER of MAXIMUM_LENGTH characters per line. */
2457 pretty_printer::pretty_printer (int maximum_length)
2458 : m_buffer (new (XCNEW (output_buffer)) output_buffer ()),
2459 m_prefix (nullptr),
2460 m_padding (pp_none),
2461 m_maximum_length (0),
2462 m_indent_skip (0),
2463 m_wrapping (),
2464 m_format_decoder (nullptr),
2465 m_format_postprocessor (NULL),
2466 m_token_printer (nullptr),
2467 m_emitted_prefix (false),
2468 m_need_newline (false),
2469 m_translate_identifiers (true),
2470 m_show_color (false),
2471 m_show_highlight_colors (false),
2472 m_url_format (URL_FORMAT_NONE),
2473 m_skipping_null_url (false)
2475 pp_line_cutoff (this) = maximum_length;
2476 /* By default, we emit prefixes once per message. */
2477 pp_prefixing_rule (this) = DIAGNOSTICS_SHOW_PREFIX_ONCE;
2478 pp_set_prefix (this, NULL);
2481 /* Copy constructor for pretty_printer. */
2483 pretty_printer::pretty_printer (const pretty_printer &other)
2484 : m_buffer (new (XCNEW (output_buffer)) output_buffer ()),
2485 m_prefix (nullptr),
2486 m_padding (other.m_padding),
2487 m_maximum_length (other.m_maximum_length),
2488 m_indent_skip (other.m_indent_skip),
2489 m_wrapping (other.m_wrapping),
2490 m_format_decoder (other.m_format_decoder),
2491 m_format_postprocessor (NULL),
2492 m_token_printer (other.m_token_printer),
2493 m_emitted_prefix (other.m_emitted_prefix),
2494 m_need_newline (other.m_need_newline),
2495 m_translate_identifiers (other.m_translate_identifiers),
2496 m_show_color (other.m_show_color),
2497 m_show_highlight_colors (other.m_show_highlight_colors),
2498 m_url_format (other.m_url_format),
2499 m_skipping_null_url (false)
2501 pp_line_cutoff (this) = m_maximum_length;
2502 /* By default, we emit prefixes once per message. */
2503 pp_prefixing_rule (this) = pp_prefixing_rule (&other);
2504 pp_set_prefix (this, NULL);
2506 if (other.m_format_postprocessor)
2507 m_format_postprocessor = other.m_format_postprocessor->clone ();
2510 pretty_printer::~pretty_printer ()
2512 if (m_format_postprocessor)
2513 delete m_format_postprocessor;
2514 m_buffer->~output_buffer ();
2515 XDELETE (m_buffer);
2516 free (m_prefix);
2519 /* Base class implementation of pretty_printer::clone vfunc. */
2521 std::unique_ptr<pretty_printer>
2522 pretty_printer::clone () const
2524 return ::make_unique<pretty_printer> (*this);
2527 /* Append a string delimited by START and END to the output area of
2528 PRETTY-PRINTER. No line wrapping is done. However, if beginning a
2529 new line then emit PRETTY-PRINTER's prefix and skip any leading
2530 whitespace if appropriate. The caller must ensure that it is
2531 safe to do so. */
2532 void
2533 pp_append_text (pretty_printer *pp, const char *start, const char *end)
2535 /* Emit prefix and skip whitespace if we're starting a new line. */
2536 if (pp_buffer (pp)->m_line_length == 0)
2538 pp->emit_prefix ();
2539 if (pp_is_wrapping_line (pp))
2540 while (start != end && *start == ' ')
2541 ++start;
2543 pp_append_r (pp, start, end - start);
2546 /* Finishes constructing a NULL-terminated character string representing
2547 the PRETTY-PRINTED text. */
2548 const char *
2549 pp_formatted_text (pretty_printer *pp)
2551 return output_buffer_formatted_text (pp_buffer (pp));
2554 /* Return a pointer to the last character emitted in PRETTY-PRINTER's
2555 output area. A NULL pointer means no character available. */
2556 const char *
2557 pp_last_position_in_text (const pretty_printer *pp)
2559 return output_buffer_last_position_in_text (pp_buffer (pp));
2562 /* Return the amount of characters PRETTY-PRINTER can accept to
2563 make a full line. Meaningful only in line-wrapping mode. */
2565 pretty_printer::remaining_character_count_for_line ()
2567 return m_maximum_length - pp_buffer (this)->m_line_length;
2570 /* Format a message into BUFFER a la printf. */
2571 void
2572 pp_printf (pretty_printer *pp, const char *msg, ...)
2574 va_list ap;
2576 va_start (ap, msg);
2577 text_info text (msg, &ap, errno);
2578 pp_format (pp, &text);
2579 pp_output_formatted_text (pp);
2580 va_end (ap);
2583 /* Format a message into PP using ngettext to handle
2584 singular vs plural. */
2586 void
2587 pp_printf_n (pretty_printer *pp,
2588 unsigned HOST_WIDE_INT n,
2589 const char *singular_gmsgid, const char *plural_gmsgid, ...)
2591 va_list ap;
2593 va_start (ap, plural_gmsgid);
2595 unsigned long gtn;
2596 if (sizeof n <= sizeof gtn)
2597 gtn = n;
2598 else
2599 /* Use the largest number ngettext can handle, otherwise
2600 preserve the six least significant decimal digits for
2601 languages where the plural form depends on them. */
2602 gtn = n <= ULONG_MAX ? n : n % 1000000LU + 1000000LU;
2603 const char *msg = ngettext (singular_gmsgid, plural_gmsgid, gtn);
2604 text_info text (msg, &ap, errno);
2605 pp_format (pp, &text);
2606 pp_output_formatted_text (pp);
2607 va_end (ap);
2610 /* Output MESSAGE verbatim into BUFFER. */
2611 void
2612 pp_verbatim (pretty_printer *pp, const char *msg, ...)
2614 va_list ap;
2616 va_start (ap, msg);
2617 text_info text (msg, &ap, errno);
2618 pp_format_verbatim (pp, &text);
2619 va_end (ap);
2624 /* Have PRETTY-PRINTER start a new line. */
2625 void
2626 pp_newline (pretty_printer *pp)
2628 obstack_1grow (pp_buffer (pp)->m_obstack, '\n');
2629 pp_needs_newline (pp) = false;
2630 pp_buffer (pp)->m_line_length = 0;
2633 /* Have PRETTY-PRINTER add a CHARACTER. */
2634 void
2635 pp_character (pretty_printer *pp, int c)
2637 if (pp_is_wrapping_line (pp)
2638 /* If printing UTF-8, don't wrap in the middle of a sequence. */
2639 && (((unsigned int) c) & 0xC0) != 0x80
2640 && pp->remaining_character_count_for_line () <= 0)
2642 pp_newline (pp);
2643 if (ISSPACE (c))
2644 return;
2646 obstack_1grow (pp_buffer (pp)->m_obstack, c);
2647 ++pp_buffer (pp)->m_line_length;
2650 /* Append a STRING to the output area of PRETTY-PRINTER; the STRING may
2651 be line-wrapped if in appropriate mode. */
2652 void
2653 pp_string (pretty_printer *pp, const char *str)
2655 gcc_checking_assert (str);
2656 pp_maybe_wrap_text (pp, str, str + strlen (str));
2659 /* As per pp_string, but only append the first LEN of STR. */
2661 void
2662 pp_string_n (pretty_printer *pp, const char *str, size_t len)
2664 gcc_checking_assert (str);
2665 pp_maybe_wrap_text (pp, str, str + len);
2668 /* Append code point C to the output area of PRETTY-PRINTER, encoding it
2669 as UTF-8. */
2671 void
2672 pp_unicode_character (pretty_printer *pp, unsigned c)
2674 static const uchar masks[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
2675 static const uchar limits[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
2676 size_t nbytes;
2677 uchar buf[6], *p = &buf[6];
2679 nbytes = 1;
2680 if (c < 0x80)
2681 *--p = c;
2682 else
2686 *--p = ((c & 0x3F) | 0x80);
2687 c >>= 6;
2688 nbytes++;
2690 while (c >= 0x3F || (c & limits[nbytes-1]));
2691 *--p = (c | masks[nbytes-1]);
2694 pp_append_r (pp, (const char *)p, nbytes);
2697 /* Append the leading N characters of STRING to the output area of
2698 PRETTY-PRINTER, quoting in hexadecimal non-printable characters.
2699 Setting N = -1 is as if N were set to strlen (STRING). The STRING
2700 may be line-wrapped if in appropriate mode. */
2701 static void
2702 pp_quoted_string (pretty_printer *pp, const char *str, size_t n /* = -1 */)
2704 gcc_checking_assert (str);
2706 const char *last = str;
2707 const char *ps;
2709 /* Compute the length if not specified. */
2710 if (n == (size_t) -1)
2711 n = strlen (str);
2713 for (ps = str; n; ++ps, --n)
2715 if (ISPRINT (*ps))
2716 continue;
2718 /* Don't escape a valid UTF-8 extended char. */
2719 const unsigned char *ups = (const unsigned char *) ps;
2720 if (*ups & 0x80)
2722 unsigned int extended_char;
2723 const int valid_utf8_len = decode_utf8_char (ups, n, &extended_char);
2724 if (valid_utf8_len > 0)
2726 ps += valid_utf8_len - 1;
2727 n -= valid_utf8_len - 1;
2728 continue;
2732 if (last < ps)
2733 pp_maybe_wrap_text (pp, last, ps);
2735 /* Append the hexadecimal value of the character. Allocate a buffer
2736 that's large enough for a 32-bit char plus the hex prefix. */
2737 char buf [11];
2738 int n = sprintf (buf, "\\x%02x", (unsigned char)*ps);
2739 pp_maybe_wrap_text (pp, buf, buf + n);
2740 last = ps + 1;
2743 pp_maybe_wrap_text (pp, last, ps);
2746 /* Maybe print out a whitespace if needed. */
2748 void
2749 pretty_printer::maybe_space ()
2751 if (m_padding != pp_none)
2753 pp_space (this);
2754 m_padding = pp_none;
2758 // Add a newline to the pretty printer PP and flush formatted text.
2760 void
2761 pp_newline_and_flush (pretty_printer *pp)
2763 pp_newline (pp);
2764 pp_flush (pp);
2765 pp_needs_newline (pp) = false;
2768 // Add a newline to the pretty printer PP, followed by indentation.
2770 void
2771 pp_newline_and_indent (pretty_printer *pp, int n)
2773 pp_indentation (pp) += n;
2774 pp_newline (pp);
2775 pp_indent (pp);
2776 pp_needs_newline (pp) = false;
2779 // Add separator C, followed by a single whitespace.
2781 void
2782 pp_separate_with (pretty_printer *pp, char c)
2784 pp_character (pp, c);
2785 pp_space (pp);
2788 /* Add a localized open quote, and if SHOW_COLOR is true, begin colorizing
2789 using the "quote" color. */
2791 void
2792 pp_begin_quote (pretty_printer *pp, bool show_color)
2794 pp_string (pp, open_quote);
2795 pp_string (pp, colorize_start (show_color, "quote"));
2798 /* If SHOW_COLOR is true, stop colorizing.
2799 Add a localized close quote. */
2801 void
2802 pp_end_quote (pretty_printer *pp, bool show_color)
2804 pp_string (pp, colorize_stop (show_color));
2805 pp_string (pp, close_quote);
2809 /* The string starting at P has LEN (at least 1) bytes left; if they
2810 start with a valid UTF-8 sequence, return the length of that
2811 sequence and set *VALUE to the value of that sequence, and
2812 otherwise return 0 and set *VALUE to (unsigned int) -1. */
2814 static int
2815 decode_utf8_char (const unsigned char *p, size_t len, unsigned int *value)
2817 unsigned int t = *p;
2819 if (len == 0)
2820 abort ();
2821 if (t & 0x80)
2823 size_t utf8_len = 0;
2824 unsigned int ch;
2825 size_t i;
2826 for (t = *p; t & 0x80; t <<= 1)
2827 utf8_len++;
2829 if (utf8_len > len || utf8_len < 2 || utf8_len > 6)
2831 *value = (unsigned int) -1;
2832 return 0;
2834 ch = *p & ((1 << (7 - utf8_len)) - 1);
2835 for (i = 1; i < utf8_len; i++)
2837 unsigned int u = p[i];
2838 if ((u & 0xC0) != 0x80)
2840 *value = (unsigned int) -1;
2841 return 0;
2843 ch = (ch << 6) | (u & 0x3F);
2845 if ( (ch <= 0x7F && utf8_len > 1)
2846 || (ch <= 0x7FF && utf8_len > 2)
2847 || (ch <= 0xFFFF && utf8_len > 3)
2848 || (ch <= 0x1FFFFF && utf8_len > 4)
2849 || (ch <= 0x3FFFFFF && utf8_len > 5)
2850 || (ch >= 0xD800 && ch <= 0xDFFF))
2852 *value = (unsigned int) -1;
2853 return 0;
2855 *value = ch;
2856 return utf8_len;
2858 else
2860 *value = t;
2861 return 1;
2865 /* Allocator for identifier_to_locale and corresponding function to
2866 free memory. */
2868 void *(*identifier_to_locale_alloc) (size_t) = xmalloc;
2869 void (*identifier_to_locale_free) (void *) = free;
2871 /* Given IDENT, an identifier in the internal encoding, return a
2872 version of IDENT suitable for diagnostics in the locale character
2873 set: either IDENT itself, or a string, allocated using
2874 identifier_to_locale_alloc, converted to the locale character set
2875 and using escape sequences if not representable in the locale
2876 character set or containing control characters or invalid byte
2877 sequences. Existing backslashes in IDENT are not doubled, so the
2878 result may not uniquely specify the contents of an arbitrary byte
2879 sequence identifier. */
2881 const char *
2882 identifier_to_locale (const char *ident)
2884 const unsigned char *uid = (const unsigned char *) ident;
2885 size_t idlen = strlen (ident);
2886 bool valid_printable_utf8 = true;
2887 bool all_ascii = true;
2888 size_t i;
2890 for (i = 0; i < idlen;)
2892 unsigned int c;
2893 size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
2894 if (utf8_len == 0 || c <= 0x1F || (c >= 0x7F && c <= 0x9F))
2896 valid_printable_utf8 = false;
2897 break;
2899 if (utf8_len > 1)
2900 all_ascii = false;
2901 i += utf8_len;
2904 /* If IDENT contains invalid UTF-8 sequences (which may occur with
2905 attributes putting arbitrary byte sequences in identifiers), or
2906 control characters, we use octal escape sequences for all bytes
2907 outside printable ASCII. */
2908 if (!valid_printable_utf8)
2910 char *ret = (char *) identifier_to_locale_alloc (4 * idlen + 1);
2911 char *p = ret;
2912 for (i = 0; i < idlen; i++)
2914 if (uid[i] > 0x1F && uid[i] < 0x7F)
2915 *p++ = uid[i];
2916 else
2918 sprintf (p, "\\%03o", uid[i]);
2919 p += 4;
2922 *p = 0;
2923 return ret;
2926 /* Otherwise, if it is valid printable ASCII, or printable UTF-8
2927 with the locale character set being UTF-8, IDENT is used. */
2928 if (all_ascii || locale_utf8)
2929 return ident;
2931 /* Otherwise IDENT is converted to the locale character set if
2932 possible. */
2933 #if defined ENABLE_NLS && defined HAVE_LANGINFO_CODESET && HAVE_ICONV
2934 if (locale_encoding != NULL)
2936 iconv_t cd = iconv_open (locale_encoding, "UTF-8");
2937 bool conversion_ok = true;
2938 char *ret = NULL;
2939 if (cd != (iconv_t) -1)
2941 size_t ret_alloc = 4 * idlen + 1;
2942 for (;;)
2944 /* Repeat the whole conversion process as needed with
2945 larger buffers so non-reversible transformations can
2946 always be detected. */
2947 ICONV_CONST char *inbuf = CONST_CAST (char *, ident);
2948 char *outbuf;
2949 size_t inbytesleft = idlen;
2950 size_t outbytesleft = ret_alloc - 1;
2951 size_t iconv_ret;
2953 ret = (char *) identifier_to_locale_alloc (ret_alloc);
2954 outbuf = ret;
2956 if (iconv (cd, 0, 0, 0, 0) == (size_t) -1)
2958 conversion_ok = false;
2959 break;
2962 iconv_ret = iconv (cd, &inbuf, &inbytesleft,
2963 &outbuf, &outbytesleft);
2964 if (iconv_ret == (size_t) -1 || inbytesleft != 0)
2966 if (errno == E2BIG)
2968 ret_alloc *= 2;
2969 identifier_to_locale_free (ret);
2970 ret = NULL;
2971 continue;
2973 else
2975 conversion_ok = false;
2976 break;
2979 else if (iconv_ret != 0)
2981 conversion_ok = false;
2982 break;
2984 /* Return to initial shift state. */
2985 if (iconv (cd, 0, 0, &outbuf, &outbytesleft) == (size_t) -1)
2987 if (errno == E2BIG)
2989 ret_alloc *= 2;
2990 identifier_to_locale_free (ret);
2991 ret = NULL;
2992 continue;
2994 else
2996 conversion_ok = false;
2997 break;
3000 *outbuf = 0;
3001 break;
3003 iconv_close (cd);
3004 if (conversion_ok)
3005 return ret;
3008 #endif
3010 /* Otherwise, convert non-ASCII characters in IDENT to UCNs. */
3012 char *ret = (char *) identifier_to_locale_alloc (10 * idlen + 1);
3013 char *p = ret;
3014 for (i = 0; i < idlen;)
3016 unsigned int c;
3017 size_t utf8_len = decode_utf8_char (&uid[i], idlen - i, &c);
3018 if (utf8_len == 1)
3019 *p++ = uid[i];
3020 else
3022 sprintf (p, "\\U%08x", c);
3023 p += 10;
3025 i += utf8_len;
3027 *p = 0;
3028 return ret;
3032 /* Support for encoding URLs.
3033 See egmontkob/Hyperlinks_in_Terminal_Emulators.md
3034 ( https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda ).
3036 > A hyperlink is opened upon encountering an OSC 8 escape sequence with
3037 > the target URI. The syntax is
3039 > OSC 8 ; params ; URI ST
3041 > A hyperlink is closed with the same escape sequence, omitting the
3042 > parameters and the URI but keeping the separators:
3044 > OSC 8 ; ; ST
3046 > OSC (operating system command) is typically ESC ].
3048 Use BEL instead of ST, as that is currently rendered better in some
3049 terminal emulators that don't support OSC 8, like konsole. */
3051 /* If URL-printing is enabled, write an "open URL" escape sequence to PP
3052 for the given URL. */
3054 void
3055 pretty_printer::begin_url (const char *url)
3057 if (!url)
3059 /* Handle null URL by skipping all output here,
3060 and in the next pp_end_url. */
3061 m_skipping_null_url = true;
3062 return;
3064 switch (m_url_format)
3066 case URL_FORMAT_NONE:
3067 break;
3068 case URL_FORMAT_ST:
3069 pp_string (this, "\33]8;;");
3070 pp_string (this, url);
3071 pp_string (this, "\33\\");
3072 break;
3073 case URL_FORMAT_BEL:
3074 pp_string (this, "\33]8;;");
3075 pp_string (this, url);
3076 pp_string (this, "\a");
3077 break;
3078 default:
3079 gcc_unreachable ();
3083 /* Helper function for pp_end_url and pp_format, return the "close URL" escape
3084 sequence string. */
3086 static const char *
3087 get_end_url_string (pretty_printer *pp)
3089 switch (pp->get_url_format ())
3091 case URL_FORMAT_NONE:
3092 return "";
3093 case URL_FORMAT_ST:
3094 return "\33]8;;\33\\";
3095 case URL_FORMAT_BEL:
3096 return "\33]8;;\a";
3097 default:
3098 gcc_unreachable ();
3102 /* If URL-printing is enabled, write a "close URL" escape sequence to PP. */
3104 void
3105 pretty_printer::end_url ()
3107 if (m_skipping_null_url)
3109 /* We gracefully handle pp_begin_url (NULL) by omitting output for
3110 both begin and end. Here we handle the latter. */
3111 m_skipping_null_url = false;
3112 return;
3114 if (m_url_format != URL_FORMAT_NONE)
3115 pp_string (this, get_end_url_string (this));
3118 /* Dump state of this pretty_printer to OUT, for debugging. */
3120 void
3121 pretty_printer::dump (FILE *out, int indent) const
3123 fprintf (out, "%*sm_show_color: %s\n",
3124 indent, "",
3125 m_show_color ? "true" : "false");
3127 fprintf (out, "%*sm_url_format: ", indent, "");
3128 switch (m_url_format)
3130 case URL_FORMAT_NONE:
3131 fprintf (out, "none");
3132 break;
3133 case URL_FORMAT_ST:
3134 fprintf (out, "st");
3135 break;
3136 case URL_FORMAT_BEL:
3137 fprintf (out, "bel");
3138 break;
3139 default:
3140 gcc_unreachable ();
3142 fprintf (out, "\n");
3144 fprintf (out, "%*sm_buffer:\n", indent, "");
3145 m_buffer->dump (out, indent + 2);
3148 /* class pp_markup::context. */
3150 void
3151 pp_markup::context::begin_quote ()
3153 gcc_assert (!m_quoted);
3154 gcc_assert (m_formatted_token_list);
3155 push_back_any_text ();
3156 m_formatted_token_list->push_back<pp_token_begin_quote> ();
3157 m_quoted = true;
3160 void
3161 pp_markup::context::end_quote ()
3163 /* Bail out if the quotes have already been ended, such as by
3164 printing a type emitting "TYPEDEF' {aka `TYPE'}". */
3165 if (!m_quoted)
3166 return;
3167 gcc_assert (m_formatted_token_list);
3168 push_back_any_text ();
3169 m_formatted_token_list->push_back<pp_token_end_quote> ();
3170 m_quoted = false;
3173 void
3174 pp_markup::context::begin_highlight_color (const char *color_name)
3176 if (!pp_show_highlight_colors (&m_pp))
3177 return;
3179 push_back_any_text ();
3180 m_formatted_token_list->push_back <pp_token_begin_color>
3181 (label_text::borrow (color_name));
3184 void
3185 pp_markup::context::end_highlight_color ()
3187 if (!pp_show_highlight_colors (&m_pp))
3188 return;
3190 push_back_any_text ();
3191 m_formatted_token_list->push_back<pp_token_end_color> ();
3194 void
3195 pp_markup::context::push_back_any_text ()
3197 obstack *cur_obstack = m_buf.m_obstack;
3198 obstack_1grow (cur_obstack, '\0');
3199 m_formatted_token_list->push_back_text
3200 (label_text::borrow (XOBFINISH (cur_obstack,
3201 const char *)));
3204 void
3205 pp_markup::comma_separated_quoted_strings::add_to_phase_2 (context &ctxt)
3207 for (unsigned i = 0; i < m_strings.length (); i++)
3209 if (i > 0)
3210 pp_string (&ctxt.m_pp, ", ");
3211 ctxt.begin_quote ();
3212 pp_string (&ctxt.m_pp, m_strings[i]);
3213 ctxt.end_quote ();
3217 /* Color names for expressing "expected" vs "actual" values. */
3218 const char *const highlight_colors::expected = "highlight-a";
3219 const char *const highlight_colors::actual = "highlight-b";
3221 /* Color names for expressing "LHS" vs "RHS" values in a binary operation. */
3222 const char *const highlight_colors::lhs = "highlight-a";
3223 const char *const highlight_colors::rhs = "highlight-b";
3225 #if CHECKING_P
3227 namespace selftest {
3229 /* Smoketest for pretty_printer. */
3231 static void
3232 test_basic_printing ()
3234 pretty_printer pp;
3235 pp_string (&pp, "hello");
3236 pp_space (&pp);
3237 pp_string (&pp, "world");
3239 ASSERT_STREQ ("hello world", pp_formatted_text (&pp));
3242 /* Helper function for testing pp_format.
3243 Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
3244 prints EXPECTED, assuming that pp_show_color is SHOW_COLOR. */
3246 static void
3247 assert_pp_format_va (const location &loc, const char *expected,
3248 bool show_color, const char *fmt, va_list *ap)
3250 pretty_printer pp;
3251 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
3253 text_info ti (fmt, ap, 0, nullptr, &rich_loc);
3255 pp_show_color (&pp) = show_color;
3256 pp_format (&pp, &ti);
3257 pp_output_formatted_text (&pp);
3258 ASSERT_STREQ_AT (loc, expected, pp_formatted_text (&pp));
3261 /* Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
3262 prints EXPECTED, with show_color disabled. */
3264 static void
3265 assert_pp_format (const location &loc, const char *expected,
3266 const char *fmt, ...)
3268 va_list ap;
3270 va_start (ap, fmt);
3271 assert_pp_format_va (loc, expected, false, fmt, &ap);
3272 va_end (ap);
3275 /* As above, but with colorization enabled. */
3277 static void
3278 assert_pp_format_colored (const location &loc, const char *expected,
3279 const char *fmt, ...)
3281 /* The tests of colorization assume the default color scheme.
3282 If GCC_COLORS is set, then the colors have potentially been
3283 overridden; skip the test. */
3284 if (getenv ("GCC_COLORS"))
3285 return;
3287 va_list ap;
3289 va_start (ap, fmt);
3290 assert_pp_format_va (loc, expected, true, fmt, &ap);
3291 va_end (ap);
3294 /* Helper function for calling testing pp_format,
3295 by calling assert_pp_format with various numbers of arguments.
3296 These exist mostly to avoid having to write SELFTEST_LOCATION
3297 throughout test_pp_format. */
3299 #define ASSERT_PP_FORMAT_1(EXPECTED, FMT, ARG1) \
3300 SELFTEST_BEGIN_STMT \
3301 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3302 (ARG1)); \
3303 SELFTEST_END_STMT
3305 #define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \
3306 SELFTEST_BEGIN_STMT \
3307 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3308 (ARG1), (ARG2)); \
3309 SELFTEST_END_STMT
3311 #define ASSERT_PP_FORMAT_3(EXPECTED, FMT, ARG1, ARG2, ARG3) \
3312 SELFTEST_BEGIN_STMT \
3313 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3314 (ARG1), (ARG2), (ARG3)); \
3315 SELFTEST_END_STMT
3317 /* Verify that pp_format works, for various format codes. */
3319 static void
3320 test_pp_format ()
3322 /* Avoid introducing locale-specific differences in the results
3323 by hardcoding open_quote and close_quote. */
3324 auto_fix_quotes fix_quotes;
3326 /* Verify that plain text is passed through unchanged. */
3327 assert_pp_format (SELFTEST_LOCATION, "unformatted", "unformatted");
3329 /* Verify various individual format codes, in the order listed in the
3330 comment for pp_format above. For each code, we append a second
3331 argument with a known bit pattern (0x12345678), to ensure that we
3332 are consuming arguments correctly. */
3333 ASSERT_PP_FORMAT_2 ("-27 12345678", "%d %x", -27, 0x12345678);
3334 ASSERT_PP_FORMAT_2 ("-5 12345678", "%i %x", -5, 0x12345678);
3335 ASSERT_PP_FORMAT_2 ("10 12345678", "%u %x", 10, 0x12345678);
3336 ASSERT_PP_FORMAT_2 ("17 12345678", "%o %x", 15, 0x12345678);
3337 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678);
3338 ASSERT_PP_FORMAT_2 ("-27 12345678", "%ld %x", (long)-27, 0x12345678);
3339 ASSERT_PP_FORMAT_2 ("-5 12345678", "%li %x", (long)-5, 0x12345678);
3340 ASSERT_PP_FORMAT_2 ("10 12345678", "%lu %x", (long)10, 0x12345678);
3341 ASSERT_PP_FORMAT_2 ("17 12345678", "%lo %x", (long)15, 0x12345678);
3342 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%lx %x", (long)0xcafebabe,
3343 0x12345678);
3344 ASSERT_PP_FORMAT_2 ("-27 12345678", "%lld %x", (long long)-27, 0x12345678);
3345 ASSERT_PP_FORMAT_2 ("-5 12345678", "%lli %x", (long long)-5, 0x12345678);
3346 ASSERT_PP_FORMAT_2 ("10 12345678", "%llu %x", (long long)10, 0x12345678);
3347 ASSERT_PP_FORMAT_2 ("17 12345678", "%llo %x", (long long)15, 0x12345678);
3348 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe,
3349 0x12345678);
3350 ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", HOST_WIDE_INT_C (-27),
3351 0x12345678);
3352 ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", HOST_WIDE_INT_C (-5),
3353 0x12345678);
3354 ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", HOST_WIDE_INT_UC (10),
3355 0x12345678);
3356 ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", HOST_WIDE_INT_C (15),
3357 0x12345678);
3358 ASSERT_PP_FORMAT_2 ("0xcafebabe 12345678", "%wx %x",
3359 HOST_WIDE_INT_C (0xcafebabe), 0x12345678);
3360 ASSERT_PP_FORMAT_2 ("-27 12345678", "%zd %x", (ssize_t)-27, 0x12345678);
3361 ASSERT_PP_FORMAT_2 ("-5 12345678", "%zi %x", (ssize_t)-5, 0x12345678);
3362 ASSERT_PP_FORMAT_2 ("10 12345678", "%zu %x", (size_t)10, 0x12345678);
3363 ASSERT_PP_FORMAT_2 ("17 12345678", "%zo %x", (size_t)15, 0x12345678);
3364 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%zx %x", (size_t)0xcafebabe,
3365 0x12345678);
3366 ASSERT_PP_FORMAT_2 ("-27 12345678", "%td %x", (ptrdiff_t)-27, 0x12345678);
3367 ASSERT_PP_FORMAT_2 ("-5 12345678", "%ti %x", (ptrdiff_t)-5, 0x12345678);
3368 ASSERT_PP_FORMAT_2 ("10 12345678", "%tu %x", (ptrdiff_t)10, 0x12345678);
3369 ASSERT_PP_FORMAT_2 ("17 12345678", "%to %x", (ptrdiff_t)15, 0x12345678);
3370 ASSERT_PP_FORMAT_2 ("1afebabe 12345678", "%tx %x", (ptrdiff_t)0x1afebabe,
3371 0x12345678);
3372 ASSERT_PP_FORMAT_2 ("1.000000 12345678", "%f %x", 1.0, 0x12345678);
3373 ASSERT_PP_FORMAT_2 ("A 12345678", "%c %x", 'A', 0x12345678);
3374 ASSERT_PP_FORMAT_2 ("hello world 12345678", "%s %x", "hello world",
3375 0x12345678);
3377 /* Not nul-terminated. */
3378 char arr[5] = { '1', '2', '3', '4', '5' };
3379 ASSERT_PP_FORMAT_3 ("123 12345678", "%.*s %x", 3, arr, 0x12345678);
3380 ASSERT_PP_FORMAT_3 ("1234 12345678", "%.*s %x", -1, "1234", 0x12345678);
3381 ASSERT_PP_FORMAT_3 ("12345 12345678", "%.*s %x", 7, "12345", 0x12345678);
3383 /* We can't test for %p; the pointer is printed in an implementation-defined
3384 manner. */
3385 ASSERT_PP_FORMAT_2 ("normal colored normal 12345678",
3386 "normal %rcolored%R normal %x",
3387 "error", 0x12345678);
3388 assert_pp_format_colored
3389 (SELFTEST_LOCATION,
3390 "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678",
3391 "normal %rcolored%R normal %x", "error", 0x12345678);
3392 /* TODO:
3393 %m: strerror(text->err_no) - does not consume a value from args_ptr. */
3394 ASSERT_PP_FORMAT_1 ("% 12345678", "%% %x", 0x12345678);
3395 ASSERT_PP_FORMAT_1 ("` 12345678", "%< %x", 0x12345678);
3396 ASSERT_PP_FORMAT_1 ("' 12345678", "%> %x", 0x12345678);
3397 ASSERT_PP_FORMAT_1 ("' 12345678", "%' %x", 0x12345678);
3398 ASSERT_PP_FORMAT_3 ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678);
3399 ASSERT_PP_FORMAT_2 ("abc 12345678", "%.3s %x", "abcdef", 0x12345678);
3401 /* Verify flag 'q'. */
3402 ASSERT_PP_FORMAT_2 ("`foo' 12345678", "%qs %x", "foo", 0x12345678);
3403 assert_pp_format_colored (SELFTEST_LOCATION,
3404 "`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x",
3405 "foo", 0x12345678);
3406 /* Verify "%@". */
3408 diagnostic_event_id_t first (2);
3409 diagnostic_event_id_t second (7);
3411 ASSERT_PP_FORMAT_2 ("first `free' at (3); second `free' at (8)",
3412 "first %<free%> at %@; second %<free%> at %@",
3413 &first, &second);
3414 assert_pp_format_colored
3415 (SELFTEST_LOCATION,
3416 "first `\e[01m\e[Kfree\e[m\e[K' at \e[01;36m\e[K(3)\e[m\e[K;"
3417 " second `\e[01m\e[Kfree\e[m\e[K' at \e[01;36m\e[K(8)\e[m\e[K",
3418 "first %<free%> at %@; second %<free%> at %@",
3419 &first, &second);
3422 /* Verify %Z. */
3423 int v[] = { 1, 2, 3 };
3424 ASSERT_PP_FORMAT_3 ("1, 2, 3 12345678", "%Z %x", v, 3, 0x12345678);
3426 int v2[] = { 0 };
3427 ASSERT_PP_FORMAT_3 ("0 12345678", "%Z %x", v2, 1, 0x12345678);
3429 /* Verify %e. */
3431 pp_element_quoted_string foo ("foo");
3432 pp_element_quoted_string bar ("bar");
3433 ASSERT_PP_FORMAT_2 ("before `foo' `bar' after",
3434 "before %e %e after",
3435 &foo, &bar);
3438 /* Verify that combinations work, along with unformatted text. */
3439 assert_pp_format (SELFTEST_LOCATION,
3440 "the quick brown fox jumps over the lazy dog",
3441 "the %s %s %s jumps over the %s %s",
3442 "quick", "brown", "fox", "lazy", "dog");
3443 assert_pp_format (SELFTEST_LOCATION, "item 3 of 7", "item %i of %i", 3, 7);
3444 assert_pp_format (SELFTEST_LOCATION, "problem with `bar' at line 10",
3445 "problem with %qs at line %i", "bar", 10);
3447 /* Verified numbered args. */
3448 assert_pp_format (SELFTEST_LOCATION,
3449 "foo: second bar: first",
3450 "foo: %2$s bar: %1$s",
3451 "first", "second");
3452 assert_pp_format (SELFTEST_LOCATION,
3453 "foo: 1066 bar: 1776",
3454 "foo: %2$i bar: %1$i",
3455 1776, 1066);
3456 assert_pp_format (SELFTEST_LOCATION,
3457 "foo: second bar: 1776",
3458 "foo: %2$s bar: %1$i",
3459 1776, "second");
3460 assert_pp_format (SELFTEST_LOCATION,
3461 "foo: sec bar: 3360",
3462 "foo: %3$.*2$s bar: %1$o",
3463 1776, 3, "second");
3464 assert_pp_format (SELFTEST_LOCATION,
3465 "foo: seco bar: 3360",
3466 "foo: %2$.4s bar: %1$o",
3467 1776, "second");
3470 static void
3471 test_merge_consecutive_text_tokens ()
3473 auto_obstack s;
3474 pp_token_list list (s);
3475 list.push_back_text (label_text::borrow ("hello"));
3476 list.push_back_text (label_text::borrow (" "));
3477 list.push_back_text (label_text::take (xstrdup ("world")));
3478 list.push_back_text (label_text::borrow ("!"));
3480 list.merge_consecutive_text_tokens ();
3481 // We expect a single text token, with concatenated text
3482 ASSERT_EQ (list.m_first, list.m_end);
3483 pp_token *tok = list.m_first;
3484 ASSERT_NE (tok, nullptr);
3485 ASSERT_EQ (tok->m_kind, pp_token::kind::text);
3486 ASSERT_STREQ (as_a <pp_token_text *> (tok)->m_value.get (), "hello world!");
3489 /* Verify that we can create custom tokens that can be lowered
3490 in phase 3. */
3492 static void
3493 test_custom_tokens_1 ()
3495 struct custom_token_adder : public pp_element
3497 public:
3498 struct value : public pp_token_custom_data::value
3500 value (custom_token_adder &adder)
3501 : m_adder (adder)
3503 m_adder.m_num_living_values++;
3505 value (const value &other)
3506 : m_adder (other.m_adder)
3508 m_adder.m_num_living_values++;
3510 value (value &&other)
3511 : m_adder (other.m_adder)
3513 m_adder.m_num_living_values++;
3515 value &operator= (const value &other) = delete;
3516 value &operator= (value &&other) = delete;
3517 ~value ()
3519 m_adder.m_num_living_values--;
3522 void dump (FILE *out) const final override
3524 fprintf (out, "\"%s\"", m_adder.m_name);
3527 bool as_standard_tokens (pp_token_list &out) final override
3529 ASSERT_TRUE (m_adder.m_num_living_values > 0);
3530 out.push_back<pp_token_text> (label_text::borrow (m_adder.m_name));
3531 return true;
3534 custom_token_adder &m_adder;
3537 custom_token_adder (const char *name)
3538 : m_name (name),
3539 m_num_living_values (0)
3543 void add_to_phase_2 (pp_markup::context &ctxt) final override
3545 auto val_ptr = make_unique<value> (*this);
3546 ctxt.m_formatted_token_list->push_back<pp_token_custom_data>
3547 (std::move (val_ptr));
3550 const char *m_name;
3551 int m_num_living_values;
3554 custom_token_adder e1 ("foo");
3555 custom_token_adder e2 ("bar");
3556 ASSERT_EQ (e1.m_num_living_values, 0);
3557 ASSERT_EQ (e2.m_num_living_values, 0);
3559 pretty_printer pp;
3560 pp_printf (&pp, "before %e middle %e after", &e1, &e2);
3562 /* Verify that instances were cleaned up. */
3563 ASSERT_EQ (e1.m_num_living_values, 0);
3564 ASSERT_EQ (e2.m_num_living_values, 0);
3566 ASSERT_STREQ (pp_formatted_text (&pp),
3567 "before foo middle bar after");
3570 /* Verify that we can create custom tokens that aren't lowered
3571 in phase 3, but instead are handled by a custom token_printer.
3572 Use this to verify the inputs seen by such token_printers. */
3574 static void
3575 test_custom_tokens_2 ()
3577 struct custom_token_adder : public pp_element
3579 struct value : public pp_token_custom_data::value
3581 public:
3582 value (custom_token_adder &adder)
3583 : m_adder (adder)
3585 m_adder.m_num_living_values++;
3587 value (const value &other)
3588 : m_adder (other.m_adder)
3590 m_adder.m_num_living_values++;
3592 value (value &&other)
3593 : m_adder (other.m_adder)
3595 m_adder.m_num_living_values++;
3597 value &operator= (const value &other) = delete;
3598 value &operator= (value &&other) = delete;
3599 ~value ()
3601 m_adder.m_num_living_values--;
3604 void dump (FILE *out) const final override
3606 fprintf (out, "\"%s\"", m_adder.m_name);
3609 bool as_standard_tokens (pp_token_list &) final override
3611 return false;
3614 custom_token_adder &m_adder;
3617 custom_token_adder (const char *name)
3618 : m_name (name),
3619 m_num_living_values (0)
3623 void add_to_phase_2 (pp_markup::context &ctxt) final override
3625 auto val_ptr = make_unique<value> (*this);
3626 ctxt.m_formatted_token_list->push_back<pp_token_custom_data>
3627 (std::move (val_ptr));
3630 const char *m_name;
3631 int m_num_living_values;
3634 class custom_token_printer : public token_printer
3636 void print_tokens (pretty_printer *pp,
3637 const pp_token_list &tokens) final override
3639 /* Verify that TOKENS has:
3640 [TEXT("before "), CUSTOM("foo"), TEXT(" middle "), CUSTOM("bar"),
3641 TEXT(" after")] */
3642 pp_token *tok_0 = tokens.m_first;
3643 ASSERT_NE (tok_0, nullptr);
3644 ASSERT_EQ (tok_0->m_kind, pp_token::kind::text);
3645 ASSERT_STREQ (as_a<pp_token_text *> (tok_0)->m_value.get (),
3646 "before ");
3648 pp_token *tok_1 = tok_0->m_next;
3649 ASSERT_NE (tok_1, nullptr);
3650 ASSERT_EQ (tok_1->m_prev, tok_0);
3651 ASSERT_EQ (tok_1->m_kind, pp_token::kind::custom_data);
3653 custom_token_adder::value *v1
3654 = static_cast <custom_token_adder::value *>
3655 (as_a<pp_token_custom_data *> (tok_1)->m_value.get ());
3656 ASSERT_STREQ (v1->m_adder.m_name, "foo");
3657 ASSERT_TRUE (v1->m_adder.m_num_living_values > 0);
3659 pp_token *tok_2 = tok_1->m_next;
3660 ASSERT_NE (tok_2, nullptr);
3661 ASSERT_EQ (tok_2->m_prev, tok_1);
3662 ASSERT_EQ (tok_2->m_kind, pp_token::kind::text);
3663 ASSERT_STREQ (as_a<pp_token_text *> (tok_2)->m_value.get (),
3664 " middle ");
3666 pp_token *tok_3 = tok_2->m_next;
3667 ASSERT_NE (tok_3, nullptr);
3668 ASSERT_EQ (tok_3->m_prev, tok_2);
3669 ASSERT_EQ (tok_3->m_kind, pp_token::kind::custom_data);
3670 custom_token_adder::value *v3
3671 = static_cast <custom_token_adder::value *>
3672 (as_a<pp_token_custom_data *> (tok_3)->m_value.get ());
3673 ASSERT_STREQ (v3->m_adder.m_name, "bar");
3674 ASSERT_TRUE (v3->m_adder.m_num_living_values > 0);
3676 pp_token *tok_4 = tok_3->m_next;
3677 ASSERT_NE (tok_4, nullptr);
3678 ASSERT_EQ (tok_4->m_prev, tok_3);
3679 ASSERT_EQ (tok_4->m_kind, pp_token::kind::text);
3680 ASSERT_STREQ (as_a<pp_token_text *> (tok_4)->m_value.get (),
3681 " after");
3682 ASSERT_EQ (tok_4->m_next, nullptr);
3684 /* Normally we'd loop over the tokens, printing them to PP
3685 and handling the custom tokens.
3686 Instead, print a message to PP to verify that we were called. */
3687 pp_string (pp, "print_tokens was called");
3691 custom_token_adder e1 ("foo");
3692 custom_token_adder e2 ("bar");
3693 ASSERT_EQ (e1.m_num_living_values, 0);
3694 ASSERT_EQ (e2.m_num_living_values, 0);
3696 custom_token_printer tp;
3697 pretty_printer pp;
3698 pp.set_token_printer (&tp);
3699 pp_printf (&pp, "before %e middle %e after", &e1, &e2);
3701 /* Verify that instances were cleaned up. */
3702 ASSERT_EQ (e1.m_num_living_values, 0);
3703 ASSERT_EQ (e2.m_num_living_values, 0);
3705 ASSERT_STREQ (pp_formatted_text (&pp),
3706 "print_tokens was called");
3709 /* Helper subroutine for test_pp_format_stack.
3710 Call pp_format (phases 1 and 2), without calling phase 3. */
3712 static void
3713 push_pp_format (pretty_printer *pp, const char *msg, ...)
3715 va_list ap;
3717 va_start (ap, msg);
3718 rich_location rich_loc (line_table, UNKNOWN_LOCATION);
3719 text_info ti (msg, &ap, 0, nullptr, &rich_loc);
3720 pp_format (pp, &ti);
3721 va_end (ap);
3724 #define ASSERT_TEXT_TOKEN(TOKEN, EXPECTED_TEXT) \
3725 SELFTEST_BEGIN_STMT \
3726 ASSERT_NE ((TOKEN), nullptr); \
3727 ASSERT_EQ ((TOKEN)->m_kind, pp_token::kind::text); \
3728 ASSERT_STREQ \
3729 (as_a <const pp_token_text *> (TOKEN)->m_value.get (), \
3730 (EXPECTED_TEXT)); \
3731 SELFTEST_END_STMT
3734 /* Verify that the stack of pp_formatted_chunks works as expected. */
3736 static void
3737 test_pp_format_stack ()
3739 auto_fix_quotes fix_quotes;
3741 pretty_printer pp;
3742 push_pp_format (&pp, "unexpected foo: %i bar: %qs", 42, "test");
3743 push_pp_format (&pp, "In function: %qs", "test_fn");
3745 /* Expect the top of the stack to have:
3746 (gdb) call top->dump()
3747 0: [TEXT("In function: ")]
3748 1: [BEGIN_QUOTE, TEXT("test_fn"), END_QUOTE]. */
3750 pp_formatted_chunks *top = pp_buffer (&pp)->m_cur_formatted_chunks;
3751 ASSERT_NE (top, nullptr);
3752 ASSERT_TEXT_TOKEN (top->get_token_lists ()[0]->m_first, "In function: ");
3753 ASSERT_EQ (top->get_token_lists ()[1]->m_first->m_kind,
3754 pp_token::kind::begin_quote);
3755 ASSERT_EQ (top->get_token_lists ()[2], nullptr);
3757 /* Expect an entry in the stack below it with:
3758 0: [TEXT("unexpected foo: ")]
3759 1: [TEXT("42")]
3760 2: [TEXT(" bar: ")]
3761 3: [BEGIN_QUOTE, TEXT("test"), END_QUOTE]. */
3762 pp_formatted_chunks *prev = top->get_prev ();
3763 ASSERT_NE (prev, nullptr);
3764 ASSERT_TEXT_TOKEN (prev->get_token_lists ()[0]->m_first, "unexpected foo: ");
3765 ASSERT_TEXT_TOKEN (prev->get_token_lists ()[1]->m_first, "42");
3766 ASSERT_TEXT_TOKEN (prev->get_token_lists ()[2]->m_first, " bar: ");
3767 ASSERT_EQ (prev->get_token_lists ()[3]->m_first->m_kind,
3768 pp_token::kind::begin_quote);
3769 ASSERT_EQ (prev->get_token_lists ()[4], nullptr);
3771 ASSERT_EQ (prev->get_prev (), nullptr);
3773 /* Pop the top of the stack. */
3774 pp_output_formatted_text (&pp);
3775 ASSERT_EQ (pp_buffer (&pp)->m_cur_formatted_chunks, prev);
3776 pp_newline (&pp);
3778 /* Pop the remaining entry from the stack. */
3779 pp_output_formatted_text (&pp);
3780 ASSERT_EQ (pp_buffer (&pp)->m_cur_formatted_chunks, nullptr);
3782 ASSERT_STREQ (pp_formatted_text (&pp),
3783 "In function: `test_fn'\nunexpected foo: 42 bar: `test'");
3786 /* Verify usage of pp_printf from within a pp_element's
3787 add_to_phase_2 vfunc. */
3788 static void
3789 test_pp_printf_within_pp_element ()
3791 class kv_element : public pp_element
3793 public:
3794 kv_element (const char *key, int value)
3795 : m_key (key), m_value (value)
3799 void add_to_phase_2 (pp_markup::context &ctxt) final override
3801 /* We can't call pp_printf directly on ctxt.m_pp from within
3802 formatting. As a workaround, work with a clone of the pp. */
3803 std::unique_ptr<pretty_printer> pp (ctxt.m_pp.clone ());
3804 pp_printf (pp.get (), "(%qs: %qi)", m_key, m_value);
3805 pp_string (&ctxt.m_pp, pp_formatted_text (pp.get ()));
3808 private:
3809 const char *m_key;
3810 int m_value;
3813 auto_fix_quotes fix_quotes;
3815 kv_element e1 ("foo", 42);
3816 kv_element e2 ("bar", 1066);
3817 ASSERT_PP_FORMAT_2 ("before (`foo': `42') (`bar': `1066') after",
3818 "before %e %e after",
3819 &e1, &e2);
3820 assert_pp_format_colored (SELFTEST_LOCATION,
3821 ("before "
3822 "(`\33[01m\33[Kfoo\33[m\33[K'"
3823 ": "
3824 "`\33[01m\33[K42\33[m\33[K')"
3826 "(`\33[01m\33[Kbar\33[m\33[K'"
3827 ": "
3828 "`\33[01m\33[K1066\33[m\33[K')"
3829 " after"),
3830 "before %e %e after",
3831 &e1, &e2);
3834 /* A subclass of pretty_printer for use by test_prefixes_and_wrapping. */
3836 class test_pretty_printer : public pretty_printer
3838 public:
3839 test_pretty_printer (enum diagnostic_prefixing_rule_t rule,
3840 int max_line_length)
3842 pp_set_prefix (this, xstrdup ("PREFIX: "));
3843 pp_prefixing_rule (this) = rule;
3844 pp_set_line_maximum_length (this, max_line_length);
3848 /* Verify that the various values of enum diagnostic_prefixing_rule_t work
3849 as expected, with and without line wrapping. */
3851 static void
3852 test_prefixes_and_wrapping ()
3854 /* Tests of the various prefixing rules, without wrapping.
3855 Newlines embedded in pp_string don't affect it; we have to
3856 explicitly call pp_newline. */
3858 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 0);
3859 pp_string (&pp, "the quick brown fox");
3860 pp_newline (&pp);
3861 pp_string (&pp, "jumps over the lazy dog");
3862 pp_newline (&pp);
3863 ASSERT_STREQ (pp_formatted_text (&pp),
3864 "PREFIX: the quick brown fox\n"
3865 " jumps over the lazy dog\n");
3868 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 0);
3869 pp_string (&pp, "the quick brown fox");
3870 pp_newline (&pp);
3871 pp_string (&pp, "jumps over the lazy dog");
3872 pp_newline (&pp);
3873 ASSERT_STREQ (pp_formatted_text (&pp),
3874 "the quick brown fox\n"
3875 "jumps over the lazy dog\n");
3878 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 0);
3879 pp_string (&pp, "the quick brown fox");
3880 pp_newline (&pp);
3881 pp_string (&pp, "jumps over the lazy dog");
3882 pp_newline (&pp);
3883 ASSERT_STREQ (pp_formatted_text (&pp),
3884 "PREFIX: the quick brown fox\n"
3885 "PREFIX: jumps over the lazy dog\n");
3888 /* Tests of the various prefixing rules, with wrapping. */
3890 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_ONCE, 20);
3891 pp_string (&pp, "the quick brown fox jumps over the lazy dog");
3892 pp_newline (&pp);
3893 pp_string (&pp, "able was I ere I saw elba");
3894 pp_newline (&pp);
3895 ASSERT_STREQ (pp_formatted_text (&pp),
3896 "PREFIX: the quick \n"
3897 " brown fox jumps \n"
3898 " over the lazy \n"
3899 " dog\n"
3900 " able was I ere I \n"
3901 " saw elba\n");
3904 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_NEVER, 20);
3905 pp_string (&pp, "the quick brown fox jumps over the lazy dog");
3906 pp_newline (&pp);
3907 pp_string (&pp, "able was I ere I saw elba");
3908 pp_newline (&pp);
3909 ASSERT_STREQ (pp_formatted_text (&pp),
3910 "the quick brown fox \n"
3911 "jumps over the lazy \n"
3912 "dog\n"
3913 "able was I ere I \n"
3914 "saw elba\n");
3917 test_pretty_printer pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE, 20);
3918 pp_string (&pp, "the quick brown fox jumps over the lazy dog");
3919 pp_newline (&pp);
3920 pp_string (&pp, "able was I ere I saw elba");
3921 pp_newline (&pp);
3922 ASSERT_STREQ (pp_formatted_text (&pp),
3923 "PREFIX: the quick brown fox jumps over the lazy dog\n"
3924 "PREFIX: able was I ere I saw elba\n");
3929 /* Verify that URL-printing works as expected. */
3931 static void
3932 test_urls ()
3935 pretty_printer pp;
3936 pp.set_url_format (URL_FORMAT_NONE);
3937 pp_begin_url (&pp, "http://example.com");
3938 pp_string (&pp, "This is a link");
3939 pp_end_url (&pp);
3940 ASSERT_STREQ ("This is a link",
3941 pp_formatted_text (&pp));
3945 pretty_printer pp;
3946 pp.set_url_format (URL_FORMAT_ST);
3947 pp_begin_url (&pp, "http://example.com");
3948 pp_string (&pp, "This is a link");
3949 pp_end_url (&pp);
3950 ASSERT_STREQ ("\33]8;;http://example.com\33\\This is a link\33]8;;\33\\",
3951 pp_formatted_text (&pp));
3955 pretty_printer pp;
3956 pp.set_url_format (URL_FORMAT_BEL);
3957 pp_begin_url (&pp, "http://example.com");
3958 pp_string (&pp, "This is a link");
3959 pp_end_url (&pp);
3960 ASSERT_STREQ ("\33]8;;http://example.com\aThis is a link\33]8;;\a",
3961 pp_formatted_text (&pp));
3965 static void
3966 test_urls_from_braces ()
3969 pretty_printer pp;
3970 pp.set_url_format (URL_FORMAT_NONE);
3971 pp_printf (&pp, "before %{text%} after",
3972 "http://example.com");
3973 ASSERT_STREQ ("before text after",
3974 pp_formatted_text (&pp));
3978 pretty_printer pp;
3979 pp.set_url_format (URL_FORMAT_ST);
3980 pp_printf (&pp, "before %{text%} after",
3981 "http://example.com");
3982 ASSERT_STREQ ("before \33]8;;http://example.com\33\\text\33]8;;\33\\ after",
3983 pp_formatted_text (&pp));
3987 pretty_printer pp;
3988 pp.set_url_format (URL_FORMAT_BEL);
3989 pp_printf (&pp, "before %{text%} after",
3990 "http://example.com");
3991 ASSERT_STREQ ("before \33]8;;http://example.com\atext\33]8;;\a after",
3992 pp_formatted_text (&pp));
3996 /* Verify that we gracefully reject null URLs. */
3998 static void
3999 test_null_urls ()
4002 pretty_printer pp;
4003 pp.set_url_format (URL_FORMAT_NONE);
4004 pp_begin_url (&pp, nullptr);
4005 pp_string (&pp, "This isn't a link");
4006 pp_end_url (&pp);
4007 ASSERT_STREQ ("This isn't a link",
4008 pp_formatted_text (&pp));
4012 pretty_printer pp;
4013 pp.set_url_format (URL_FORMAT_ST);
4014 pp_begin_url (&pp, nullptr);
4015 pp_string (&pp, "This isn't a link");
4016 pp_end_url (&pp);
4017 ASSERT_STREQ ("This isn't a link",
4018 pp_formatted_text (&pp));
4022 pretty_printer pp;
4023 pp.set_url_format (URL_FORMAT_BEL);
4024 pp_begin_url (&pp, nullptr);
4025 pp_string (&pp, "This isn't a link");
4026 pp_end_url (&pp);
4027 ASSERT_STREQ ("This isn't a link",
4028 pp_formatted_text (&pp));
4032 /* Verify that URLification works as expected. */
4034 static void
4035 pp_printf_with_urlifier (pretty_printer *pp,
4036 const urlifier *urlifier,
4037 const char *msg, ...)
4039 va_list ap;
4041 va_start (ap, msg);
4042 text_info text (msg, &ap, errno);
4043 pp_format (pp, &text);
4044 pp_output_formatted_text (pp, urlifier);
4045 va_end (ap);
4048 static void
4049 test_urlification ()
4051 class test_urlifier : public urlifier
4053 public:
4054 char *
4055 get_url_for_quoted_text (const char *p, size_t sz) const final override
4057 if (!strncmp (p, "-foption", sz))
4058 return xstrdup ("http://example.com");
4059 return nullptr;
4063 auto_fix_quotes fix_quotes;
4064 const test_urlifier urlifier;
4066 /* Uses of "%<" and "%>". */
4069 pretty_printer pp;
4070 pp.set_url_format (URL_FORMAT_NONE);
4071 pp_printf_with_urlifier (&pp, &urlifier,
4072 "foo %<-foption%> %<unrecognized%> bar");
4073 ASSERT_STREQ ("foo `-foption' `unrecognized' bar",
4074 pp_formatted_text (&pp));
4077 pretty_printer pp;
4078 pp.set_url_format (URL_FORMAT_ST);
4079 pp_printf_with_urlifier (&pp, &urlifier,
4080 "foo %<-foption%> %<unrecognized%> bar");
4081 ASSERT_STREQ
4082 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
4083 " `unrecognized' bar",
4084 pp_formatted_text (&pp));
4087 pretty_printer pp;
4088 pp.set_url_format (URL_FORMAT_BEL);
4089 pp_printf_with_urlifier (&pp, &urlifier,
4090 "foo %<-foption%> %<unrecognized%> bar");
4091 ASSERT_STREQ
4092 ("foo `\33]8;;http://example.com\a-foption\33]8;;\a'"
4093 " `unrecognized' bar",
4094 pp_formatted_text (&pp));
4098 /* Use of "%qs". */
4100 pretty_printer pp;
4101 pp.set_url_format (URL_FORMAT_ST);
4102 pp_printf_with_urlifier (&pp, &urlifier,
4103 "foo %qs %qs bar",
4104 "-foption", "unrecognized");
4105 ASSERT_STREQ
4106 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
4107 " `unrecognized' bar",
4108 pp_formatted_text (&pp));
4111 /* Mixed usage of %< and %s, where the quoted string is built between
4112 a mixture of phase 1 and phase 2. */
4114 pretty_printer pp;
4115 pp.set_url_format (URL_FORMAT_ST);
4116 pp_printf_with_urlifier (&pp, &urlifier,
4117 "foo %<-f%s%> bar",
4118 "option");
4119 ASSERT_STREQ
4120 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4121 pp_formatted_text (&pp));
4124 /* Likewise, where there is trailing phase 1 content within the
4125 quoted region. */
4127 pretty_printer pp;
4128 pp.set_url_format (URL_FORMAT_ST);
4129 pp_printf_with_urlifier (&pp, &urlifier,
4130 "foo %<-f%sion%> bar %<-f%sion%> baz",
4131 "opt", "opt");
4132 ASSERT_STREQ
4133 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
4134 pp_formatted_text (&pp));
4137 /* Likewise. */
4139 pretty_printer pp;
4140 pp.set_url_format (URL_FORMAT_ST);
4141 pp_printf_with_urlifier (&pp, &urlifier,
4142 "foo %<%sption%> bar %<-f%sion%> baz",
4143 "-fo", "opt");
4144 ASSERT_STREQ
4145 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
4146 pp_formatted_text (&pp));
4149 /* Another mixed usage of %< and %s, where the quoted string is built
4150 between a mixture of phase 1 and multiple phase 2. */
4152 pretty_printer pp;
4153 pp.set_url_format (URL_FORMAT_ST);
4154 pp_printf_with_urlifier (&pp, &urlifier,
4155 "foo %<-f%s%s%> bar",
4156 "opt", "ion");
4157 ASSERT_STREQ
4158 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4159 pp_formatted_text (&pp));
4162 /* Mixed usage of %< and %s with a prefix. */
4164 pretty_printer pp;
4165 pp.set_url_format (URL_FORMAT_ST);
4166 pp_set_prefix (&pp, xstrdup ("PREFIX"));
4167 pp_printf_with_urlifier (&pp, &urlifier,
4168 "foo %<-f%s%> bar",
4169 "option");
4170 ASSERT_STREQ
4171 ("PREFIXfoo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4172 pp_formatted_text (&pp));
4175 /* Example of mixed %< and %s with numbered args. */
4177 pretty_printer pp;
4178 pp.set_url_format (URL_FORMAT_ST);
4179 pp_printf_with_urlifier (&pp, &urlifier,
4180 "foo %<-f%2$st%1$sn%> bar",
4181 "io", "op");
4182 ASSERT_STREQ
4183 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4184 pp_formatted_text (&pp));
4187 /* Example of %e. */
4189 pretty_printer pp;
4190 pp.set_url_format (URL_FORMAT_ST);
4191 pp_element_quoted_string elem ("-foption");
4192 pp_printf_with_urlifier (&pp, &urlifier,
4193 "foo %e bar",
4194 &elem);
4195 ASSERT_STREQ
4196 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4197 pp_formatted_text (&pp));
4200 /* Test the example from pretty-print-format-impl.h. */
4202 pretty_printer pp;
4203 pp.set_url_format (URL_FORMAT_ST);
4204 pp_printf_with_urlifier (&pp, &urlifier,
4205 "foo: %i, bar: %s, option: %qs",
4206 42, "baz", "-foption");
4207 ASSERT_STREQ (pp_formatted_text (&pp),
4208 "foo: 42, bar: baz, option:"
4209 " `\e]8;;http://example.com\e\\-foption\e]8;;\e\\'");
4213 /* Test multibyte awareness. */
4214 static void test_utf8 ()
4217 /* Check that pp_quoted_string leaves valid UTF-8 alone. */
4219 pretty_printer pp;
4220 const char *s = "\xf0\x9f\x98\x82";
4221 pp_quoted_string (&pp, s);
4222 ASSERT_STREQ (pp_formatted_text (&pp), s);
4225 /* Check that pp_quoted_string escapes non-UTF-8 nonprintable bytes. */
4227 pretty_printer pp;
4228 pp_quoted_string (&pp, "\xf0!\x9f\x98\x82");
4229 ASSERT_STREQ (pp_formatted_text (&pp),
4230 "\\xf0!\\x9f\\x98\\x82");
4233 /* Check that pp_character will line-wrap at the beginning of a UTF-8
4234 sequence, but not in the middle. */
4236 pretty_printer pp (3);
4237 const char s[] = "---\xf0\x9f\x98\x82";
4238 for (int i = 0; i != sizeof (s) - 1; ++i)
4239 pp_character (&pp, s[i]);
4240 pp_newline (&pp);
4241 for (int i = 1; i != sizeof (s) - 1; ++i)
4242 pp_character (&pp, s[i]);
4243 pp_character (&pp, '-');
4244 ASSERT_STREQ (pp_formatted_text (&pp),
4245 "---\n"
4246 "\xf0\x9f\x98\x82\n"
4247 "--\xf0\x9f\x98\x82\n"
4248 "-");
4253 /* Verify that class comma_separated_quoted_strings works as expected. */
4255 static void
4256 test_comma_separated_quoted_strings ()
4258 auto_fix_quotes fix_quotes;
4260 auto_vec<const char *> none;
4261 pp_markup::comma_separated_quoted_strings e_none (none);
4263 auto_vec<const char *> one;
4264 one.safe_push ("one");
4265 pp_markup::comma_separated_quoted_strings e_one (one);
4267 auto_vec<const char *> many;
4268 many.safe_push ("0");
4269 many.safe_push ("1");
4270 many.safe_push ("2");
4271 pp_markup::comma_separated_quoted_strings e_many (many);
4273 ASSERT_PP_FORMAT_3 ("none: () one: (`one') many: (`0', `1', `2')",
4274 "none: (%e) one: (%e) many: (%e)",
4275 &e_none, &e_one, &e_many);
4276 assert_pp_format_colored (SELFTEST_LOCATION,
4277 "one: (`\e[01m\e[Kone\e[m\e[K')",
4278 "one: (%e)",
4279 &e_one);
4282 /* Run all of the selftests within this file. */
4284 void
4285 pretty_print_cc_tests ()
4287 test_basic_printing ();
4288 test_pp_format ();
4289 test_merge_consecutive_text_tokens ();
4290 test_custom_tokens_1 ();
4291 test_custom_tokens_2 ();
4292 test_pp_format_stack ();
4293 test_pp_printf_within_pp_element ();
4294 test_prefixes_and_wrapping ();
4295 test_urls ();
4296 test_urls_from_braces ();
4297 test_null_urls ();
4298 test_urlification ();
4299 test_utf8 ();
4300 test_comma_separated_quoted_strings ();
4303 } // namespace selftest
4305 #endif /* CHECKING_P */