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
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
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/>. */
22 #define INCLUDE_VECTOR
24 #include "coretypes.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"
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. */
49 #define WIN32_LEAN_AND_MEAN 1
52 /* Write all bytes in [s,s+n) into the specified stream.
53 Errors are ignored. */
55 write_all (HANDLE h
, const char *s
, size_t n
)
66 if (!WriteFile (h
, s
+ n
- rem
, step
, &step
, NULL
))
72 /* Find the beginning of an escape sequence.
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. */
82 find_esc_head (int *prefix_len
, const char **head
, const char *str
)
90 c
= (unsigned char) *r
;
96 if (escaped
&& 0x40 <= c
&& c
<= 0x5F)
103 if (0x80 <= c
&& c
<= 0x9F)
105 /* Found (case 2). */
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. */
120 find_esc_terminator (const char **term
, const char *str
)
127 c
= (unsigned char) *r
;
133 if (0x40 <= c
&& c
<= 0x7E)
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. */
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. */
154 CONSOLE_SCREEN_BUFFER_INFO sb
;
156 /* ED and EL parameters. */
159 /* SGR parameters. */
160 WORD attrib_add
, attrib_rm
;
163 switch (MAKEWORD (esc_code
, *esc_term
))
166 Move the cursor up by n1 characters. */
167 case MAKEWORD ('[', 'A'):
168 if (esc_head
== esc_term
)
172 n1
= strtol (esc_head
, &eptr
, 10);
173 if (eptr
!= esc_term
)
177 if (GetConsoleScreenBufferInfo (h
, &sb
))
179 cr
= sb
.dwCursorPosition
;
180 /* Stop at the topmost boundary. */
185 SetConsoleCursorPosition (h
, cr
);
190 Move the cursor down by n1 characters. */
191 case MAKEWORD ('[', 'B'):
192 if (esc_head
== esc_term
)
196 n1
= strtol (esc_head
, &eptr
, 10);
197 if (eptr
!= esc_term
)
201 if (GetConsoleScreenBufferInfo (h
, &sb
))
203 cr
= sb
.dwCursorPosition
;
204 /* Stop at the bottommost boundary. */
205 if (sb
.dwSize
.Y
- cr
.Y
> n1
)
209 SetConsoleCursorPosition (h
, cr
);
214 Move the cursor right by n1 characters. */
215 case MAKEWORD ('[', 'C'):
216 if (esc_head
== esc_term
)
220 n1
= strtol (esc_head
, &eptr
, 10);
221 if (eptr
!= esc_term
)
225 if (GetConsoleScreenBufferInfo (h
, &sb
))
227 cr
= sb
.dwCursorPosition
;
228 /* Stop at the rightmost boundary. */
229 if (sb
.dwSize
.X
- cr
.X
> n1
)
233 SetConsoleCursorPosition (h
, cr
);
238 Move the cursor left by n1 characters. */
239 case MAKEWORD ('[', 'D'):
240 if (esc_head
== esc_term
)
244 n1
= strtol (esc_head
, &eptr
, 10);
245 if (eptr
!= esc_term
)
249 if (GetConsoleScreenBufferInfo (h
, &sb
))
251 cr
= sb
.dwCursorPosition
;
252 /* Stop at the leftmost boundary. */
257 SetConsoleCursorPosition (h
, cr
);
262 Move the cursor to the beginning of the n1-th line downwards. */
263 case MAKEWORD ('[', 'E'):
264 if (esc_head
== esc_term
)
268 n1
= strtol (esc_head
, &eptr
, 10);
269 if (eptr
!= esc_term
)
273 if (GetConsoleScreenBufferInfo (h
, &sb
))
275 cr
= sb
.dwCursorPosition
;
277 /* Stop at the bottommost boundary. */
278 if (sb
.dwSize
.Y
- cr
.Y
> n1
)
282 SetConsoleCursorPosition (h
, cr
);
287 Move the cursor to the beginning of the n1-th line upwards. */
288 case MAKEWORD ('[', 'F'):
289 if (esc_head
== esc_term
)
293 n1
= strtol (esc_head
, &eptr
, 10);
294 if (eptr
!= esc_term
)
298 if (GetConsoleScreenBufferInfo (h
, &sb
))
300 cr
= sb
.dwCursorPosition
;
302 /* Stop at the topmost boundary. */
307 SetConsoleCursorPosition (h
, cr
);
312 Move the cursor to the (1-based) n1-th column. */
313 case MAKEWORD ('[', 'G'):
314 if (esc_head
== esc_term
)
318 n1
= strtol (esc_head
, &eptr
, 10);
319 if (eptr
!= esc_term
)
323 if (GetConsoleScreenBufferInfo (h
, &sb
))
325 cr
= sb
.dwCursorPosition
;
327 /* Stop at the leftmost or rightmost boundary. */
330 else if (n1
> sb
.dwSize
.X
)
334 SetConsoleCursorPosition (h
, cr
);
338 /* ESC [ n1 ';' n2 'H'
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. */
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
)
362 /* Both parameters are given. The first one shall be
363 terminated by the semicolon. */
364 n1
= strtol (esc_head
, &eptr
, 10);
367 n2
= strtol (delim
+ 1, &eptr
, 10);
368 if (eptr
!= esc_term
)
372 if (GetConsoleScreenBufferInfo (h
, &sb
))
374 cr
= sb
.dwCursorPosition
;
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. */
387 else if (n1
> sb
.dwSize
.Y
)
391 /* Stop at the leftmost or rightmost boundary. */
394 else if (n2
> sb
.dwSize
.X
)
398 SetConsoleCursorPosition (h
, cr
);
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. */
411 n1
= strtol (esc_head
, &eptr
, 10);
412 if (eptr
!= esc_term
)
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. */
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
;
430 /* If the cursor is in or under the window, erase from
431 it to the top of the window; otherwise, do nothing. */
433 cr
.Y
= sb
.srWindow
.Top
;
434 cnt
= sb
.dwCursorPosition
.X
+ 1;
435 rows
= sb
.dwCursorPosition
.Y
- sb
.srWindow
.Top
;
438 /* Erase the entire window. */
439 cr
.X
= sb
.srWindow
.Left
;
440 cr
.Y
= sb
.srWindow
.Top
;
442 rows
= sb
.srWindow
.Bottom
- sb
.srWindow
.Top
+ 1;
445 /* Erase the entire buffer. */
454 cnt
+= rows
* sb
.dwSize
.X
;
455 FillConsoleOutputCharacterW (h
, L
' ', cnt
, cr
, &step
);
456 FillConsoleOutputAttribute (h
, sb
.wAttributes
, cnt
, cr
, &step
);
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. */
469 n1
= strtol (esc_head
, &eptr
, 10);
470 if (eptr
!= esc_term
)
474 if (GetConsoleScreenBufferInfo (h
, &sb
))
479 /* Erase from the cursor to the end. */
480 cr
= sb
.dwCursorPosition
;
481 cnt
= sb
.dwSize
.X
- sb
.dwCursorPosition
.X
;
484 /* Erase from the cursor to the beginning. */
485 cr
= sb
.dwCursorPosition
;
487 cnt
= sb
.dwCursorPosition
.X
+ 1;
490 /* Erase the entire line. */
491 cr
= sb
.dwCursorPosition
;
496 FillConsoleOutputCharacterW (h
, L
' ', cnt
, cr
, &step
);
497 FillConsoleOutputAttribute (h
, sb
.wAttributes
, cnt
, cr
, &step
);
501 /* ESC [ n1 ';' n2 'm'
502 Set SGR parameters. Zero or more parameters will follow. */
503 case MAKEWORD ('[', 'm'):
506 if (esc_head
== esc_term
)
508 /* When no parameter is given, reset the console. */
509 attrib_add
|= (FOREGROUND_RED
| FOREGROUND_GREEN
511 attrib_rm
= -1; /* Removes everything. */
517 /* Parse a parameter. */
518 n1
= strtol (param
, &eptr
, 10);
519 if (*eptr
!= ';' && eptr
!= esc_term
)
526 attrib_add
|= (FOREGROUND_RED
| FOREGROUND_GREEN
528 attrib_rm
= -1; /* Removes everything. */
532 attrib_add
|= FOREGROUND_INTENSITY
;
536 attrib_add
|= COMMON_LVB_UNDERSCORE
;
540 /* XXX: It is not BLINKING at all! */
541 attrib_add
|= BACKGROUND_INTENSITY
;
545 attrib_add
|= COMMON_LVB_REVERSE_VIDEO
;
549 attrib_add
&= ~FOREGROUND_INTENSITY
;
550 attrib_rm
|= FOREGROUND_INTENSITY
;
554 attrib_add
&= ~COMMON_LVB_UNDERSCORE
;
555 attrib_rm
|= COMMON_LVB_UNDERSCORE
;
559 /* XXX: It is not BLINKING at all! */
560 attrib_add
&= ~BACKGROUND_INTENSITY
;
561 attrib_rm
|= BACKGROUND_INTENSITY
;
565 attrib_add
&= ~COMMON_LVB_REVERSE_VIDEO
;
566 attrib_rm
|= COMMON_LVB_REVERSE_VIDEO
;
576 /* Foreground color. */
577 attrib_add
&= ~(FOREGROUND_RED
| FOREGROUND_GREEN
581 attrib_add
|= FOREGROUND_RED
;
583 attrib_add
|= FOREGROUND_GREEN
;
585 attrib_add
|= FOREGROUND_BLUE
;
586 attrib_rm
|= (FOREGROUND_RED
| FOREGROUND_GREEN
590 /* Reserved for extended foreground color.
591 Don't know how to handle parameters remaining.
595 /* Reset foreground color. */
597 attrib_add
|= (FOREGROUND_RED
| FOREGROUND_GREEN
599 attrib_rm
|= (FOREGROUND_RED
| FOREGROUND_GREEN
610 /* Background color. */
611 attrib_add
&= ~(BACKGROUND_RED
| BACKGROUND_GREEN
615 attrib_add
|= BACKGROUND_RED
;
617 attrib_add
|= BACKGROUND_GREEN
;
619 attrib_add
|= BACKGROUND_BLUE
;
620 attrib_rm
|= (BACKGROUND_RED
| BACKGROUND_GREEN
624 /* Reserved for extended background color.
625 Don't know how to handle parameters remaining.
629 /* Reset background color. */
631 attrib_add
&= ~(BACKGROUND_RED
| BACKGROUND_GREEN
633 attrib_rm
|= (BACKGROUND_RED
| BACKGROUND_GREEN
638 /* Prepare the next parameter. */
641 while (param
!= esc_term
);
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
);
666 mingw_ansi_fputs (const char *str
, FILE *fp
)
668 const char *read
= str
;
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
)
678 /* Don't mess up stdio functions with Windows APIs. */
681 if (GetConsoleMode (h
, &mode
)
682 #ifdef ENABLE_VIRTUAL_TERMINAL_PROCESSING
683 && !(mode
& ENABLE_VIRTUAL_TERMINAL_PROCESSING
)
686 /* If it is a console, and doesn't support ANSI escape codes, translate
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
));
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
701 write_all (h
, read
, esc_head
- prefix_len
- read
);
702 eat_esc_sequence (h
, esc_code
, esc_head
, esc_term
);
706 /* If it is not a console, write everything as-is. */
707 write_all (h
, read
, strlen (read
));
712 #endif /* __MINGW32__ */
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);
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. */
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
);
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 ();
741 return UNKNOWN_LOCATION
;
744 // Default construct an output buffer.
746 output_buffer::output_buffer ()
747 : m_formatted_obstack (),
749 m_obstack (&m_formatted_obstack
),
750 m_cur_formatted_chunks (nullptr),
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
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;
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
;
807 fprintf (out
, "%02x ", ((const unsigned char *)buf
)[idx
]);
812 for (size_t offset
= 0; offset
< bytes_per_hexdump_line
; ++offset
)
814 const size_t idx
= line_start_idx
+ offset
;
817 unsigned char ch
= ((const unsigned char *)buf
)[idx
];
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. */
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
);
857 for (pp_formatted_chunks
*iter
= m_cur_formatted_chunks
;
859 iter
= iter
->m_prev
, depth
++)
861 fprintf (out
, "%*spp_formatted_chunks: depth %i\n",
864 iter
->dump (out
, indent
+ 2);
869 #define PTRDIFF_MAX INTTYPE_MAXIMUM (ptrdiff_t)
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) \
880 pp_scalar (PP, "%" F, va_arg (ARG, T)); \
884 pp_scalar (PP, "%l" F, va_arg (ARG, long T)); \
888 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
889 va_arg (ARG, long long T)); \
893 if (T (-1) < T (0)) \
894 pp_scalar (PP, "%" GCC_PRISZ F, \
895 (fmt_size_t) va_arg (ARG, ssize_t)); \
897 pp_scalar (PP, "%" GCC_PRISZ F, \
898 (fmt_size_t) va_arg (ARG, size_t)); \
902 if (T (-1) >= T (0)) \
904 unsigned long long a = va_arg (ARG, ptrdiff_t); \
905 unsigned long long m = PTRDIFF_MAX; \
907 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
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)); \
917 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
919 va_arg (ARG, ptrdiff_t)); \
928 /* Subroutine of pp_set_maximum_length. Set up PRETTY-PRINTER's
929 internal maximum characters per line. */
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);
943 int prefix_length
= m_prefix
? strlen (m_prefix
) : 0;
944 /* If the prefix is ridiculously too long, output at least
946 if (pp_line_cutoff (this) - prefix_length
< 32)
947 m_maximum_length
= pp_line_cutoff (this) + 32;
949 m_maximum_length
= pp_line_cutoff (this);
953 /* Clear this pretty_printer's output state. */
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
>
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]);
970 pp_left_bracket (pp
);
971 for (unsigned int i
= 0; i
< N
; ++i
)
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. */
987 pp_write_text_to_stream (pretty_printer
*pp
)
989 const char *text
= pp_formatted_text (pp
);
991 mingw_ansi_fputs (text
, pp_buffer (pp
)->m_stream
);
993 fputs (text
, pp_buffer (pp
)->m_stream
);
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
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. */
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
;
1019 /* Print newlines as a left-aligned newline. */
1025 /* The following characters are only special for record-shape nodes. */
1032 escape_char
= for_record
;
1035 /* The following characters always have to be escaped
1036 for use in labels. */
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');
1048 escape_char
= false;
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
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 "'" or in HTML as "'");.
1074 Experiments show that escaping "'" doesn't seem to be necessary. */
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
;
1088 fputs (""", fp
);
1091 fputs ("&", fp
);
1106 pp_clear_output_area (pp
);
1109 /* Wrap a text delimited by START and END into PRETTY-PRINTER. */
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')
1123 && p
- start
>= pp
->remaining_character_count_for_line ())
1125 pp_append_text (pp
, start
, p
);
1129 if (start
!= end
&& ISBLANK (*start
))
1134 if (start
!= end
&& *start
== '\n')
1142 /* Same as pp_wrap_text but wrap text only when in line-wrapping mode. */
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
);
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. */
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. */
1164 pp_indent (pretty_printer
*pp
)
1166 int n
= pp_indentation (pp
);
1169 for (i
= 0; i
< n
; ++i
)
1173 static const char *get_end_url_string (pretty_printer
*);
1175 /* struct pp_token. */
1177 pp_token::pp_token (enum kind k
)
1185 pp_token::dump (FILE *out
) const
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 ());
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 ());
1206 case kind::end_color
:
1207 fprintf (out
, "END_COLOR");
1209 case kind::begin_quote
:
1210 fprintf (out
, "BEGIN_QUOTE");
1212 case kind::end_quote
:
1213 fprintf (out
, "END_QUOTE");
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 ());
1224 fprintf (out
, "END_URL");
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 ());
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
);
1249 /* Allocate SZ bytes within S, which must not be half-way through
1250 building another object. */
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
);
1263 /* Make room for a pp_token instance within obstack S. */
1266 pp_token::operator new (size_t sz
, obstack
&s
)
1268 return allocate_object (sz
, s
);
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. */
1283 pp_token_list::operator new (size_t sz
, obstack
&s
)
1285 return allocate_object (sz
, s
);
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
)
1303 pp_token_list::pp_token_list (pp_token_list
&&other
)
1304 : m_obstack (other
.m_obstack
),
1305 m_first (other
.m_first
),
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
;
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
));
1331 pp_token_list::push_back (std::unique_ptr
<pp_token
> tok
)
1335 gcc_assert (m_end
== nullptr);
1336 m_first
= tok
.get ();
1341 gcc_assert (m_end
!= nullptr);
1342 m_end
->m_next
= tok
.get ();
1343 tok
->m_prev
= m_end
;
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)
1363 gcc_assert (result
->m_prev
== nullptr);
1364 m_first
= result
->m_next
;
1367 gcc_assert (result
!= m_end
);
1368 m_first
->m_prev
= nullptr;
1372 gcc_assert (result
== m_end
);
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
)
1385 gcc_assert (tok
!= m_first
);
1386 tok
->m_prev
->m_next
= tok
->m_next
;
1390 gcc_assert (tok
== m_first
);
1391 m_first
= tok
->m_next
;
1395 gcc_assert (tok
!= m_end
);
1396 tok
->m_next
->m_prev
= tok
->m_prev
;
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. */
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
;
1430 gcc_assert (relative_tok
== m_end
);
1433 new_tok
->m_prev
= relative_tok
;
1434 new_tok
->m_next
= relative_tok
->m_next
;
1435 relative_tok
->m_next
= new_tok
;
1439 pp_token_list::replace_custom_tokens ()
1441 pp_token
*iter
= m_first
;
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
1455 gcc_assert (tok
->m_kind
!= pp_token::kind::custom_data
);
1456 insert_after (std::move (tok
), iter
);
1458 remove_token (iter
);
1465 /* Merge any runs of consecutive text tokens within this list
1466 into individual text tokens. */
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
;
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
1488 /* Calculate size of buffer for merged text. */
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
);
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
);
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
;
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. */
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
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 ())))
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
;
1557 iter
= iter
->m_next
;
1562 pp_token_list::dump (FILE *out
) const
1564 for (auto iter
= m_first
; iter
; iter
= 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. */
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;
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: ",
1597 m_args
[idx
]->dump (out
);
1601 /* Finish any text accumulating within CUR_OBSTACK,
1603 Push a text pp_token to the end of TOK_LIST containing
1604 a borrowed copy of the text in CUR_OBSTACK. */
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
,
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.
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.
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
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
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
1669 format_phase_1 (const text_info
&text
,
1670 obstack
&chunk_obstack
,
1671 pp_token_list
**args
,
1672 pp_token_list
***formatters
);
1675 format_phase_2 (pretty_printer
*pp
,
1677 obstack
&chunk_obstack
,
1678 pp_token_list
***formatters
);
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
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:
1705 /* Set output to the argument obstack, and switch line-wrapping and
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"
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
;
1726 format_phase_1 (const text_info
&text
,
1727 obstack
&chunk_obstack
,
1728 pp_token_list
**args
,
1729 pp_token_list
***formatters
)
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
);
1753 obstack_1grow (&chunk_obstack
, '%');
1759 push_back_any_text (cur_token_list
, &chunk_obstack
);
1760 cur_token_list
->push_back
<pp_token_begin_quote
> ();
1767 push_back_any_text (cur_token_list
, &chunk_obstack
);
1768 cur_token_list
->push_back
<pp_token_end_quote
> ();
1774 push_back_any_text (cur_token_list
, &chunk_obstack
);
1775 cur_token_list
->push_back
<pp_token_end_quote
> ();
1782 push_back_any_text (cur_token_list
, &chunk_obstack
);
1783 cur_token_list
->push_back
<pp_token_end_url
> ();
1790 push_back_any_text (cur_token_list
, &chunk_obstack
);
1791 cur_token_list
->push_back
<pp_token_end_color
> ();
1798 const char *errstr
= xstrerror (text
.m_err_no
);
1799 obstack_grow (&chunk_obstack
, errstr
, strlen (errstr
));
1805 /* Handled in phase 2. Terminate the plain chunk here. */
1806 push_back_any_text (cur_token_list
, &chunk_obstack
);
1810 /* Start a new token list for the formatting args. */
1811 args
[chunk
] = cur_token_list
= pp_token_list::make (chunk_obstack
);
1817 argno
= strtoul (p
, &end
, 10) - 1;
1819 gcc_assert (*p
== '$');
1822 any_numbered
= true;
1823 gcc_assert (!any_unnumbered
);
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
);
1839 while (strchr ("qwlzt+#", p
[-1]));
1843 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1844 (where M == N + 1). */
1849 obstack_1grow (&chunk_obstack
, *p
);
1852 while (ISDIGIT (p
[-1]));
1853 gcc_assert (p
[-1] == 's');
1857 gcc_assert (*p
== '*');
1858 obstack_1grow (&chunk_obstack
, '*');
1864 unsigned int argno2
= strtoul (p
, &end
, 10) - 1;
1866 gcc_assert (argno2
== argno
- 1);
1867 gcc_assert (!any_unnumbered
);
1868 gcc_assert (*p
== '$');
1871 formatters
[argno2
] = formatters
[argno
];
1875 gcc_assert (!any_numbered
);
1876 formatters
[argno
+1] = formatters
[argno
];
1879 gcc_assert (*p
== 's');
1880 obstack_1grow (&chunk_obstack
, 's');
1886 push_back_any_text (cur_token_list
, &chunk_obstack
);
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. */
1908 format_phase_2 (pretty_printer
*pp
,
1910 obstack
&chunk_obstack
,
1911 pp_token_list
***formatters
)
1914 for (argno
= 0; formatters
[argno
]; argno
++)
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
1936 for (p
= as_a
<pp_token_text
*> (tok_list
->m_first
)->m_value
.get ();; p
++)
1941 gcc_assert (!quote
);
1961 gcc_assert (!precision
);
1966 gcc_assert (!precision
);
1971 /* We don't support precision beyond that of "long long". */
1972 gcc_assert (precision
< 2);
1979 gcc_assert (!wide
|| precision
== 0);
1983 push_back_any_text (formatted_tok_list
, &chunk_obstack
);
1984 formatted_tok_list
->push_back
<pp_token_begin_quote
> ();
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
));
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
);
2007 const char str
[2] = { chr
, '\0' };
2008 pp_quoted_string (pp
, str
, 1);
2016 pp_wide_integer (pp
, va_arg (*text
.m_args_ptr
, HOST_WIDE_INT
));
2018 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
2024 pp_scalar (pp
, "%" HOST_WIDE_INT_PRINT
"o",
2025 va_arg (*text
.m_args_ptr
, unsigned HOST_WIDE_INT
));
2027 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
2033 pp_quoted_string (pp
, va_arg (*text
.m_args_ptr
, const char *));
2035 pp_string (pp
, va_arg (*text
.m_args_ptr
, const char *));
2039 pp_pointer (pp
, va_arg (*text
.m_args_ptr
, void *));
2044 pp_scalar (pp
, HOST_WIDE_INT_PRINT_UNSIGNED
,
2045 va_arg (*text
.m_args_ptr
, unsigned HOST_WIDE_INT
));
2047 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
2052 pp_double (pp
, va_arg (*text
.m_args_ptr
, double));
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
]);
2074 pp_scalar (pp
, HOST_WIDE_INT_PRINT_HEX
,
2075 va_arg (*text
.m_args_ptr
, unsigned HOST_WIDE_INT
));
2077 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
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. */
2093 n
= strtoul (p
, &end
, 10);
2095 gcc_assert (*p
== 's');
2099 gcc_assert (*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]);
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
);
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
);
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
));
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
);
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
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
, "e
,
2160 *formatted_tok_list
);
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
;
2177 for (; argno
< PP_NL_ARGMAX
; argno
++)
2178 gcc_assert (!formatters
[argno
]);
2185 obstack_init (&m_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
);
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. */
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 ();
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
);
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. */
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
)
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 ());
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 ()));
2290 case pp_token::kind::end_color
:
2291 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
2294 case pp_token::kind::begin_quote
:
2295 pp_begin_quote (pp
, pp_show_color (pp
));
2297 case pp_token::kind::end_quote
:
2298 pp_end_quote (pp
, pp_show_color (pp
));
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 ());
2307 case pp_token::kind::end_url
:
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
)));
2323 case pp_token::kind::custom_data
:
2324 /* These should have been eliminated by replace_custom_tokens. */
2330 /* Helper subroutine of output_verbatim and verbatim. Do the appropriate
2331 settings needed by BUFFER for a verbatim formatting. */
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. */
2349 pp_flush (pretty_printer
*pp
)
2352 if (!pp_buffer (pp
)->m_flush_p
)
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. */
2361 pp_really_flush (pretty_printer
*pp
)
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
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. */
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. */
2391 pretty_printer::set_prefix (char *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. */
2405 pp_take_prefix (pretty_printer
*pp
)
2407 char *result
= pp
->m_prefix
;
2408 pp
->m_prefix
= nullptr;
2412 /* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */
2414 pp_destroy_prefix (pretty_printer
*pp
)
2418 free (pp
->m_prefix
);
2419 pp
->m_prefix
= nullptr;
2423 /* Write out this pretty_printer's prefix. */
2425 pretty_printer::emit_prefix ()
2429 switch (pp_prefixing_rule (this))
2432 case DIAGNOSTICS_SHOW_PREFIX_NEVER
:
2435 case DIAGNOSTICS_SHOW_PREFIX_ONCE
:
2436 if (m_emitted_prefix
)
2441 pp_indentation (this) += 3;
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;
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 ()),
2460 m_padding (pp_none
),
2461 m_maximum_length (0),
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 ()),
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 ();
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
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)
2539 if (pp_is_wrapping_line (pp
))
2540 while (start
!= end
&& *start
== ' ')
2543 pp_append_r (pp
, start
, end
- start
);
2546 /* Finishes constructing a NULL-terminated character string representing
2547 the PRETTY-PRINTED text. */
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. */
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. */
2572 pp_printf (pretty_printer
*pp
, const char *msg
, ...)
2577 text_info
text (msg
, &ap
, errno
);
2578 pp_format (pp
, &text
);
2579 pp_output_formatted_text (pp
);
2583 /* Format a message into PP using ngettext to handle
2584 singular vs plural. */
2587 pp_printf_n (pretty_printer
*pp
,
2588 unsigned HOST_WIDE_INT n
,
2589 const char *singular_gmsgid
, const char *plural_gmsgid
, ...)
2593 va_start (ap
, plural_gmsgid
);
2596 if (sizeof n
<= sizeof gtn
)
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
);
2610 /* Output MESSAGE verbatim into BUFFER. */
2612 pp_verbatim (pretty_printer
*pp
, const char *msg
, ...)
2617 text_info
text (msg
, &ap
, errno
);
2618 pp_format_verbatim (pp
, &text
);
2624 /* Have PRETTY-PRINTER start a new line. */
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. */
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)
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. */
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. */
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
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 };
2677 uchar buf
[6], *p
= &buf
[6];
2686 *--p
= ((c
& 0x3F) | 0x80);
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. */
2702 pp_quoted_string (pretty_printer
*pp
, const char *str
, size_t n
/* = -1 */)
2704 gcc_checking_assert (str
);
2706 const char *last
= str
;
2709 /* Compute the length if not specified. */
2710 if (n
== (size_t) -1)
2713 for (ps
= str
; n
; ++ps
, --n
)
2718 /* Don't escape a valid UTF-8 extended char. */
2719 const unsigned char *ups
= (const unsigned char *) ps
;
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;
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. */
2738 int n
= sprintf (buf
, "\\x%02x", (unsigned char)*ps
);
2739 pp_maybe_wrap_text (pp
, buf
, buf
+ n
);
2743 pp_maybe_wrap_text (pp
, last
, ps
);
2746 /* Maybe print out a whitespace if needed. */
2749 pretty_printer::maybe_space ()
2751 if (m_padding
!= pp_none
)
2754 m_padding
= pp_none
;
2758 // Add a newline to the pretty printer PP and flush formatted text.
2761 pp_newline_and_flush (pretty_printer
*pp
)
2765 pp_needs_newline (pp
) = false;
2768 // Add a newline to the pretty printer PP, followed by indentation.
2771 pp_newline_and_indent (pretty_printer
*pp
, int n
)
2773 pp_indentation (pp
) += n
;
2776 pp_needs_newline (pp
) = false;
2779 // Add separator C, followed by a single whitespace.
2782 pp_separate_with (pretty_printer
*pp
, char c
)
2784 pp_character (pp
, c
);
2788 /* Add a localized open quote, and if SHOW_COLOR is true, begin colorizing
2789 using the "quote" color. */
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. */
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. */
2815 decode_utf8_char (const unsigned char *p
, size_t len
, unsigned int *value
)
2817 unsigned int t
= *p
;
2823 size_t utf8_len
= 0;
2826 for (t
= *p
; t
& 0x80; t
<<= 1)
2829 if (utf8_len
> len
|| utf8_len
< 2 || utf8_len
> 6)
2831 *value
= (unsigned int) -1;
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;
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;
2865 /* Allocator for identifier_to_locale and corresponding function to
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. */
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;
2890 for (i
= 0; i
< idlen
;)
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;
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);
2912 for (i
= 0; i
< idlen
; i
++)
2914 if (uid
[i
] > 0x1F && uid
[i
] < 0x7F)
2918 sprintf (p
, "\\%03o", uid
[i
]);
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
)
2931 /* Otherwise IDENT is converted to the locale character set if
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;
2939 if (cd
!= (iconv_t
) -1)
2941 size_t ret_alloc
= 4 * idlen
+ 1;
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
);
2949 size_t inbytesleft
= idlen
;
2950 size_t outbytesleft
= ret_alloc
- 1;
2953 ret
= (char *) identifier_to_locale_alloc (ret_alloc
);
2956 if (iconv (cd
, 0, 0, 0, 0) == (size_t) -1)
2958 conversion_ok
= false;
2962 iconv_ret
= iconv (cd
, &inbuf
, &inbytesleft
,
2963 &outbuf
, &outbytesleft
);
2964 if (iconv_ret
== (size_t) -1 || inbytesleft
!= 0)
2969 identifier_to_locale_free (ret
);
2975 conversion_ok
= false;
2979 else if (iconv_ret
!= 0)
2981 conversion_ok
= false;
2984 /* Return to initial shift state. */
2985 if (iconv (cd
, 0, 0, &outbuf
, &outbytesleft
) == (size_t) -1)
2990 identifier_to_locale_free (ret
);
2996 conversion_ok
= false;
3010 /* Otherwise, convert non-ASCII characters in IDENT to UCNs. */
3012 char *ret
= (char *) identifier_to_locale_alloc (10 * idlen
+ 1);
3014 for (i
= 0; i
< idlen
;)
3017 size_t utf8_len
= decode_utf8_char (&uid
[i
], idlen
- i
, &c
);
3022 sprintf (p
, "\\U%08x", c
);
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:
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. */
3055 pretty_printer::begin_url (const char *url
)
3059 /* Handle null URL by skipping all output here,
3060 and in the next pp_end_url. */
3061 m_skipping_null_url
= true;
3064 switch (m_url_format
)
3066 case URL_FORMAT_NONE
:
3069 pp_string (this, "\33]8;;");
3070 pp_string (this, url
);
3071 pp_string (this, "\33\\");
3073 case URL_FORMAT_BEL
:
3074 pp_string (this, "\33]8;;");
3075 pp_string (this, url
);
3076 pp_string (this, "\a");
3083 /* Helper function for pp_end_url and pp_format, return the "close URL" escape
3087 get_end_url_string (pretty_printer
*pp
)
3089 switch (pp
->get_url_format ())
3091 case URL_FORMAT_NONE
:
3094 return "\33]8;;\33\\";
3095 case URL_FORMAT_BEL
:
3102 /* If URL-printing is enabled, write a "close URL" escape sequence to PP. */
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;
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. */
3121 pretty_printer::dump (FILE *out
, int indent
) const
3123 fprintf (out
, "%*sm_show_color: %s\n",
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");
3134 fprintf (out
, "st");
3136 case URL_FORMAT_BEL
:
3137 fprintf (out
, "bel");
3142 fprintf (out
, "\n");
3144 fprintf (out
, "%*sm_buffer:\n", indent
, "");
3145 m_buffer
->dump (out
, indent
+ 2);
3148 /* class pp_markup::context. */
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
> ();
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'}". */
3167 gcc_assert (m_formatted_token_list
);
3168 push_back_any_text ();
3169 m_formatted_token_list
->push_back
<pp_token_end_quote
> ();
3174 pp_markup::context::begin_highlight_color (const char *color_name
)
3176 if (!pp_show_highlight_colors (&m_pp
))
3179 push_back_any_text ();
3180 m_formatted_token_list
->push_back
<pp_token_begin_color
>
3181 (label_text::borrow (color_name
));
3185 pp_markup::context::end_highlight_color ()
3187 if (!pp_show_highlight_colors (&m_pp
))
3190 push_back_any_text ();
3191 m_formatted_token_list
->push_back
<pp_token_end_color
> ();
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
,
3205 pp_markup::comma_separated_quoted_strings::add_to_phase_2 (context
&ctxt
)
3207 for (unsigned i
= 0; i
< m_strings
.length (); i
++)
3210 pp_string (&ctxt
.m_pp
, ", ");
3211 ctxt
.begin_quote ();
3212 pp_string (&ctxt
.m_pp
, m_strings
[i
]);
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";
3227 namespace selftest
{
3229 /* Smoketest for pretty_printer. */
3232 test_basic_printing ()
3235 pp_string (&pp
, "hello");
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. */
3247 assert_pp_format_va (const location
&loc
, const char *expected
,
3248 bool show_color
, const char *fmt
, va_list *ap
)
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. */
3265 assert_pp_format (const location
&loc
, const char *expected
,
3266 const char *fmt
, ...)
3271 assert_pp_format_va (loc
, expected
, false, fmt
, &ap
);
3275 /* As above, but with colorization enabled. */
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"))
3290 assert_pp_format_va (loc
, expected
, true, fmt
, &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), \
3305 #define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \
3306 SELFTEST_BEGIN_STMT \
3307 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
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)); \
3317 /* Verify that pp_format works, for various format codes. */
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,
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,
3350 ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", HOST_WIDE_INT_C (-27),
3352 ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", HOST_WIDE_INT_C (-5),
3354 ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", HOST_WIDE_INT_UC (10),
3356 ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", HOST_WIDE_INT_C (15),
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,
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,
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",
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
3385 ASSERT_PP_FORMAT_2 ("normal colored normal 12345678",
3386 "normal %rcolored%R normal %x",
3387 "error", 0x12345678);
3388 assert_pp_format_colored
3390 "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678",
3391 "normal %rcolored%R normal %x", "error", 0x12345678);
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",
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 %@",
3414 assert_pp_format_colored
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 %@",
3423 int v
[] = { 1, 2, 3 };
3424 ASSERT_PP_FORMAT_3 ("1, 2, 3 12345678", "%Z %x", v
, 3, 0x12345678);
3427 ASSERT_PP_FORMAT_3 ("0 12345678", "%Z %x", v2
, 1, 0x12345678);
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",
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",
3452 assert_pp_format (SELFTEST_LOCATION
,
3453 "foo: 1066 bar: 1776",
3454 "foo: %2$i bar: %1$i",
3456 assert_pp_format (SELFTEST_LOCATION
,
3457 "foo: second bar: 1776",
3458 "foo: %2$s bar: %1$i",
3460 assert_pp_format (SELFTEST_LOCATION
,
3461 "foo: sec bar: 3360",
3462 "foo: %3$.*2$s bar: %1$o",
3464 assert_pp_format (SELFTEST_LOCATION
,
3465 "foo: seco bar: 3360",
3466 "foo: %2$.4s bar: %1$o",
3471 test_merge_consecutive_text_tokens ()
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
3493 test_custom_tokens_1 ()
3495 struct custom_token_adder
: public pp_element
3498 struct value
: public pp_token_custom_data::value
3500 value (custom_token_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;
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
));
3534 custom_token_adder
&m_adder
;
3537 custom_token_adder (const char *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
));
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);
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. */
3575 test_custom_tokens_2 ()
3577 struct custom_token_adder
: public pp_element
3579 struct value
: public pp_token_custom_data::value
3582 value (custom_token_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;
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
3614 custom_token_adder
&m_adder
;
3617 custom_token_adder (const char *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
));
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"),
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 (),
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 (),
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 (),
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
;
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. */
3713 push_pp_format (pretty_printer
*pp
, const char *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
);
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); \
3729 (as_a <const pp_token_text *> (TOKEN)->m_value.get (), \
3734 /* Verify that the stack of pp_formatted_chunks works as expected. */
3737 test_pp_format_stack ()
3739 auto_fix_quotes fix_quotes
;
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: ")]
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
);
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. */
3789 test_pp_printf_within_pp_element ()
3791 class kv_element
: public pp_element
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 ()));
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",
3820 assert_pp_format_colored (SELFTEST_LOCATION
,
3822 "(`\33[01m\33[Kfoo\33[m\33[K'"
3824 "`\33[01m\33[K42\33[m\33[K')"
3826 "(`\33[01m\33[Kbar\33[m\33[K'"
3828 "`\33[01m\33[K1066\33[m\33[K')"
3830 "before %e %e after",
3834 /* A subclass of pretty_printer for use by test_prefixes_and_wrapping. */
3836 class test_pretty_printer
: public pretty_printer
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. */
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");
3861 pp_string (&pp
, "jumps over the lazy dog");
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");
3871 pp_string (&pp
, "jumps over the lazy dog");
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");
3881 pp_string (&pp
, "jumps over the lazy dog");
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");
3893 pp_string (&pp
, "able was I ere I saw elba");
3895 ASSERT_STREQ (pp_formatted_text (&pp
),
3896 "PREFIX: the quick \n"
3897 " brown fox jumps \n"
3900 " able was I ere I \n"
3904 test_pretty_printer
pp (DIAGNOSTICS_SHOW_PREFIX_NEVER
, 20);
3905 pp_string (&pp
, "the quick brown fox jumps over the lazy dog");
3907 pp_string (&pp
, "able was I ere I saw elba");
3909 ASSERT_STREQ (pp_formatted_text (&pp
),
3910 "the quick brown fox \n"
3911 "jumps over the lazy \n"
3913 "able was I ere I \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");
3920 pp_string (&pp
, "able was I ere I saw elba");
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. */
3936 pp
.set_url_format (URL_FORMAT_NONE
);
3937 pp_begin_url (&pp
, "http://example.com");
3938 pp_string (&pp
, "This is a link");
3940 ASSERT_STREQ ("This is a link",
3941 pp_formatted_text (&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");
3950 ASSERT_STREQ ("\33]8;;http://example.com\33\\This is a link\33]8;;\33\\",
3951 pp_formatted_text (&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");
3960 ASSERT_STREQ ("\33]8;;http://example.com\aThis is a link\33]8;;\a",
3961 pp_formatted_text (&pp
));
3966 test_urls_from_braces ()
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
));
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
));
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. */
4003 pp
.set_url_format (URL_FORMAT_NONE
);
4004 pp_begin_url (&pp
, nullptr);
4005 pp_string (&pp
, "This isn't a link");
4007 ASSERT_STREQ ("This isn't a link",
4008 pp_formatted_text (&pp
));
4013 pp
.set_url_format (URL_FORMAT_ST
);
4014 pp_begin_url (&pp
, nullptr);
4015 pp_string (&pp
, "This isn't a link");
4017 ASSERT_STREQ ("This isn't a link",
4018 pp_formatted_text (&pp
));
4023 pp
.set_url_format (URL_FORMAT_BEL
);
4024 pp_begin_url (&pp
, nullptr);
4025 pp_string (&pp
, "This isn't a link");
4027 ASSERT_STREQ ("This isn't a link",
4028 pp_formatted_text (&pp
));
4032 /* Verify that URLification works as expected. */
4035 pp_printf_with_urlifier (pretty_printer
*pp
,
4036 const urlifier
*urlifier
,
4037 const char *msg
, ...)
4042 text_info
text (msg
, &ap
, errno
);
4043 pp_format (pp
, &text
);
4044 pp_output_formatted_text (pp
, urlifier
);
4049 test_urlification ()
4051 class test_urlifier
: public urlifier
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");
4063 auto_fix_quotes fix_quotes
;
4064 const test_urlifier urlifier
;
4066 /* Uses of "%<" and "%>". */
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
));
4078 pp
.set_url_format (URL_FORMAT_ST
);
4079 pp_printf_with_urlifier (&pp
, &urlifier
,
4080 "foo %<-foption%> %<unrecognized%> bar");
4082 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
4083 " `unrecognized' bar",
4084 pp_formatted_text (&pp
));
4088 pp
.set_url_format (URL_FORMAT_BEL
);
4089 pp_printf_with_urlifier (&pp
, &urlifier
,
4090 "foo %<-foption%> %<unrecognized%> bar");
4092 ("foo `\33]8;;http://example.com\a-foption\33]8;;\a'"
4093 " `unrecognized' bar",
4094 pp_formatted_text (&pp
));
4101 pp
.set_url_format (URL_FORMAT_ST
);
4102 pp_printf_with_urlifier (&pp
, &urlifier
,
4104 "-foption", "unrecognized");
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. */
4115 pp
.set_url_format (URL_FORMAT_ST
);
4116 pp_printf_with_urlifier (&pp
, &urlifier
,
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
4128 pp
.set_url_format (URL_FORMAT_ST
);
4129 pp_printf_with_urlifier (&pp
, &urlifier
,
4130 "foo %<-f%sion%> bar %<-f%sion%> baz",
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
));
4140 pp
.set_url_format (URL_FORMAT_ST
);
4141 pp_printf_with_urlifier (&pp
, &urlifier
,
4142 "foo %<%sption%> bar %<-f%sion%> baz",
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. */
4153 pp
.set_url_format (URL_FORMAT_ST
);
4154 pp_printf_with_urlifier (&pp
, &urlifier
,
4155 "foo %<-f%s%s%> bar",
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. */
4165 pp
.set_url_format (URL_FORMAT_ST
);
4166 pp_set_prefix (&pp
, xstrdup ("PREFIX"));
4167 pp_printf_with_urlifier (&pp
, &urlifier
,
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. */
4178 pp
.set_url_format (URL_FORMAT_ST
);
4179 pp_printf_with_urlifier (&pp
, &urlifier
,
4180 "foo %<-f%2$st%1$sn%> bar",
4183 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4184 pp_formatted_text (&pp
));
4187 /* Example of %e. */
4190 pp
.set_url_format (URL_FORMAT_ST
);
4191 pp_element_quoted_string
elem ("-foption");
4192 pp_printf_with_urlifier (&pp
, &urlifier
,
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. */
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. */
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. */
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
]);
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
),
4246 "\xf0\x9f\x98\x82\n"
4247 "--\xf0\x9f\x98\x82\n"
4253 /* Verify that class comma_separated_quoted_strings works as expected. */
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')",
4282 /* Run all of the selftests within this file. */
4285 pretty_print_cc_tests ()
4287 test_basic_printing ();
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 ();
4296 test_urls_from_braces ();
4298 test_urlification ();
4300 test_comma_separated_quoted_strings ();
4303 } // namespace selftest
4305 #endif /* CHECKING_P */