1 /* Various declarations for language-independent pretty-print subroutines.
2 Copyright (C) 2003-2024 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_MEMORY
23 #define INCLUDE_VECTOR
25 #include "coretypes.h"
27 #include "pretty-print.h"
28 #include "pretty-print-format-impl.h"
29 #include "pretty-print-markup.h"
30 #include "pretty-print-urlifier.h"
31 #include "diagnostic-color.h"
32 #include "diagnostic-event-id.h"
33 #include "diagnostic-highlight-colors.h"
34 #include "make-unique.h"
43 /* Replacement for fputs() that handles ANSI escape codes on Windows NT.
44 Contributed by: Liu Hao (lh_mouse at 126 dot com)
46 XXX: This file is compiled into libcommon.a that will be self-contained.
47 It looks like that these functions can be put nowhere else. */
50 #define WIN32_LEAN_AND_MEAN 1
53 /* Write all bytes in [s,s+n) into the specified stream.
54 Errors are ignored. */
56 write_all (HANDLE h
, const char *s
, size_t n
)
67 if (!WriteFile (h
, s
+ n
- rem
, step
, &step
, NULL
))
73 /* Find the beginning of an escape sequence.
75 1. If the sequence begins with an ESC character (0x1B) and a second
76 character X in [0x40,0x5F], returns X and stores a pointer to
77 the third character into *head.
78 2. If the sequence begins with a character X in [0x80,0x9F], returns
79 (X-0x40) and stores a pointer to the second character into *head.
80 Stores the number of ESC character(s) in *prefix_len.
81 Returns 0 if no such sequence can be found. */
83 find_esc_head (int *prefix_len
, const char **head
, const char *str
)
91 c
= (unsigned char) *r
;
97 if (escaped
&& 0x40 <= c
&& c
<= 0x5F)
104 if (0x80 <= c
&& c
<= 0x9F)
106 /* Found (case 2). */
116 /* Find the terminator of an escape sequence.
117 str should be the value stored in *head by a previous successful
118 call to find_esc_head().
119 Returns 0 if no such sequence can be found. */
121 find_esc_terminator (const char **term
, const char *str
)
128 c
= (unsigned char) *r
;
134 if (0x40 <= c
&& c
<= 0x7E)
144 /* Handle a sequence of codes. Sequences that are invalid, reserved,
145 unrecognized or unimplemented are ignored silently.
146 There isn't much we can do because of lameness of Windows consoles. */
148 eat_esc_sequence (HANDLE h
, int esc_code
,
149 const char *esc_head
, const char *esc_term
)
151 /* Numbers in an escape sequence cannot be negative, because
152 a minus sign in the middle of it would have terminated it. */
155 CONSOLE_SCREEN_BUFFER_INFO sb
;
157 /* ED and EL parameters. */
160 /* SGR parameters. */
161 WORD attrib_add
, attrib_rm
;
164 switch (MAKEWORD (esc_code
, *esc_term
))
167 Move the cursor up by n1 characters. */
168 case MAKEWORD ('[', 'A'):
169 if (esc_head
== esc_term
)
173 n1
= strtol (esc_head
, &eptr
, 10);
174 if (eptr
!= esc_term
)
178 if (GetConsoleScreenBufferInfo (h
, &sb
))
180 cr
= sb
.dwCursorPosition
;
181 /* Stop at the topmost boundary. */
186 SetConsoleCursorPosition (h
, cr
);
191 Move the cursor down by n1 characters. */
192 case MAKEWORD ('[', 'B'):
193 if (esc_head
== esc_term
)
197 n1
= strtol (esc_head
, &eptr
, 10);
198 if (eptr
!= esc_term
)
202 if (GetConsoleScreenBufferInfo (h
, &sb
))
204 cr
= sb
.dwCursorPosition
;
205 /* Stop at the bottommost boundary. */
206 if (sb
.dwSize
.Y
- cr
.Y
> n1
)
210 SetConsoleCursorPosition (h
, cr
);
215 Move the cursor right by n1 characters. */
216 case MAKEWORD ('[', 'C'):
217 if (esc_head
== esc_term
)
221 n1
= strtol (esc_head
, &eptr
, 10);
222 if (eptr
!= esc_term
)
226 if (GetConsoleScreenBufferInfo (h
, &sb
))
228 cr
= sb
.dwCursorPosition
;
229 /* Stop at the rightmost boundary. */
230 if (sb
.dwSize
.X
- cr
.X
> n1
)
234 SetConsoleCursorPosition (h
, cr
);
239 Move the cursor left by n1 characters. */
240 case MAKEWORD ('[', 'D'):
241 if (esc_head
== esc_term
)
245 n1
= strtol (esc_head
, &eptr
, 10);
246 if (eptr
!= esc_term
)
250 if (GetConsoleScreenBufferInfo (h
, &sb
))
252 cr
= sb
.dwCursorPosition
;
253 /* Stop at the leftmost boundary. */
258 SetConsoleCursorPosition (h
, cr
);
263 Move the cursor to the beginning of the n1-th line downwards. */
264 case MAKEWORD ('[', 'E'):
265 if (esc_head
== esc_term
)
269 n1
= strtol (esc_head
, &eptr
, 10);
270 if (eptr
!= esc_term
)
274 if (GetConsoleScreenBufferInfo (h
, &sb
))
276 cr
= sb
.dwCursorPosition
;
278 /* Stop at the bottommost boundary. */
279 if (sb
.dwSize
.Y
- cr
.Y
> n1
)
283 SetConsoleCursorPosition (h
, cr
);
288 Move the cursor to the beginning of the n1-th line upwards. */
289 case MAKEWORD ('[', 'F'):
290 if (esc_head
== esc_term
)
294 n1
= strtol (esc_head
, &eptr
, 10);
295 if (eptr
!= esc_term
)
299 if (GetConsoleScreenBufferInfo (h
, &sb
))
301 cr
= sb
.dwCursorPosition
;
303 /* Stop at the topmost boundary. */
308 SetConsoleCursorPosition (h
, cr
);
313 Move the cursor to the (1-based) n1-th column. */
314 case MAKEWORD ('[', 'G'):
315 if (esc_head
== esc_term
)
319 n1
= strtol (esc_head
, &eptr
, 10);
320 if (eptr
!= esc_term
)
324 if (GetConsoleScreenBufferInfo (h
, &sb
))
326 cr
= sb
.dwCursorPosition
;
328 /* Stop at the leftmost or rightmost boundary. */
331 else if (n1
> sb
.dwSize
.X
)
335 SetConsoleCursorPosition (h
, cr
);
339 /* ESC [ n1 ';' n2 'H'
341 Move the cursor to the (1-based) n1-th row and
342 (also 1-based) n2-th column. */
343 case MAKEWORD ('[', 'H'):
344 case MAKEWORD ('[', 'f'):
345 if (esc_head
== esc_term
)
347 /* Both parameters are omitted and set to 1 by default. */
351 else if (!(delim
= (char *) memchr (esc_head
, ';',
352 esc_term
- esc_head
)))
354 /* Only the first parameter is given. The second one is
355 set to 1 by default. */
356 n1
= strtol (esc_head
, &eptr
, 10);
357 if (eptr
!= esc_term
)
363 /* Both parameters are given. The first one shall be
364 terminated by the semicolon. */
365 n1
= strtol (esc_head
, &eptr
, 10);
368 n2
= strtol (delim
+ 1, &eptr
, 10);
369 if (eptr
!= esc_term
)
373 if (GetConsoleScreenBufferInfo (h
, &sb
))
375 cr
= sb
.dwCursorPosition
;
378 /* The cursor position shall be relative to the view coord of
379 the console window, which is usually smaller than the actual
380 buffer. FWIW, the 'appropriate' solution will be shrinking
381 the buffer to match the size of the console window,
382 destroying scrollback in the process. */
383 n1
+= sb
.srWindow
.Top
;
384 n2
+= sb
.srWindow
.Left
;
385 /* Stop at the topmost or bottommost boundary. */
388 else if (n1
> sb
.dwSize
.Y
)
392 /* Stop at the leftmost or rightmost boundary. */
395 else if (n2
> sb
.dwSize
.X
)
399 SetConsoleCursorPosition (h
, cr
);
405 case MAKEWORD ('[', 'J'):
406 if (esc_head
== esc_term
)
407 /* This is one of the very few codes whose parameters have
408 a default value of zero. */
412 n1
= strtol (esc_head
, &eptr
, 10);
413 if (eptr
!= esc_term
)
417 if (GetConsoleScreenBufferInfo (h
, &sb
))
419 /* The cursor is not necessarily in the console window, which
420 makes the behavior of this code harder to define. */
424 /* If the cursor is in or above the window, erase from
425 it to the bottom of the window; otherwise, do nothing. */
426 cr
= sb
.dwCursorPosition
;
427 cnt
= sb
.dwSize
.X
- sb
.dwCursorPosition
.X
;
428 rows
= sb
.srWindow
.Bottom
- sb
.dwCursorPosition
.Y
;
431 /* If the cursor is in or under the window, erase from
432 it to the top of the window; otherwise, do nothing. */
434 cr
.Y
= sb
.srWindow
.Top
;
435 cnt
= sb
.dwCursorPosition
.X
+ 1;
436 rows
= sb
.dwCursorPosition
.Y
- sb
.srWindow
.Top
;
439 /* Erase the entire window. */
440 cr
.X
= sb
.srWindow
.Left
;
441 cr
.Y
= sb
.srWindow
.Top
;
443 rows
= sb
.srWindow
.Bottom
- sb
.srWindow
.Top
+ 1;
446 /* Erase the entire buffer. */
455 cnt
+= rows
* sb
.dwSize
.X
;
456 FillConsoleOutputCharacterW (h
, L
' ', cnt
, cr
, &step
);
457 FillConsoleOutputAttribute (h
, sb
.wAttributes
, cnt
, cr
, &step
);
463 case MAKEWORD ('[', 'K'):
464 if (esc_head
== esc_term
)
465 /* This is one of the very few codes whose parameters have
466 a default value of zero. */
470 n1
= strtol (esc_head
, &eptr
, 10);
471 if (eptr
!= esc_term
)
475 if (GetConsoleScreenBufferInfo (h
, &sb
))
480 /* Erase from the cursor to the end. */
481 cr
= sb
.dwCursorPosition
;
482 cnt
= sb
.dwSize
.X
- sb
.dwCursorPosition
.X
;
485 /* Erase from the cursor to the beginning. */
486 cr
= sb
.dwCursorPosition
;
488 cnt
= sb
.dwCursorPosition
.X
+ 1;
491 /* Erase the entire line. */
492 cr
= sb
.dwCursorPosition
;
497 FillConsoleOutputCharacterW (h
, L
' ', cnt
, cr
, &step
);
498 FillConsoleOutputAttribute (h
, sb
.wAttributes
, cnt
, cr
, &step
);
502 /* ESC [ n1 ';' n2 'm'
503 Set SGR parameters. Zero or more parameters will follow. */
504 case MAKEWORD ('[', 'm'):
507 if (esc_head
== esc_term
)
509 /* When no parameter is given, reset the console. */
510 attrib_add
|= (FOREGROUND_RED
| FOREGROUND_GREEN
512 attrib_rm
= -1; /* Removes everything. */
518 /* Parse a parameter. */
519 n1
= strtol (param
, &eptr
, 10);
520 if (*eptr
!= ';' && eptr
!= esc_term
)
527 attrib_add
|= (FOREGROUND_RED
| FOREGROUND_GREEN
529 attrib_rm
= -1; /* Removes everything. */
533 attrib_add
|= FOREGROUND_INTENSITY
;
537 attrib_add
|= COMMON_LVB_UNDERSCORE
;
541 /* XXX: It is not BLINKING at all! */
542 attrib_add
|= BACKGROUND_INTENSITY
;
546 attrib_add
|= COMMON_LVB_REVERSE_VIDEO
;
550 attrib_add
&= ~FOREGROUND_INTENSITY
;
551 attrib_rm
|= FOREGROUND_INTENSITY
;
555 attrib_add
&= ~COMMON_LVB_UNDERSCORE
;
556 attrib_rm
|= COMMON_LVB_UNDERSCORE
;
560 /* XXX: It is not BLINKING at all! */
561 attrib_add
&= ~BACKGROUND_INTENSITY
;
562 attrib_rm
|= BACKGROUND_INTENSITY
;
566 attrib_add
&= ~COMMON_LVB_REVERSE_VIDEO
;
567 attrib_rm
|= COMMON_LVB_REVERSE_VIDEO
;
577 /* Foreground color. */
578 attrib_add
&= ~(FOREGROUND_RED
| FOREGROUND_GREEN
582 attrib_add
|= FOREGROUND_RED
;
584 attrib_add
|= FOREGROUND_GREEN
;
586 attrib_add
|= FOREGROUND_BLUE
;
587 attrib_rm
|= (FOREGROUND_RED
| FOREGROUND_GREEN
591 /* Reserved for extended foreground color.
592 Don't know how to handle parameters remaining.
596 /* Reset foreground color. */
598 attrib_add
|= (FOREGROUND_RED
| FOREGROUND_GREEN
600 attrib_rm
|= (FOREGROUND_RED
| FOREGROUND_GREEN
611 /* Background color. */
612 attrib_add
&= ~(BACKGROUND_RED
| BACKGROUND_GREEN
616 attrib_add
|= BACKGROUND_RED
;
618 attrib_add
|= BACKGROUND_GREEN
;
620 attrib_add
|= BACKGROUND_BLUE
;
621 attrib_rm
|= (BACKGROUND_RED
| BACKGROUND_GREEN
625 /* Reserved for extended background color.
626 Don't know how to handle parameters remaining.
630 /* Reset background color. */
632 attrib_add
&= ~(BACKGROUND_RED
| BACKGROUND_GREEN
634 attrib_rm
|= (BACKGROUND_RED
| BACKGROUND_GREEN
639 /* Prepare the next parameter. */
642 while (param
!= esc_term
);
645 /* 0xFFFF removes everything. If it is not the case,
646 care must be taken to preserve old attributes. */
647 if (attrib_rm
!= 0xFFFF && GetConsoleScreenBufferInfo (h
, &sb
))
649 attrib_add
|= sb
.wAttributes
& ~attrib_rm
;
651 if (attrib_add
& COMMON_LVB_REVERSE_VIDEO
)
653 /* COMMON_LVB_REVERSE_VIDEO is only effective for DBCS.
654 * Swap foreground and background colors by hand.
656 attrib_add
= (attrib_add
& 0xFF00)
657 | ((attrib_add
& 0x00F0) >> 4)
658 | ((attrib_add
& 0x000F) << 4);
659 attrib_add
&= ~COMMON_LVB_REVERSE_VIDEO
;
661 SetConsoleTextAttribute (h
, attrib_add
);
667 mingw_ansi_fputs (const char *str
, FILE *fp
)
669 const char *read
= str
;
672 int esc_code
, prefix_len
;
673 const char *esc_head
, *esc_term
;
675 h
= (HANDLE
) _get_osfhandle (_fileno (fp
));
676 if (h
== INVALID_HANDLE_VALUE
)
679 /* Don't mess up stdio functions with Windows APIs. */
682 if (GetConsoleMode (h
, &mode
)
683 #ifdef ENABLE_VIRTUAL_TERMINAL_PROCESSING
684 && !(mode
& ENABLE_VIRTUAL_TERMINAL_PROCESSING
)
687 /* If it is a console, and doesn't support ANSI escape codes, translate
691 if ((esc_code
= find_esc_head (&prefix_len
, &esc_head
, read
)) == 0)
693 /* Write all remaining characters, then exit. */
694 write_all (h
, read
, strlen (read
));
697 if (find_esc_terminator (&esc_term
, esc_head
) == 0)
698 /* Ignore incomplete escape sequences at the moment.
699 FIXME: The escape state shall be cached for further calls
702 write_all (h
, read
, esc_head
- prefix_len
- read
);
703 eat_esc_sequence (h
, esc_code
, esc_head
, esc_term
);
707 /* If it is not a console, write everything as-is. */
708 write_all (h
, read
, strlen (read
));
713 #endif /* __MINGW32__ */
716 decode_utf8_char (const unsigned char *, size_t len
, unsigned int *);
717 static void pp_quoted_string (pretty_printer
*, const char *, size_t = -1);
720 default_token_printer (pretty_printer
*pp
,
721 const pp_token_list
&tokens
);
723 /* Overwrite the given location/range within this text_info's rich_location.
724 For use e.g. when implementing "+" in client format decoders. */
727 text_info::set_location (unsigned int idx
, location_t loc
,
728 enum range_display_kind range_display_kind
)
730 gcc_checking_assert (m_richloc
);
731 m_richloc
->set_range (idx
, loc
, range_display_kind
);
735 text_info::get_location (unsigned int index_of_location
) const
737 gcc_checking_assert (m_richloc
);
739 if (index_of_location
== 0)
740 return m_richloc
->get_loc ();
742 return UNKNOWN_LOCATION
;
745 // Default construct an output buffer.
747 output_buffer::output_buffer ()
748 : m_formatted_obstack (),
750 m_obstack (&m_formatted_obstack
),
751 m_cur_formatted_chunks (nullptr),
757 obstack_init (&m_formatted_obstack
);
758 obstack_init (&m_chunk_obstack
);
761 // Release resources owned by an output buffer at the end of lifetime.
763 output_buffer::~output_buffer ()
765 obstack_free (&m_chunk_obstack
, NULL
);
766 obstack_free (&m_formatted_obstack
, NULL
);
769 /* Allocate a new pp_formatted_chunks from chunk_obstack and push
770 it onto this buffer's stack.
771 This represents the result of phases 1 and 2 of formatting. */
773 pp_formatted_chunks
*
774 output_buffer::push_formatted_chunks ()
776 /* Allocate a new chunk structure. */
777 pp_formatted_chunks
*new_chunk_array
778 = XOBNEW (&m_chunk_obstack
, pp_formatted_chunks
);
779 new_chunk_array
->m_prev
= m_cur_formatted_chunks
;
780 m_cur_formatted_chunks
= new_chunk_array
;
781 return new_chunk_array
;
784 /* Deallocate the current pp_formatted_chunks structure and everything after it
785 (i.e. the associated series of formatted strings, pp_token_lists, and
789 output_buffer::pop_formatted_chunks ()
791 pp_formatted_chunks
*old_top
= m_cur_formatted_chunks
;
792 gcc_assert (old_top
);
793 m_cur_formatted_chunks
= old_top
->m_prev
;
794 obstack_free (&m_chunk_obstack
, old_top
);
797 /* Dump state of this output_buffer to OUT, for debugging. */
800 output_buffer::dump (FILE *out
) const
803 for (pp_formatted_chunks
*iter
= m_cur_formatted_chunks
;
805 iter
= iter
->m_prev
, depth
++)
807 fprintf (out
, "pp_formatted_chunks: depth %i\n", depth
);
813 #define PTRDIFF_MAX INTTYPE_MAXIMUM (ptrdiff_t)
816 /* Format an integer given by va_arg (ARG, type-specifier T) where
817 type-specifier is a precision modifier as indicated by PREC. F is
818 a string used to construct the appropriate format-specifier. */
819 #define pp_integer_with_precision(PP, ARG, PREC, T, F) \
824 pp_scalar (PP, "%" F, va_arg (ARG, T)); \
828 pp_scalar (PP, "%l" F, va_arg (ARG, long T)); \
832 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
833 va_arg (ARG, long long T)); \
837 if (T (-1) < T (0)) \
838 pp_scalar (PP, "%" GCC_PRISZ F, \
839 (fmt_size_t) va_arg (ARG, ssize_t)); \
841 pp_scalar (PP, "%" GCC_PRISZ F, \
842 (fmt_size_t) va_arg (ARG, size_t)); \
846 if (T (-1) >= T (0)) \
848 unsigned long long a = va_arg (ARG, ptrdiff_t); \
849 unsigned long long m = PTRDIFF_MAX; \
851 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
854 else if (sizeof (ptrdiff_t) <= sizeof (int)) \
855 pp_scalar (PP, "%" F, \
856 (int) va_arg (ARG, ptrdiff_t)); \
857 else if (sizeof (ptrdiff_t) <= sizeof (long)) \
858 pp_scalar (PP, "%l" F, \
859 (long int) va_arg (ARG, ptrdiff_t)); \
861 pp_scalar (PP, "%" HOST_LONG_LONG_FORMAT F, \
863 va_arg (ARG, ptrdiff_t)); \
872 /* Subroutine of pp_set_maximum_length. Set up PRETTY-PRINTER's
873 internal maximum characters per line. */
876 pretty_printer::set_real_maximum_length ()
878 /* If we're told not to wrap lines then do the obvious thing. In case
879 we'll emit prefix only once per message, it is appropriate
880 not to increase unnecessarily the line-length cut-off. */
881 if (!pp_is_wrapping_line (this)
882 || pp_prefixing_rule (this) == DIAGNOSTICS_SHOW_PREFIX_ONCE
883 || pp_prefixing_rule (this) == DIAGNOSTICS_SHOW_PREFIX_NEVER
)
884 m_maximum_length
= pp_line_cutoff (this);
887 int prefix_length
= m_prefix
? strlen (m_prefix
) : 0;
888 /* If the prefix is ridiculously too long, output at least
890 if (pp_line_cutoff (this) - prefix_length
< 32)
891 m_maximum_length
= pp_line_cutoff (this) + 32;
893 m_maximum_length
= pp_line_cutoff (this);
897 /* Clear this pretty_printer's output state. */
899 pretty_printer::clear_state ()
901 m_emitted_prefix
= false;
902 pp_indentation (this) = 0;
905 /* Print X to PP in decimal. */
906 template<unsigned int N
, typename T
>
908 pp_wide_integer (pretty_printer
*pp
, const poly_int
<N
, T
> &x
)
910 if (x
.is_constant ())
911 pp_wide_integer (pp
, x
.coeffs
[0]);
914 pp_left_bracket (pp
);
915 for (unsigned int i
= 0; i
< N
; ++i
)
919 pp_wide_integer (pp
, x
.coeffs
[i
]);
921 pp_right_bracket (pp
);
925 template void pp_wide_integer (pretty_printer
*, const poly_uint16
&);
926 template void pp_wide_integer (pretty_printer
*, const poly_int64
&);
927 template void pp_wide_integer (pretty_printer
*, const poly_uint64
&);
929 /* Flush the formatted text of PRETTY-PRINTER onto the attached stream. */
931 pp_write_text_to_stream (pretty_printer
*pp
)
933 const char *text
= pp_formatted_text (pp
);
935 mingw_ansi_fputs (text
, pp_buffer (pp
)->m_stream
);
937 fputs (text
, pp_buffer (pp
)->m_stream
);
939 pp_clear_output_area (pp
);
942 /* As pp_write_text_to_stream, but for GraphViz label output.
944 Flush the formatted text of pretty-printer PP onto the attached stream.
945 Replace characters in PPF that have special meaning in a GraphViz .dot
948 This routine is not very fast, but it doesn't have to be as this is only
949 be used by routines dumping intermediate representations in graph form. */
952 pp_write_text_as_dot_label_to_stream (pretty_printer
*pp
, bool for_record
)
954 const char *text
= pp_formatted_text (pp
);
955 const char *p
= text
;
956 FILE *fp
= pp_buffer (pp
)->m_stream
;
963 /* Print newlines as a left-aligned newline. */
969 /* The following characters are only special for record-shape nodes. */
976 escape_char
= for_record
;
979 /* The following characters always have to be escaped
980 for use in labels. */
982 /* There is a bug in some (f.i. 2.36.0) versions of graphiz
983 ( http://www.graphviz.org/mantisbt/view.php?id=2524 ) related to
984 backslash as last char in label. Let's avoid triggering it. */
985 gcc_assert (*(p
+ 1) != '\0');
1002 pp_clear_output_area (pp
);
1005 /* As pp_write_text_to_stream, but for GraphViz HTML-like strings.
1007 Flush the formatted text of pretty-printer PP onto the attached stream,
1008 escaping these characters
1010 using XML escape sequences.
1012 http://www.graphviz.org/doc/info/lang.html#html states:
1013 special XML escape sequences for ", &, <, and > may be necessary in
1014 order to embed these characters in attribute values or raw text
1015 This doesn't list "'" (which would normally be escaped in XML
1016 as "'" or in HTML as "'");.
1018 Experiments show that escaping "'" doesn't seem to be necessary. */
1021 pp_write_text_as_html_like_dot_to_stream (pretty_printer
*pp
)
1023 const char *text
= pp_formatted_text (pp
);
1024 const char *p
= text
;
1025 FILE *fp
= pp_buffer (pp
)->m_stream
;
1032 fputs (""", fp
);
1035 fputs ("&", fp
);
1050 pp_clear_output_area (pp
);
1053 /* Wrap a text delimited by START and END into PRETTY-PRINTER. */
1055 pp_wrap_text (pretty_printer
*pp
, const char *start
, const char *end
)
1057 bool wrapping_line
= pp_is_wrapping_line (pp
);
1059 while (start
!= end
)
1061 /* Dump anything bordered by whitespaces. */
1063 const char *p
= start
;
1064 while (p
!= end
&& !ISBLANK (*p
) && *p
!= '\n')
1067 && p
- start
>= pp
->remaining_character_count_for_line ())
1069 pp_append_text (pp
, start
, p
);
1073 if (start
!= end
&& ISBLANK (*start
))
1078 if (start
!= end
&& *start
== '\n')
1086 /* Same as pp_wrap_text but wrap text only when in line-wrapping mode. */
1088 pp_maybe_wrap_text (pretty_printer
*pp
, const char *start
, const char *end
)
1090 if (pp_is_wrapping_line (pp
))
1091 pp_wrap_text (pp
, start
, end
);
1093 pp_append_text (pp
, start
, end
);
1096 /* Append to the output area of PRETTY-PRINTER a string specified by its
1097 STARTing character and LENGTH. */
1099 pp_append_r (pretty_printer
*pp
, const char *start
, int length
)
1101 output_buffer_append_r (pp_buffer (pp
), start
, length
);
1104 /* Insert enough spaces into the output area of PRETTY-PRINTER to bring
1105 the column position to the current indentation level, assuming that a
1106 newline has just been written to the buffer. */
1108 pp_indent (pretty_printer
*pp
)
1110 int n
= pp_indentation (pp
);
1113 for (i
= 0; i
< n
; ++i
)
1117 static const char *get_end_url_string (pretty_printer
*);
1119 /* struct pp_token. */
1121 pp_token::pp_token (enum kind k
)
1129 pp_token::dump (FILE *out
) const
1137 const pp_token_text
*sub
= as_a
<const pp_token_text
*> (this);
1138 gcc_assert (sub
->m_value
.get ());
1139 fprintf (out
, "TEXT(\"%s\")", sub
->m_value
.get ());
1142 case kind::begin_color
:
1144 const pp_token_begin_color
*sub
1145 = as_a
<const pp_token_begin_color
*> (this);
1146 gcc_assert (sub
->m_value
.get ());
1147 fprintf (out
, "BEGIN_COLOR(\"%s\")", sub
->m_value
.get ());
1150 case kind::end_color
:
1151 fprintf (out
, "END_COLOR");
1153 case kind::begin_quote
:
1154 fprintf (out
, "BEGIN_QUOTE");
1156 case kind::end_quote
:
1157 fprintf (out
, "END_QUOTE");
1159 case kind::begin_url
:
1161 const pp_token_begin_url
*sub
1162 = as_a
<const pp_token_begin_url
*> (this);
1163 gcc_assert (sub
->m_value
.get ());
1164 fprintf (out
, "BEGIN_URL(\"%s\")", sub
->m_value
.get ());
1168 fprintf (out
, "END_URL");
1171 case kind::event_id
:
1173 const pp_token_event_id
*sub
1174 = as_a
<const pp_token_event_id
*> (this);
1175 gcc_assert (sub
->m_event_id
.known_p ());
1176 fprintf (out
, "EVENT((%i))", sub
->m_event_id
.one_based ());
1180 case kind::custom_data
:
1182 const pp_token_custom_data
*sub
1183 = as_a
<const pp_token_custom_data
*> (this);
1184 gcc_assert (sub
->m_value
.get ());
1185 fprintf (out
, "CUSTOM(");
1186 sub
->m_value
->dump (out
);
1193 /* Allocate SZ bytes within S, which must not be half-way through
1194 building another object. */
1197 allocate_object (size_t sz
, obstack
&s
)
1199 /* We must not be half-way through an object. */
1200 gcc_assert (obstack_base (&s
) == obstack_next_free (&s
));
1202 obstack_blank (&s
, sz
);
1203 void *buf
= obstack_finish (&s
);
1207 /* Make room for a pp_token instance within obstack S. */
1210 pp_token::operator new (size_t sz
, obstack
&s
)
1212 return allocate_object (sz
, s
);
1216 pp_token::operator delete (void *)
1218 /* No-op: pp_tokens are allocated within obstacks, so
1219 the memory will be reclaimed when the obstack is freed. */
1222 /* class pp_token_list. */
1224 /* Make room for a pp_token_list instance within obstack S. */
1227 pp_token_list::operator new (size_t sz
, obstack
&s
)
1229 return allocate_object (sz
, s
);
1233 pp_token_list::operator delete (void *)
1235 /* No-op: pp_token_list allocated within obstacks don't
1236 need their own reclaim the memory will be reclaimed when
1237 the obstack is freed. */
1240 pp_token_list::pp_token_list (obstack
&s
)
1247 pp_token_list::pp_token_list (pp_token_list
&&other
)
1248 : m_obstack (other
.m_obstack
),
1249 m_first (other
.m_first
),
1252 other
.m_first
= nullptr;
1253 other
.m_end
= nullptr;
1256 pp_token_list::~pp_token_list ()
1258 for (auto iter
= m_first
; iter
; )
1260 pp_token
*next
= iter
->m_next
;
1267 pp_token_list::push_back_text (label_text
&&text
)
1269 if (text
.get ()[0] == '\0')
1270 return; // pushing empty string is a no-op
1271 push_back
<pp_token_text
> (std::move (text
));
1275 pp_token_list::push_back (std::unique_ptr
<pp_token
> tok
)
1279 gcc_assert (m_end
== nullptr);
1280 m_first
= tok
.get ();
1285 gcc_assert (m_end
!= nullptr);
1286 m_end
->m_next
= tok
.get ();
1287 tok
->m_prev
= m_end
;
1294 pp_token_list::push_back_list (pp_token_list
&&list
)
1296 while (auto tok
= list
.pop_front ())
1297 push_back (std::move (tok
));
1300 std::unique_ptr
<pp_token
>
1301 pp_token_list::pop_front ()
1303 pp_token
*result
= m_first
;
1304 if (result
== nullptr)
1307 gcc_assert (result
->m_prev
== nullptr);
1308 m_first
= result
->m_next
;
1311 gcc_assert (result
!= m_end
);
1312 m_first
->m_prev
= nullptr;
1316 gcc_assert (result
== m_end
);
1319 result
->m_next
= nullptr;
1320 return std::unique_ptr
<pp_token
> (result
);
1323 std::unique_ptr
<pp_token
>
1324 pp_token_list::remove_token (pp_token
*tok
)
1329 gcc_assert (tok
!= m_first
);
1330 tok
->m_prev
->m_next
= tok
->m_next
;
1334 gcc_assert (tok
== m_first
);
1335 m_first
= tok
->m_next
;
1339 gcc_assert (tok
!= m_end
);
1340 tok
->m_next
->m_prev
= tok
->m_prev
;
1344 gcc_assert (tok
== m_end
);
1345 m_end
= tok
->m_prev
;
1347 tok
->m_prev
= nullptr;
1348 tok
->m_next
= nullptr;
1349 gcc_assert (m_first
!= tok
);
1350 gcc_assert (m_end
!= tok
);
1351 return std::unique_ptr
<pp_token
> (tok
);
1354 /* Insert NEW_TOK after RELATIVE_TOK. */
1357 pp_token_list::insert_after (std::unique_ptr
<pp_token
> new_tok_up
,
1358 pp_token
*relative_tok
)
1360 pp_token
*new_tok
= new_tok_up
.release ();
1362 gcc_assert (new_tok
);
1363 gcc_assert (new_tok
->m_prev
== nullptr);
1364 gcc_assert (new_tok
->m_next
== nullptr);
1365 gcc_assert (relative_tok
);
1367 if (relative_tok
->m_next
)
1369 gcc_assert (relative_tok
!= m_end
);
1370 relative_tok
->m_next
->m_prev
= new_tok
;
1374 gcc_assert (relative_tok
== m_end
);
1377 new_tok
->m_prev
= relative_tok
;
1378 new_tok
->m_next
= relative_tok
->m_next
;
1379 relative_tok
->m_next
= new_tok
;
1383 pp_token_list::replace_custom_tokens ()
1385 pp_token
*iter
= m_first
;
1388 pp_token
*next
= iter
->m_next
;
1389 if (iter
->m_kind
== pp_token::kind::custom_data
)
1391 pp_token_list
tok_list (m_obstack
);
1392 pp_token_custom_data
*sub
= as_a
<pp_token_custom_data
*> (iter
);
1393 if (sub
->m_value
->as_standard_tokens (tok_list
))
1395 while (auto tok
= tok_list
.pop_front ())
1397 /* The resulting token list must not contain any
1399 gcc_assert (tok
->m_kind
!= pp_token::kind::custom_data
);
1400 insert_after (std::move (tok
), iter
);
1402 remove_token (iter
);
1409 /* Merge any runs of consecutive text tokens within this list
1410 into individual text tokens. */
1413 pp_token_list::merge_consecutive_text_tokens ()
1415 pp_token
*start_of_run
= m_first
;
1416 while (start_of_run
)
1418 if (start_of_run
->m_kind
!= pp_token::kind::text
)
1420 start_of_run
= start_of_run
->m_next
;
1423 pp_token
*end_of_run
= start_of_run
;
1424 while (end_of_run
->m_next
1425 && end_of_run
->m_next
->m_kind
== pp_token::kind::text
)
1426 end_of_run
= end_of_run
->m_next
;
1427 if (end_of_run
!= start_of_run
)
1429 /* start_of_run through end_of_run are a run of consecutive
1432 /* Calculate size of buffer for merged text. */
1434 for (auto iter
= start_of_run
; iter
!= end_of_run
->m_next
;
1435 iter
= iter
->m_next
)
1437 pp_token_text
*iter_text
= static_cast<pp_token_text
*> (iter
);
1438 sz
+= strlen (iter_text
->m_value
.get ());
1441 /* Allocate and populate buffer for merged text
1442 (within m_obstack). */
1443 char * const buf
= (char *)allocate_object (sz
+ 1, m_obstack
);
1445 for (auto iter
= start_of_run
; iter
!= end_of_run
->m_next
;
1446 iter
= iter
->m_next
)
1448 pp_token_text
*iter_text
= static_cast<pp_token_text
*> (iter
);
1449 size_t iter_sz
= strlen (iter_text
->m_value
.get ());
1450 memcpy (p
, iter_text
->m_value
.get (), iter_sz
);
1455 /* Replace start_of_run's buffer pointer with the new buffer. */
1456 static_cast<pp_token_text
*> (start_of_run
)->m_value
1457 = label_text::borrow (buf
);
1459 /* Remove all the other text tokens in the run. */
1460 pp_token
* const next
= end_of_run
->m_next
;
1461 while (start_of_run
->m_next
!= next
)
1462 remove_token (start_of_run
->m_next
);
1463 start_of_run
= next
;
1466 start_of_run
= end_of_run
->m_next
;
1470 /* Apply URLIFIER to this token list.
1471 Find BEGIN_QUOTE, TEXT, END_QUOTE triples, and if URLIFIER has a url
1472 for the value of TEXT, then wrap TEXT in a {BEGIN,END}_URL pair. */
1475 pp_token_list::apply_urlifier (const urlifier
&urlifier
)
1477 for (pp_token
*iter
= m_first
; iter
; )
1479 if (iter
->m_kind
== pp_token::kind::begin_quote
1481 && iter
->m_next
->m_kind
== pp_token::kind::text
1482 && iter
->m_next
->m_next
1483 && iter
->m_next
->m_next
->m_kind
== pp_token::kind::end_quote
)
1485 pp_token
*begin_quote
= iter
;
1486 pp_token_text
*text
= as_a
<pp_token_text
*> (begin_quote
->m_next
);
1487 pp_token
*end_quote
= text
->m_next
;
1488 if (char *url
= urlifier
.get_url_for_quoted_text
1489 (text
->m_value
.get (),
1490 strlen (text
->m_value
.get ())))
1493 = make_token
<pp_token_begin_url
> (label_text::take (url
));
1494 auto end_url
= make_token
<pp_token_end_url
> ();
1495 insert_after (std::move (begin_url
), begin_quote
);
1496 insert_after (std::move (end_url
), text
);
1498 iter
= end_quote
->m_next
;
1501 iter
= iter
->m_next
;
1506 pp_token_list::dump (FILE *out
) const
1509 for (auto iter
= m_first
; iter
; iter
= iter
->m_next
)
1513 fprintf (out
, ", ");
1515 fprintf (out
, "]\n");
1519 /* Adds a chunk to the end of formatted output, so that it
1520 will be printed by pp_output_formatted_text. */
1523 pp_formatted_chunks::append_formatted_chunk (obstack
&s
, const char *content
)
1525 unsigned int chunk_idx
;
1526 for (chunk_idx
= 0; m_args
[chunk_idx
]; chunk_idx
++)
1528 pp_token_list
*tokens
= pp_token_list::make (s
);
1529 tokens
->push_back_text (label_text::borrow (content
));
1530 m_args
[chunk_idx
++] = tokens
;
1531 m_args
[chunk_idx
] = nullptr;
1535 pp_formatted_chunks::dump (FILE *out
) const
1537 for (size_t idx
= 0; m_args
[idx
]; ++idx
)
1539 fprintf (out
, "%i: ", (int)idx
);
1540 m_args
[idx
]->dump (out
);
1544 /* Finish any text accumulating within CUR_OBSTACK,
1546 Push a text pp_token to the end of TOK_LIST containing
1547 a borrowed copy of the text in CUR_OBSTACK. */
1550 push_back_any_text (pp_token_list
*tok_list
,
1551 obstack
*cur_obstack
)
1553 obstack_1grow (cur_obstack
, '\0');
1554 tok_list
->push_back_text
1555 (label_text::borrow (XOBFINISH (cur_obstack
,
1559 /* The following format specifiers are recognized as being client independent:
1560 %d, %i: (signed) integer in base ten.
1561 %u: unsigned integer in base ten.
1562 %o: unsigned integer in base eight.
1563 %x: unsigned integer in base sixteen.
1564 %ld, %li, %lo, %lu, %lx: long versions of the above.
1565 %lld, %lli, %llo, %llu, %llx: long long versions.
1566 %wd, %wi, %wo, %wu, %wx: HOST_WIDE_INT versions.
1567 %zd, %zi, %zo, %zu, %zx: size_t versions.
1568 %td, %ti, %to, %tu, %tx: ptrdiff_t versions.
1572 %p: pointer (printed in a host-dependent manner).
1573 %r: if pp_show_color(pp), switch to color identified by const char *.
1574 %R: if pp_show_color(pp), reset color.
1575 %m: strerror(text->err_no) - does not consume a value from args_ptr.
1579 %{: URL start. Consumes a const char * argument for the URL.
1580 %}: URL end. Does not consume any arguments.
1581 %': apostrophe (should only be used in untranslated messages;
1582 translations should use appropriate punctuation directly).
1583 %@: diagnostic_event_id_ptr, for which event_id->known_p () must be true.
1584 %.*s: a substring the length of which is specified by an argument
1586 %Ns: likewise, but length specified as constant in the format string.
1587 Flag 'q': quote formatted text (must come immediately after '%').
1588 %Z: Requires two arguments - array of int, and len. Prints elements
1591 %e: Consumes a pp_element * argument.
1593 Arguments can be used sequentially, or through %N$ resp. *N$
1594 notation Nth argument after the format string. If %N$ / *N$
1595 notation is used, it must be used for all arguments, except %m, %%,
1596 %<, %>, %} and %', which may not have a number, as they do not consume
1597 an argument. When %M$.*N$s is used, M must be N + 1. (This may
1598 also be written %M$.*s, provided N is not otherwise used.) The
1599 format string must have conversion specifiers with argument numbers
1600 1 up to highest argument; each argument may only be used once.
1601 A format string can have at most 30 arguments. */
1603 /* Implementation of pp_format.
1604 Formatting phases 1 and 2:
1605 - push a pp_formatted_chunks instance.
1606 - render TEXT->format_spec plus text->m_args_ptr into the pp_formatted_chunks
1607 instance as pp_token_lists.
1608 Phase 3 is in pp_output_formatted_text, which pops the pp_formatted_chunks
1612 format_phase_1 (const text_info
&text
,
1613 obstack
&chunk_obstack
,
1614 pp_token_list
**args
,
1615 pp_token_list
***formatters
);
1618 format_phase_2 (pretty_printer
*pp
,
1620 obstack
&chunk_obstack
,
1621 pp_token_list
***formatters
);
1624 pretty_printer::format (text_info
&text
)
1626 pp_formatted_chunks
*new_chunk_array
= m_buffer
->push_formatted_chunks ();
1627 pp_token_list
**args
= new_chunk_array
->m_args
;
1629 pp_token_list
**formatters
[PP_NL_ARGMAX
];
1630 memset (formatters
, 0, sizeof formatters
);
1632 /* Formatting phase 1: split up TEXT->format_spec into chunks in
1633 pp_buffer (PP)->args[]. Even-numbered chunks are to be output
1634 verbatim, odd-numbered chunks are format specifiers.
1635 %m, %%, %<, %>, %} and %' are replaced with the appropriate text at
1637 format_phase_1 (text
, m_buffer
->m_chunk_obstack
, args
, formatters
);
1639 /* Note that you can debug the state of the chunk arrays here using
1640 (gdb) call m_buffer->cur_chunk_array->dump()
1641 which, given e.g. "foo: %s bar: %s" might print:
1648 /* Set output to the argument obstack, and switch line-wrapping and
1650 m_buffer
->m_obstack
= &m_buffer
->m_chunk_obstack
;
1651 const int old_line_length
= m_buffer
->m_line_length
;
1652 const pp_wrapping_mode_t old_wrapping_mode
= pp_set_verbatim_wrapping (this);
1654 format_phase_2 (this, text
, m_buffer
->m_chunk_obstack
, formatters
);
1656 /* If the client supplied a postprocessing object, call its "handle"
1658 if (m_format_postprocessor
)
1659 m_format_postprocessor
->handle (this);
1661 /* Revert to normal obstack and wrapping mode. */
1662 m_buffer
->m_obstack
= &m_buffer
->m_formatted_obstack
;
1663 m_buffer
->m_line_length
= old_line_length
;
1664 pp_wrapping_mode (this) = old_wrapping_mode
;
1669 format_phase_1 (const text_info
&text
,
1670 obstack
&chunk_obstack
,
1671 pp_token_list
**args
,
1672 pp_token_list
***formatters
)
1675 unsigned int curarg
= 0;
1676 bool any_unnumbered
= false, any_numbered
= false;
1677 pp_token_list
*cur_token_list
;
1678 args
[chunk
++] = cur_token_list
= pp_token_list::make (chunk_obstack
);
1679 for (const char *p
= text
.m_format_spec
; *p
; )
1681 while (*p
!= '\0' && *p
!= '%')
1683 obstack_1grow (&chunk_obstack
, *p
);
1696 obstack_1grow (&chunk_obstack
, '%');
1702 push_back_any_text (cur_token_list
, &chunk_obstack
);
1703 cur_token_list
->push_back
<pp_token_begin_quote
> ();
1710 push_back_any_text (cur_token_list
, &chunk_obstack
);
1711 cur_token_list
->push_back
<pp_token_end_quote
> ();
1717 push_back_any_text (cur_token_list
, &chunk_obstack
);
1718 cur_token_list
->push_back
<pp_token_end_quote
> ();
1725 push_back_any_text (cur_token_list
, &chunk_obstack
);
1726 cur_token_list
->push_back
<pp_token_end_url
> ();
1733 push_back_any_text (cur_token_list
, &chunk_obstack
);
1734 cur_token_list
->push_back
<pp_token_end_color
> ();
1741 const char *errstr
= xstrerror (text
.m_err_no
);
1742 obstack_grow (&chunk_obstack
, errstr
, strlen (errstr
));
1748 /* Handled in phase 2. Terminate the plain chunk here. */
1749 push_back_any_text (cur_token_list
, &chunk_obstack
);
1753 /* Start a new token list for the formatting args. */
1754 args
[chunk
] = cur_token_list
= pp_token_list::make (chunk_obstack
);
1760 argno
= strtoul (p
, &end
, 10) - 1;
1762 gcc_assert (*p
== '$');
1765 any_numbered
= true;
1766 gcc_assert (!any_unnumbered
);
1771 any_unnumbered
= true;
1772 gcc_assert (!any_numbered
);
1774 gcc_assert (argno
< PP_NL_ARGMAX
);
1775 gcc_assert (!formatters
[argno
]);
1776 formatters
[argno
] = &args
[chunk
++];
1779 obstack_1grow (&chunk_obstack
, *p
);
1782 while (strchr ("qwlzt+#", p
[-1]));
1786 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
1787 (where M == N + 1). */
1792 obstack_1grow (&chunk_obstack
, *p
);
1795 while (ISDIGIT (p
[-1]));
1796 gcc_assert (p
[-1] == 's');
1800 gcc_assert (*p
== '*');
1801 obstack_1grow (&chunk_obstack
, '*');
1807 unsigned int argno2
= strtoul (p
, &end
, 10) - 1;
1809 gcc_assert (argno2
== argno
- 1);
1810 gcc_assert (!any_unnumbered
);
1811 gcc_assert (*p
== '$');
1814 formatters
[argno2
] = formatters
[argno
];
1818 gcc_assert (!any_numbered
);
1819 formatters
[argno
+1] = formatters
[argno
];
1822 gcc_assert (*p
== 's');
1823 obstack_1grow (&chunk_obstack
, 's');
1829 push_back_any_text (cur_token_list
, &chunk_obstack
);
1833 obstack_1grow (&chunk_obstack
, '\0');
1834 push_back_any_text (cur_token_list
, &chunk_obstack
);
1836 /* Start a new token list for the next (non-formatted) text. */
1837 gcc_assert (chunk
< PP_NL_ARGMAX
* 2);
1838 args
[chunk
++] = cur_token_list
= pp_token_list::make (chunk_obstack
);
1841 obstack_1grow (&chunk_obstack
, '\0');
1842 push_back_any_text (cur_token_list
, &chunk_obstack
);
1843 gcc_assert (chunk
< PP_NL_ARGMAX
* 2);
1844 args
[chunk
] = nullptr;
1847 /* Second phase. Replace each formatter with pp_tokens for the formatted
1848 text it corresponds to, consuming va_args from TEXT->m_args_ptr. */
1851 format_phase_2 (pretty_printer
*pp
,
1853 obstack
&chunk_obstack
,
1854 pp_token_list
***formatters
)
1857 for (argno
= 0; formatters
[argno
]; argno
++)
1865 /* We expect a single text token containing the formatter. */
1866 pp_token_list
*tok_list
= *(formatters
[argno
]);
1867 gcc_assert (tok_list
);
1868 gcc_assert (tok_list
->m_first
== tok_list
->m_end
);
1869 gcc_assert (tok_list
->m_first
->m_kind
== pp_token::kind::text
);
1871 /* Accumulate the value of the formatted text into here. */
1872 pp_token_list
*formatted_tok_list
1873 = pp_token_list::make (chunk_obstack
);
1875 /* We do not attempt to enforce any ordering on the modifier
1879 for (p
= as_a
<pp_token_text
*> (tok_list
->m_first
)->m_value
.get ();; p
++)
1884 gcc_assert (!quote
);
1904 gcc_assert (!precision
);
1909 gcc_assert (!precision
);
1914 /* We don't support precision beyond that of "long long". */
1915 gcc_assert (precision
< 2);
1922 gcc_assert (!wide
|| precision
== 0);
1926 push_back_any_text (formatted_tok_list
, &chunk_obstack
);
1927 formatted_tok_list
->push_back
<pp_token_begin_quote
> ();
1934 const char *color
= va_arg (*text
.m_args_ptr
, const char *);
1935 formatted_tok_list
->push_back
<pp_token_begin_color
>
1936 (label_text::borrow (color
));
1942 /* When quoting, print alphanumeric, punctuation, and the space
1943 character unchanged, and all others in hexadecimal with the
1944 "\x" prefix. Otherwise print them all unchanged. */
1945 char chr
= (char) va_arg (*text
.m_args_ptr
, int);
1946 if (ISPRINT (chr
) || !quote
)
1947 pp_character (pp
, chr
);
1950 const char str
[2] = { chr
, '\0' };
1951 pp_quoted_string (pp
, str
, 1);
1959 pp_wide_integer (pp
, va_arg (*text
.m_args_ptr
, HOST_WIDE_INT
));
1961 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
1967 pp_scalar (pp
, "%" HOST_WIDE_INT_PRINT
"o",
1968 va_arg (*text
.m_args_ptr
, unsigned HOST_WIDE_INT
));
1970 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
1976 pp_quoted_string (pp
, va_arg (*text
.m_args_ptr
, const char *));
1978 pp_string (pp
, va_arg (*text
.m_args_ptr
, const char *));
1982 pp_pointer (pp
, va_arg (*text
.m_args_ptr
, void *));
1987 pp_scalar (pp
, HOST_WIDE_INT_PRINT_UNSIGNED
,
1988 va_arg (*text
.m_args_ptr
, unsigned HOST_WIDE_INT
));
1990 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
1995 pp_double (pp
, va_arg (*text
.m_args_ptr
, double));
2000 int *v
= va_arg (*text
.m_args_ptr
, int *);
2001 unsigned len
= va_arg (*text
.m_args_ptr
, unsigned);
2003 for (unsigned i
= 0; i
< len
; ++i
)
2005 pp_scalar (pp
, "%i", v
[i
]);
2017 pp_scalar (pp
, HOST_WIDE_INT_PRINT_HEX
,
2018 va_arg (*text
.m_args_ptr
, unsigned HOST_WIDE_INT
));
2020 pp_integer_with_precision (pp
, *text
.m_args_ptr
, precision
,
2029 /* We handle '%.Ns' and '%.*s' or '%M$.*N$s'
2030 (where M == N + 1). The format string should be verified
2031 already from the first phase. */
2036 n
= strtoul (p
, &end
, 10);
2038 gcc_assert (*p
== 's');
2042 gcc_assert (*p
== '*');
2044 gcc_assert (*p
== 's');
2045 n
= va_arg (*text
.m_args_ptr
, int);
2047 /* This consumes a second entry in the formatters array. */
2048 gcc_assert (formatters
[argno
] == formatters
[argno
+1]);
2052 s
= va_arg (*text
.m_args_ptr
, const char *);
2054 /* Append the lesser of precision and strlen (s) characters
2055 from the array (which need not be a nul-terminated string).
2056 Negative precision is treated as if it were omitted. */
2057 size_t len
= n
< 0 ? strlen (s
) : strnlen (s
, n
);
2059 pp_append_text (pp
, s
, s
+ len
);
2065 /* diagnostic_event_id_t *. */
2066 diagnostic_event_id_ptr event_id
2067 = va_arg (*text
.m_args_ptr
, diagnostic_event_id_ptr
);
2068 gcc_assert (event_id
->known_p ());
2069 formatted_tok_list
->push_back
<pp_token_event_id
> (*event_id
);
2075 const char *url
= va_arg (*text
.m_args_ptr
, const char *);
2076 formatted_tok_list
->push_back
<pp_token_begin_url
>
2077 (label_text::borrow (url
));
2083 pp_element
*element
= va_arg (*text
.m_args_ptr
, pp_element
*);
2084 pp_markup::context
ctxt (*pp
,
2085 quote
, /* by reference */
2086 formatted_tok_list
);
2087 element
->add_to_phase_2 (ctxt
);
2093 /* Call the format decoder.
2094 Pass the address of "quote" so that format decoders can
2095 potentially disable printing of the closing quote
2096 (e.g. when printing "'TYPEDEF' aka 'TYPE'" in the C family
2098 printer_fn format_decoder
= pp_format_decoder (pp
);
2099 gcc_assert (format_decoder
);
2100 gcc_assert (formatted_tok_list
);
2101 bool ok
= format_decoder (pp
, &text
, p
,
2102 precision
, wide
, plus
, hash
, "e
,
2103 *formatted_tok_list
);
2110 push_back_any_text (formatted_tok_list
, &chunk_obstack
);
2111 formatted_tok_list
->push_back
<pp_token_end_quote
> ();
2114 push_back_any_text (formatted_tok_list
, &chunk_obstack
);
2115 delete *formatters
[argno
];
2116 *formatters
[argno
] = formatted_tok_list
;
2120 for (; argno
< PP_NL_ARGMAX
; argno
++)
2121 gcc_assert (!formatters
[argno
]);
2128 obstack_init (&m_obstack
);
2133 obstack_free (&m_obstack
, NULL
);
2136 operator obstack
& () { return m_obstack
; }
2138 void grow (const void *src
, size_t length
)
2140 obstack_grow (&m_obstack
, src
, length
);
2143 void *object_base () const
2145 return m_obstack
.object_base
;
2148 size_t object_size () const
2150 return obstack_object_size (&m_obstack
);
2156 /* Phase 3 of formatting a message (phases 1 and 2 done by pp_format).
2158 Pop a pp_formatted_chunks from chunk_obstack, collecting all the tokens from
2159 phases 1 and 2 of formatting, and writing into text in formatted_obstack.
2161 If URLIFIER is non-null then use it on any quoted text that was not
2162 handled in phases 1 or 2 to potentially add URLs. */
2165 pp_output_formatted_text (pretty_printer
*pp
,
2166 const urlifier
*urlifier
)
2168 output_buffer
* const buffer
= pp_buffer (pp
);
2169 gcc_assert (buffer
->m_obstack
== &buffer
->m_formatted_obstack
);
2171 pp_formatted_chunks
*chunk_array
= buffer
->m_cur_formatted_chunks
;
2172 pp_token_list
* const *token_lists
= chunk_array
->get_token_lists ();
2175 /* Consolidate into one token list. */
2176 pp_token_list
tokens (buffer
->m_chunk_obstack
);
2177 for (unsigned chunk
= 0; token_lists
[chunk
]; chunk
++)
2179 tokens
.push_back_list (std::move (*token_lists
[chunk
]));
2180 delete token_lists
[chunk
];
2183 tokens
.replace_custom_tokens ();
2185 tokens
.merge_consecutive_text_tokens ();
2188 tokens
.apply_urlifier (*urlifier
);
2190 /* This is a third phase, first 2 phases done in pp_format_args.
2191 Now we actually print it. */
2192 if (pp
->m_token_printer
)
2193 pp
->m_token_printer
->print_tokens (pp
, tokens
);
2195 default_token_printer (pp
, tokens
);
2197 /* Close the scope here to ensure that "tokens" above is fully cleared up
2198 before popping the current pp_formatted_chunks, since that latter will pop
2199 the chunk_obstack, and "tokens" may be using blocks within
2200 the current pp_formatted_chunks's chunk_obstack level. */
2203 buffer
->pop_formatted_chunks ();
2206 /* Default implementation of token printing. */
2209 default_token_printer (pretty_printer
*pp
,
2210 const pp_token_list
&tokens
)
2212 /* Convert to text, possibly with colorization, URLs, etc. */
2213 for (auto iter
= tokens
.m_first
; iter
; iter
= iter
->m_next
)
2214 switch (iter
->m_kind
)
2219 case pp_token::kind::text
:
2221 pp_token_text
*sub
= as_a
<pp_token_text
*> (iter
);
2222 pp_string (pp
, sub
->m_value
.get ());
2226 case pp_token::kind::begin_color
:
2228 pp_token_begin_color
*sub
= as_a
<pp_token_begin_color
*> (iter
);
2229 pp_string (pp
, colorize_start (pp_show_color (pp
),
2230 sub
->m_value
.get ()));
2233 case pp_token::kind::end_color
:
2234 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
2237 case pp_token::kind::begin_quote
:
2238 pp_begin_quote (pp
, pp_show_color (pp
));
2240 case pp_token::kind::end_quote
:
2241 pp_end_quote (pp
, pp_show_color (pp
));
2244 case pp_token::kind::begin_url
:
2246 pp_token_begin_url
*sub
= as_a
<pp_token_begin_url
*> (iter
);
2247 pp_begin_url (pp
, sub
->m_value
.get ());
2250 case pp_token::kind::end_url
:
2254 case pp_token::kind::event_id
:
2256 pp_token_event_id
*sub
= as_a
<pp_token_event_id
*> (iter
);
2257 gcc_assert (sub
->m_event_id
.known_p ());
2258 pp_string (pp
, colorize_start (pp_show_color (pp
), "path"));
2259 pp_character (pp
, '(');
2260 pp_decimal_int (pp
, sub
->m_event_id
.one_based ());
2261 pp_character (pp
, ')');
2262 pp_string (pp
, colorize_stop (pp_show_color (pp
)));
2266 case pp_token::kind::custom_data
:
2267 /* These should have been eliminated by replace_custom_tokens. */
2273 /* Helper subroutine of output_verbatim and verbatim. Do the appropriate
2274 settings needed by BUFFER for a verbatim formatting. */
2276 pp_format_verbatim (pretty_printer
*pp
, text_info
*text
)
2278 /* Set verbatim mode. */
2279 pp_wrapping_mode_t oldmode
= pp_set_verbatim_wrapping (pp
);
2281 /* Do the actual formatting. */
2282 pp_format (pp
, text
);
2283 pp_output_formatted_text (pp
);
2285 /* Restore previous settings. */
2286 pp_wrapping_mode (pp
) = oldmode
;
2289 /* Flush the content of BUFFER onto the attached stream. This
2290 function does nothing unless pp->output_buffer->flush_p. */
2292 pp_flush (pretty_printer
*pp
)
2295 if (!pp_buffer (pp
)->m_flush_p
)
2297 pp_write_text_to_stream (pp
);
2298 fflush (pp_buffer (pp
)->m_stream
);
2301 /* Flush the content of BUFFER onto the attached stream independently
2302 of the value of pp->output_buffer->flush_p. */
2304 pp_really_flush (pretty_printer
*pp
)
2307 pp_write_text_to_stream (pp
);
2308 fflush (pp_buffer (pp
)->m_stream
);
2311 /* Sets the number of maximum characters per line PRETTY-PRINTER can
2312 output in line-wrapping mode. A LENGTH value 0 suppresses
2315 pp_set_line_maximum_length (pretty_printer
*pp
, int length
)
2317 pp_line_cutoff (pp
) = length
;
2318 pp
->set_real_maximum_length ();
2321 /* Clear PRETTY-PRINTER output area text info. */
2323 pp_clear_output_area (pretty_printer
*pp
)
2325 obstack_free (pp_buffer (pp
)->m_obstack
,
2326 obstack_base (pp_buffer (pp
)->m_obstack
));
2327 pp_buffer (pp
)->m_line_length
= 0;
2330 /* Set PREFIX for PRETTY-PRINTER, taking ownership of PREFIX, which
2331 will eventually be free-ed. */
2334 pretty_printer::set_prefix (char *prefix
)
2338 set_real_maximum_length ();
2339 m_emitted_prefix
= false;
2340 pp_indentation (this) = 0;
2343 /* Take ownership of PP's prefix, setting it to NULL.
2344 This allows clients to save, override, and then restore an existing
2345 prefix, without it being free-ed. */
2348 pp_take_prefix (pretty_printer
*pp
)
2350 char *result
= pp
->m_prefix
;
2351 pp
->m_prefix
= nullptr;
2355 /* Free PRETTY-PRINTER's prefix, a previously malloc()'d string. */
2357 pp_destroy_prefix (pretty_printer
*pp
)
2361 free (pp
->m_prefix
);
2362 pp
->m_prefix
= nullptr;
2366 /* Write out this pretty_printer's prefix. */
2368 pretty_printer::emit_prefix ()
2372 switch (pp_prefixing_rule (this))
2375 case DIAGNOSTICS_SHOW_PREFIX_NEVER
:
2378 case DIAGNOSTICS_SHOW_PREFIX_ONCE
:
2379 if (m_emitted_prefix
)
2384 pp_indentation (this) += 3;
2387 case DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
:
2389 int prefix_length
= strlen (m_prefix
);
2390 pp_append_r (this, m_prefix
, prefix_length
);
2391 m_emitted_prefix
= true;
2398 /* Construct a PRETTY-PRINTER of MAXIMUM_LENGTH characters per line. */
2400 pretty_printer::pretty_printer (int maximum_length
)
2401 : m_buffer (new (XCNEW (output_buffer
)) output_buffer ()),
2403 m_padding (pp_none
),
2404 m_maximum_length (0),
2407 m_format_decoder (nullptr),
2408 m_format_postprocessor (NULL
),
2409 m_token_printer (nullptr),
2410 m_emitted_prefix (false),
2411 m_need_newline (false),
2412 m_translate_identifiers (true),
2413 m_show_color (false),
2414 m_show_highlight_colors (false),
2415 m_url_format (URL_FORMAT_NONE
),
2416 m_skipping_null_url (false)
2418 pp_line_cutoff (this) = maximum_length
;
2419 /* By default, we emit prefixes once per message. */
2420 pp_prefixing_rule (this) = DIAGNOSTICS_SHOW_PREFIX_ONCE
;
2421 pp_set_prefix (this, NULL
);
2424 /* Copy constructor for pretty_printer. */
2426 pretty_printer::pretty_printer (const pretty_printer
&other
)
2427 : m_buffer (new (XCNEW (output_buffer
)) output_buffer ()),
2429 m_padding (other
.m_padding
),
2430 m_maximum_length (other
.m_maximum_length
),
2431 m_indent_skip (other
.m_indent_skip
),
2432 m_wrapping (other
.m_wrapping
),
2433 m_format_decoder (other
.m_format_decoder
),
2434 m_format_postprocessor (NULL
),
2435 m_token_printer (other
.m_token_printer
),
2436 m_emitted_prefix (other
.m_emitted_prefix
),
2437 m_need_newline (other
.m_need_newline
),
2438 m_translate_identifiers (other
.m_translate_identifiers
),
2439 m_show_color (other
.m_show_color
),
2440 m_show_highlight_colors (other
.m_show_highlight_colors
),
2441 m_url_format (other
.m_url_format
),
2442 m_skipping_null_url (false)
2444 pp_line_cutoff (this) = m_maximum_length
;
2445 /* By default, we emit prefixes once per message. */
2446 pp_prefixing_rule (this) = pp_prefixing_rule (&other
);
2447 pp_set_prefix (this, NULL
);
2449 if (other
.m_format_postprocessor
)
2450 m_format_postprocessor
= other
.m_format_postprocessor
->clone ();
2453 pretty_printer::~pretty_printer ()
2455 if (m_format_postprocessor
)
2456 delete m_format_postprocessor
;
2457 m_buffer
->~output_buffer ();
2462 /* Base class implementation of pretty_printer::clone vfunc. */
2465 pretty_printer::clone () const
2467 return new pretty_printer (*this);
2470 /* Append a string delimited by START and END to the output area of
2471 PRETTY-PRINTER. No line wrapping is done. However, if beginning a
2472 new line then emit PRETTY-PRINTER's prefix and skip any leading
2473 whitespace if appropriate. The caller must ensure that it is
2476 pp_append_text (pretty_printer
*pp
, const char *start
, const char *end
)
2478 /* Emit prefix and skip whitespace if we're starting a new line. */
2479 if (pp_buffer (pp
)->m_line_length
== 0)
2482 if (pp_is_wrapping_line (pp
))
2483 while (start
!= end
&& *start
== ' ')
2486 pp_append_r (pp
, start
, end
- start
);
2489 /* Finishes constructing a NULL-terminated character string representing
2490 the PRETTY-PRINTED text. */
2492 pp_formatted_text (pretty_printer
*pp
)
2494 return output_buffer_formatted_text (pp_buffer (pp
));
2497 /* Return a pointer to the last character emitted in PRETTY-PRINTER's
2498 output area. A NULL pointer means no character available. */
2500 pp_last_position_in_text (const pretty_printer
*pp
)
2502 return output_buffer_last_position_in_text (pp_buffer (pp
));
2505 /* Return the amount of characters PRETTY-PRINTER can accept to
2506 make a full line. Meaningful only in line-wrapping mode. */
2508 pretty_printer::remaining_character_count_for_line ()
2510 return m_maximum_length
- pp_buffer (this)->m_line_length
;
2513 /* Format a message into BUFFER a la printf. */
2515 pp_printf (pretty_printer
*pp
, const char *msg
, ...)
2520 text_info
text (msg
, &ap
, errno
);
2521 pp_format (pp
, &text
);
2522 pp_output_formatted_text (pp
);
2527 /* Output MESSAGE verbatim into BUFFER. */
2529 pp_verbatim (pretty_printer
*pp
, const char *msg
, ...)
2534 text_info
text (msg
, &ap
, errno
);
2535 pp_format_verbatim (pp
, &text
);
2541 /* Have PRETTY-PRINTER start a new line. */
2543 pp_newline (pretty_printer
*pp
)
2545 obstack_1grow (pp_buffer (pp
)->m_obstack
, '\n');
2546 pp_needs_newline (pp
) = false;
2547 pp_buffer (pp
)->m_line_length
= 0;
2550 /* Have PRETTY-PRINTER add a CHARACTER. */
2552 pp_character (pretty_printer
*pp
, int c
)
2554 if (pp_is_wrapping_line (pp
)
2555 /* If printing UTF-8, don't wrap in the middle of a sequence. */
2556 && (((unsigned int) c
) & 0xC0) != 0x80
2557 && pp
->remaining_character_count_for_line () <= 0)
2563 obstack_1grow (pp_buffer (pp
)->m_obstack
, c
);
2564 ++pp_buffer (pp
)->m_line_length
;
2567 /* Append a STRING to the output area of PRETTY-PRINTER; the STRING may
2568 be line-wrapped if in appropriate mode. */
2570 pp_string (pretty_printer
*pp
, const char *str
)
2572 gcc_checking_assert (str
);
2573 pp_maybe_wrap_text (pp
, str
, str
+ strlen (str
));
2576 /* As per pp_string, but only append the first LEN of STR. */
2579 pp_string_n (pretty_printer
*pp
, const char *str
, size_t len
)
2581 gcc_checking_assert (str
);
2582 pp_maybe_wrap_text (pp
, str
, str
+ len
);
2585 /* Append code point C to the output area of PRETTY-PRINTER, encoding it
2589 pp_unicode_character (pretty_printer
*pp
, unsigned c
)
2591 static const uchar masks
[6] = { 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
2592 static const uchar limits
[6] = { 0x80, 0xE0, 0xF0, 0xF8, 0xFC, 0xFE };
2594 uchar buf
[6], *p
= &buf
[6];
2603 *--p
= ((c
& 0x3F) | 0x80);
2607 while (c
>= 0x3F || (c
& limits
[nbytes
-1]));
2608 *--p
= (c
| masks
[nbytes
-1]);
2611 pp_append_r (pp
, (const char *)p
, nbytes
);
2614 /* Append the leading N characters of STRING to the output area of
2615 PRETTY-PRINTER, quoting in hexadecimal non-printable characters.
2616 Setting N = -1 is as if N were set to strlen (STRING). The STRING
2617 may be line-wrapped if in appropriate mode. */
2619 pp_quoted_string (pretty_printer
*pp
, const char *str
, size_t n
/* = -1 */)
2621 gcc_checking_assert (str
);
2623 const char *last
= str
;
2626 /* Compute the length if not specified. */
2627 if (n
== (size_t) -1)
2630 for (ps
= str
; n
; ++ps
, --n
)
2635 /* Don't escape a valid UTF-8 extended char. */
2636 const unsigned char *ups
= (const unsigned char *) ps
;
2639 unsigned int extended_char
;
2640 const int valid_utf8_len
= decode_utf8_char (ups
, n
, &extended_char
);
2641 if (valid_utf8_len
> 0)
2643 ps
+= valid_utf8_len
- 1;
2644 n
-= valid_utf8_len
- 1;
2650 pp_maybe_wrap_text (pp
, last
, ps
);
2652 /* Append the hexadecimal value of the character. Allocate a buffer
2653 that's large enough for a 32-bit char plus the hex prefix. */
2655 int n
= sprintf (buf
, "\\x%02x", (unsigned char)*ps
);
2656 pp_maybe_wrap_text (pp
, buf
, buf
+ n
);
2660 pp_maybe_wrap_text (pp
, last
, ps
);
2663 /* Maybe print out a whitespace if needed. */
2666 pretty_printer::maybe_space ()
2668 if (m_padding
!= pp_none
)
2671 m_padding
= pp_none
;
2675 // Add a newline to the pretty printer PP and flush formatted text.
2678 pp_newline_and_flush (pretty_printer
*pp
)
2682 pp_needs_newline (pp
) = false;
2685 // Add a newline to the pretty printer PP, followed by indentation.
2688 pp_newline_and_indent (pretty_printer
*pp
, int n
)
2690 pp_indentation (pp
) += n
;
2693 pp_needs_newline (pp
) = false;
2696 // Add separator C, followed by a single whitespace.
2699 pp_separate_with (pretty_printer
*pp
, char c
)
2701 pp_character (pp
, c
);
2705 /* Add a localized open quote, and if SHOW_COLOR is true, begin colorizing
2706 using the "quote" color. */
2709 pp_begin_quote (pretty_printer
*pp
, bool show_color
)
2711 pp_string (pp
, open_quote
);
2712 pp_string (pp
, colorize_start (show_color
, "quote"));
2715 /* If SHOW_COLOR is true, stop colorizing.
2716 Add a localized close quote. */
2719 pp_end_quote (pretty_printer
*pp
, bool show_color
)
2721 pp_string (pp
, colorize_stop (show_color
));
2722 pp_string (pp
, close_quote
);
2726 /* The string starting at P has LEN (at least 1) bytes left; if they
2727 start with a valid UTF-8 sequence, return the length of that
2728 sequence and set *VALUE to the value of that sequence, and
2729 otherwise return 0 and set *VALUE to (unsigned int) -1. */
2732 decode_utf8_char (const unsigned char *p
, size_t len
, unsigned int *value
)
2734 unsigned int t
= *p
;
2740 size_t utf8_len
= 0;
2743 for (t
= *p
; t
& 0x80; t
<<= 1)
2746 if (utf8_len
> len
|| utf8_len
< 2 || utf8_len
> 6)
2748 *value
= (unsigned int) -1;
2751 ch
= *p
& ((1 << (7 - utf8_len
)) - 1);
2752 for (i
= 1; i
< utf8_len
; i
++)
2754 unsigned int u
= p
[i
];
2755 if ((u
& 0xC0) != 0x80)
2757 *value
= (unsigned int) -1;
2760 ch
= (ch
<< 6) | (u
& 0x3F);
2762 if ( (ch
<= 0x7F && utf8_len
> 1)
2763 || (ch
<= 0x7FF && utf8_len
> 2)
2764 || (ch
<= 0xFFFF && utf8_len
> 3)
2765 || (ch
<= 0x1FFFFF && utf8_len
> 4)
2766 || (ch
<= 0x3FFFFFF && utf8_len
> 5)
2767 || (ch
>= 0xD800 && ch
<= 0xDFFF))
2769 *value
= (unsigned int) -1;
2782 /* Allocator for identifier_to_locale and corresponding function to
2785 void *(*identifier_to_locale_alloc
) (size_t) = xmalloc
;
2786 void (*identifier_to_locale_free
) (void *) = free
;
2788 /* Given IDENT, an identifier in the internal encoding, return a
2789 version of IDENT suitable for diagnostics in the locale character
2790 set: either IDENT itself, or a string, allocated using
2791 identifier_to_locale_alloc, converted to the locale character set
2792 and using escape sequences if not representable in the locale
2793 character set or containing control characters or invalid byte
2794 sequences. Existing backslashes in IDENT are not doubled, so the
2795 result may not uniquely specify the contents of an arbitrary byte
2796 sequence identifier. */
2799 identifier_to_locale (const char *ident
)
2801 const unsigned char *uid
= (const unsigned char *) ident
;
2802 size_t idlen
= strlen (ident
);
2803 bool valid_printable_utf8
= true;
2804 bool all_ascii
= true;
2807 for (i
= 0; i
< idlen
;)
2810 size_t utf8_len
= decode_utf8_char (&uid
[i
], idlen
- i
, &c
);
2811 if (utf8_len
== 0 || c
<= 0x1F || (c
>= 0x7F && c
<= 0x9F))
2813 valid_printable_utf8
= false;
2821 /* If IDENT contains invalid UTF-8 sequences (which may occur with
2822 attributes putting arbitrary byte sequences in identifiers), or
2823 control characters, we use octal escape sequences for all bytes
2824 outside printable ASCII. */
2825 if (!valid_printable_utf8
)
2827 char *ret
= (char *) identifier_to_locale_alloc (4 * idlen
+ 1);
2829 for (i
= 0; i
< idlen
; i
++)
2831 if (uid
[i
] > 0x1F && uid
[i
] < 0x7F)
2835 sprintf (p
, "\\%03o", uid
[i
]);
2843 /* Otherwise, if it is valid printable ASCII, or printable UTF-8
2844 with the locale character set being UTF-8, IDENT is used. */
2845 if (all_ascii
|| locale_utf8
)
2848 /* Otherwise IDENT is converted to the locale character set if
2850 #if defined ENABLE_NLS && defined HAVE_LANGINFO_CODESET && HAVE_ICONV
2851 if (locale_encoding
!= NULL
)
2853 iconv_t cd
= iconv_open (locale_encoding
, "UTF-8");
2854 bool conversion_ok
= true;
2856 if (cd
!= (iconv_t
) -1)
2858 size_t ret_alloc
= 4 * idlen
+ 1;
2861 /* Repeat the whole conversion process as needed with
2862 larger buffers so non-reversible transformations can
2863 always be detected. */
2864 ICONV_CONST
char *inbuf
= CONST_CAST (char *, ident
);
2866 size_t inbytesleft
= idlen
;
2867 size_t outbytesleft
= ret_alloc
- 1;
2870 ret
= (char *) identifier_to_locale_alloc (ret_alloc
);
2873 if (iconv (cd
, 0, 0, 0, 0) == (size_t) -1)
2875 conversion_ok
= false;
2879 iconv_ret
= iconv (cd
, &inbuf
, &inbytesleft
,
2880 &outbuf
, &outbytesleft
);
2881 if (iconv_ret
== (size_t) -1 || inbytesleft
!= 0)
2886 identifier_to_locale_free (ret
);
2892 conversion_ok
= false;
2896 else if (iconv_ret
!= 0)
2898 conversion_ok
= false;
2901 /* Return to initial shift state. */
2902 if (iconv (cd
, 0, 0, &outbuf
, &outbytesleft
) == (size_t) -1)
2907 identifier_to_locale_free (ret
);
2913 conversion_ok
= false;
2927 /* Otherwise, convert non-ASCII characters in IDENT to UCNs. */
2929 char *ret
= (char *) identifier_to_locale_alloc (10 * idlen
+ 1);
2931 for (i
= 0; i
< idlen
;)
2934 size_t utf8_len
= decode_utf8_char (&uid
[i
], idlen
- i
, &c
);
2939 sprintf (p
, "\\U%08x", c
);
2949 /* Support for encoding URLs.
2950 See egmontkob/Hyperlinks_in_Terminal_Emulators.md
2951 ( https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda ).
2953 > A hyperlink is opened upon encountering an OSC 8 escape sequence with
2954 > the target URI. The syntax is
2956 > OSC 8 ; params ; URI ST
2958 > A hyperlink is closed with the same escape sequence, omitting the
2959 > parameters and the URI but keeping the separators:
2963 > OSC (operating system command) is typically ESC ].
2965 Use BEL instead of ST, as that is currently rendered better in some
2966 terminal emulators that don't support OSC 8, like konsole. */
2968 /* If URL-printing is enabled, write an "open URL" escape sequence to PP
2969 for the given URL. */
2972 pretty_printer::begin_url (const char *url
)
2976 /* Handle null URL by skipping all output here,
2977 and in the next pp_end_url. */
2978 m_skipping_null_url
= true;
2981 switch (m_url_format
)
2983 case URL_FORMAT_NONE
:
2986 pp_string (this, "\33]8;;");
2987 pp_string (this, url
);
2988 pp_string (this, "\33\\");
2990 case URL_FORMAT_BEL
:
2991 pp_string (this, "\33]8;;");
2992 pp_string (this, url
);
2993 pp_string (this, "\a");
3000 /* Helper function for pp_end_url and pp_format, return the "close URL" escape
3004 get_end_url_string (pretty_printer
*pp
)
3006 switch (pp
->get_url_format ())
3008 case URL_FORMAT_NONE
:
3011 return "\33]8;;\33\\";
3012 case URL_FORMAT_BEL
:
3019 /* If URL-printing is enabled, write a "close URL" escape sequence to PP. */
3022 pretty_printer::end_url ()
3024 if (m_skipping_null_url
)
3026 /* We gracefully handle pp_begin_url (NULL) by omitting output for
3027 both begin and end. Here we handle the latter. */
3028 m_skipping_null_url
= false;
3031 if (m_url_format
!= URL_FORMAT_NONE
)
3032 pp_string (this, get_end_url_string (this));
3035 /* Dump state of this pretty_printer to OUT, for debugging. */
3038 pretty_printer::dump (FILE *out
) const
3040 m_buffer
->dump (out
);
3043 /* class pp_markup::context. */
3046 pp_markup::context::begin_quote ()
3048 gcc_assert (!m_quoted
);
3049 gcc_assert (m_formatted_token_list
);
3050 push_back_any_text ();
3051 m_formatted_token_list
->push_back
<pp_token_begin_quote
> ();
3056 pp_markup::context::end_quote ()
3058 /* Bail out if the quotes have already been ended, such as by
3059 printing a type emitting "TYPEDEF' {aka `TYPE'}". */
3062 gcc_assert (m_formatted_token_list
);
3063 push_back_any_text ();
3064 m_formatted_token_list
->push_back
<pp_token_end_quote
> ();
3069 pp_markup::context::begin_highlight_color (const char *color_name
)
3071 if (!pp_show_highlight_colors (&m_pp
))
3074 push_back_any_text ();
3075 m_formatted_token_list
->push_back
<pp_token_begin_color
>
3076 (label_text::borrow (color_name
));
3080 pp_markup::context::end_highlight_color ()
3082 if (!pp_show_highlight_colors (&m_pp
))
3085 push_back_any_text ();
3086 m_formatted_token_list
->push_back
<pp_token_end_color
> ();
3090 pp_markup::context::push_back_any_text ()
3092 obstack
*cur_obstack
= m_buf
.m_obstack
;
3093 obstack_1grow (cur_obstack
, '\0');
3094 m_formatted_token_list
->push_back_text
3095 (label_text::borrow (XOBFINISH (cur_obstack
,
3100 pp_markup::comma_separated_quoted_strings::add_to_phase_2 (context
&ctxt
)
3102 for (unsigned i
= 0; i
< m_strings
.length (); i
++)
3105 pp_string (&ctxt
.m_pp
, ", ");
3106 ctxt
.begin_quote ();
3107 pp_string (&ctxt
.m_pp
, m_strings
[i
]);
3112 /* Color names for expressing "expected" vs "actual" values. */
3113 const char *const highlight_colors::expected
= "highlight-a";
3114 const char *const highlight_colors::actual
= "highlight-b";
3116 /* Color names for expressing "LHS" vs "RHS" values in a binary operation. */
3117 const char *const highlight_colors::lhs
= "highlight-a";
3118 const char *const highlight_colors::rhs
= "highlight-b";
3122 namespace selftest
{
3124 /* Smoketest for pretty_printer. */
3127 test_basic_printing ()
3130 pp_string (&pp
, "hello");
3132 pp_string (&pp
, "world");
3134 ASSERT_STREQ ("hello world", pp_formatted_text (&pp
));
3137 /* Helper function for testing pp_format.
3138 Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
3139 prints EXPECTED, assuming that pp_show_color is SHOW_COLOR. */
3142 assert_pp_format_va (const location
&loc
, const char *expected
,
3143 bool show_color
, const char *fmt
, va_list *ap
)
3146 rich_location
rich_loc (line_table
, UNKNOWN_LOCATION
);
3148 text_info
ti (fmt
, ap
, 0, nullptr, &rich_loc
);
3150 pp_show_color (&pp
) = show_color
;
3151 pp_format (&pp
, &ti
);
3152 pp_output_formatted_text (&pp
);
3153 ASSERT_STREQ_AT (loc
, expected
, pp_formatted_text (&pp
));
3156 /* Verify that pp_format (FMT, ...) followed by pp_output_formatted_text
3157 prints EXPECTED, with show_color disabled. */
3160 assert_pp_format (const location
&loc
, const char *expected
,
3161 const char *fmt
, ...)
3166 assert_pp_format_va (loc
, expected
, false, fmt
, &ap
);
3170 /* As above, but with colorization enabled. */
3173 assert_pp_format_colored (const location
&loc
, const char *expected
,
3174 const char *fmt
, ...)
3176 /* The tests of colorization assume the default color scheme.
3177 If GCC_COLORS is set, then the colors have potentially been
3178 overridden; skip the test. */
3179 if (getenv ("GCC_COLORS"))
3185 assert_pp_format_va (loc
, expected
, true, fmt
, &ap
);
3189 /* Helper function for calling testing pp_format,
3190 by calling assert_pp_format with various numbers of arguments.
3191 These exist mostly to avoid having to write SELFTEST_LOCATION
3192 throughout test_pp_format. */
3194 #define ASSERT_PP_FORMAT_1(EXPECTED, FMT, ARG1) \
3195 SELFTEST_BEGIN_STMT \
3196 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3200 #define ASSERT_PP_FORMAT_2(EXPECTED, FMT, ARG1, ARG2) \
3201 SELFTEST_BEGIN_STMT \
3202 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3206 #define ASSERT_PP_FORMAT_3(EXPECTED, FMT, ARG1, ARG2, ARG3) \
3207 SELFTEST_BEGIN_STMT \
3208 assert_pp_format ((SELFTEST_LOCATION), (EXPECTED), (FMT), \
3209 (ARG1), (ARG2), (ARG3)); \
3212 class test_element
: public pp_element
3215 test_element (const char *text
) : m_text (text
) {}
3217 void add_to_phase_2 (pp_markup::context
&ctxt
) final override
3219 ctxt
.begin_quote ();
3220 pp_string (&ctxt
.m_pp
, m_text
);
3228 /* Verify that pp_format works, for various format codes. */
3233 /* Avoid introducing locale-specific differences in the results
3234 by hardcoding open_quote and close_quote. */
3235 auto_fix_quotes fix_quotes
;
3237 /* Verify that plain text is passed through unchanged. */
3238 assert_pp_format (SELFTEST_LOCATION
, "unformatted", "unformatted");
3240 /* Verify various individual format codes, in the order listed in the
3241 comment for pp_format above. For each code, we append a second
3242 argument with a known bit pattern (0x12345678), to ensure that we
3243 are consuming arguments correctly. */
3244 ASSERT_PP_FORMAT_2 ("-27 12345678", "%d %x", -27, 0x12345678);
3245 ASSERT_PP_FORMAT_2 ("-5 12345678", "%i %x", -5, 0x12345678);
3246 ASSERT_PP_FORMAT_2 ("10 12345678", "%u %x", 10, 0x12345678);
3247 ASSERT_PP_FORMAT_2 ("17 12345678", "%o %x", 15, 0x12345678);
3248 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%x %x", 0xcafebabe, 0x12345678);
3249 ASSERT_PP_FORMAT_2 ("-27 12345678", "%ld %x", (long)-27, 0x12345678);
3250 ASSERT_PP_FORMAT_2 ("-5 12345678", "%li %x", (long)-5, 0x12345678);
3251 ASSERT_PP_FORMAT_2 ("10 12345678", "%lu %x", (long)10, 0x12345678);
3252 ASSERT_PP_FORMAT_2 ("17 12345678", "%lo %x", (long)15, 0x12345678);
3253 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%lx %x", (long)0xcafebabe,
3255 ASSERT_PP_FORMAT_2 ("-27 12345678", "%lld %x", (long long)-27, 0x12345678);
3256 ASSERT_PP_FORMAT_2 ("-5 12345678", "%lli %x", (long long)-5, 0x12345678);
3257 ASSERT_PP_FORMAT_2 ("10 12345678", "%llu %x", (long long)10, 0x12345678);
3258 ASSERT_PP_FORMAT_2 ("17 12345678", "%llo %x", (long long)15, 0x12345678);
3259 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%llx %x", (long long)0xcafebabe,
3261 ASSERT_PP_FORMAT_2 ("-27 12345678", "%wd %x", HOST_WIDE_INT_C (-27),
3263 ASSERT_PP_FORMAT_2 ("-5 12345678", "%wi %x", HOST_WIDE_INT_C (-5),
3265 ASSERT_PP_FORMAT_2 ("10 12345678", "%wu %x", HOST_WIDE_INT_UC (10),
3267 ASSERT_PP_FORMAT_2 ("17 12345678", "%wo %x", HOST_WIDE_INT_C (15),
3269 ASSERT_PP_FORMAT_2 ("0xcafebabe 12345678", "%wx %x",
3270 HOST_WIDE_INT_C (0xcafebabe), 0x12345678);
3271 ASSERT_PP_FORMAT_2 ("-27 12345678", "%zd %x", (ssize_t
)-27, 0x12345678);
3272 ASSERT_PP_FORMAT_2 ("-5 12345678", "%zi %x", (ssize_t
)-5, 0x12345678);
3273 ASSERT_PP_FORMAT_2 ("10 12345678", "%zu %x", (size_t)10, 0x12345678);
3274 ASSERT_PP_FORMAT_2 ("17 12345678", "%zo %x", (size_t)15, 0x12345678);
3275 ASSERT_PP_FORMAT_2 ("cafebabe 12345678", "%zx %x", (size_t)0xcafebabe,
3277 ASSERT_PP_FORMAT_2 ("-27 12345678", "%td %x", (ptrdiff_t)-27, 0x12345678);
3278 ASSERT_PP_FORMAT_2 ("-5 12345678", "%ti %x", (ptrdiff_t)-5, 0x12345678);
3279 ASSERT_PP_FORMAT_2 ("10 12345678", "%tu %x", (ptrdiff_t)10, 0x12345678);
3280 ASSERT_PP_FORMAT_2 ("17 12345678", "%to %x", (ptrdiff_t)15, 0x12345678);
3281 ASSERT_PP_FORMAT_2 ("1afebabe 12345678", "%tx %x", (ptrdiff_t)0x1afebabe,
3283 ASSERT_PP_FORMAT_2 ("1.000000 12345678", "%f %x", 1.0, 0x12345678);
3284 ASSERT_PP_FORMAT_2 ("A 12345678", "%c %x", 'A', 0x12345678);
3285 ASSERT_PP_FORMAT_2 ("hello world 12345678", "%s %x", "hello world",
3288 /* Not nul-terminated. */
3289 char arr
[5] = { '1', '2', '3', '4', '5' };
3290 ASSERT_PP_FORMAT_3 ("123 12345678", "%.*s %x", 3, arr
, 0x12345678);
3291 ASSERT_PP_FORMAT_3 ("1234 12345678", "%.*s %x", -1, "1234", 0x12345678);
3292 ASSERT_PP_FORMAT_3 ("12345 12345678", "%.*s %x", 7, "12345", 0x12345678);
3294 /* We can't test for %p; the pointer is printed in an implementation-defined
3296 ASSERT_PP_FORMAT_2 ("normal colored normal 12345678",
3297 "normal %rcolored%R normal %x",
3298 "error", 0x12345678);
3299 assert_pp_format_colored
3301 "normal \33[01;31m\33[Kcolored\33[m\33[K normal 12345678",
3302 "normal %rcolored%R normal %x", "error", 0x12345678);
3304 %m: strerror(text->err_no) - does not consume a value from args_ptr. */
3305 ASSERT_PP_FORMAT_1 ("% 12345678", "%% %x", 0x12345678);
3306 ASSERT_PP_FORMAT_1 ("` 12345678", "%< %x", 0x12345678);
3307 ASSERT_PP_FORMAT_1 ("' 12345678", "%> %x", 0x12345678);
3308 ASSERT_PP_FORMAT_1 ("' 12345678", "%' %x", 0x12345678);
3309 ASSERT_PP_FORMAT_3 ("abc 12345678", "%.*s %x", 3, "abcdef", 0x12345678);
3310 ASSERT_PP_FORMAT_2 ("abc 12345678", "%.3s %x", "abcdef", 0x12345678);
3312 /* Verify flag 'q'. */
3313 ASSERT_PP_FORMAT_2 ("`foo' 12345678", "%qs %x", "foo", 0x12345678);
3314 assert_pp_format_colored (SELFTEST_LOCATION
,
3315 "`\33[01m\33[Kfoo\33[m\33[K' 12345678", "%qs %x",
3319 diagnostic_event_id_t
first (2);
3320 diagnostic_event_id_t
second (7);
3322 ASSERT_PP_FORMAT_2 ("first `free' at (3); second `free' at (8)",
3323 "first %<free%> at %@; second %<free%> at %@",
3325 assert_pp_format_colored
3327 "first `\e[01m\e[Kfree\e[m\e[K' at \e[01;36m\e[K(3)\e[m\e[K;"
3328 " second `\e[01m\e[Kfree\e[m\e[K' at \e[01;36m\e[K(8)\e[m\e[K",
3329 "first %<free%> at %@; second %<free%> at %@",
3334 int v
[] = { 1, 2, 3 };
3335 ASSERT_PP_FORMAT_3 ("1, 2, 3 12345678", "%Z %x", v
, 3, 0x12345678);
3338 ASSERT_PP_FORMAT_3 ("0 12345678", "%Z %x", v2
, 1, 0x12345678);
3342 test_element
foo ("foo");
3343 test_element
bar ("bar");
3344 ASSERT_PP_FORMAT_2 ("before `foo' `bar' after",
3345 "before %e %e after",
3349 /* Verify that combinations work, along with unformatted text. */
3350 assert_pp_format (SELFTEST_LOCATION
,
3351 "the quick brown fox jumps over the lazy dog",
3352 "the %s %s %s jumps over the %s %s",
3353 "quick", "brown", "fox", "lazy", "dog");
3354 assert_pp_format (SELFTEST_LOCATION
, "item 3 of 7", "item %i of %i", 3, 7);
3355 assert_pp_format (SELFTEST_LOCATION
, "problem with `bar' at line 10",
3356 "problem with %qs at line %i", "bar", 10);
3358 /* Verified numbered args. */
3359 assert_pp_format (SELFTEST_LOCATION
,
3360 "foo: second bar: first",
3361 "foo: %2$s bar: %1$s",
3363 assert_pp_format (SELFTEST_LOCATION
,
3364 "foo: 1066 bar: 1776",
3365 "foo: %2$i bar: %1$i",
3367 assert_pp_format (SELFTEST_LOCATION
,
3368 "foo: second bar: 1776",
3369 "foo: %2$s bar: %1$i",
3374 test_merge_consecutive_text_tokens ()
3377 pp_token_list
list (s
);
3378 list
.push_back_text (label_text::borrow ("hello"));
3379 list
.push_back_text (label_text::borrow (" "));
3380 list
.push_back_text (label_text::take (xstrdup ("world")));
3381 list
.push_back_text (label_text::borrow ("!"));
3383 list
.merge_consecutive_text_tokens ();
3384 // We expect a single text token, with concatenated text
3385 ASSERT_EQ (list
.m_first
, list
.m_end
);
3386 pp_token
*tok
= list
.m_first
;
3387 ASSERT_NE (tok
, nullptr);
3388 ASSERT_EQ (tok
->m_kind
, pp_token::kind::text
);
3389 ASSERT_STREQ (as_a
<pp_token_text
*> (tok
)->m_value
.get (), "hello world!");
3392 /* Verify that we can create custom tokens that can be lowered
3396 test_custom_tokens_1 ()
3398 struct custom_token_adder
: public pp_element
3401 struct value
: public pp_token_custom_data::value
3403 value (custom_token_adder
&adder
)
3406 m_adder
.m_num_living_values
++;
3408 value (const value
&other
)
3409 : m_adder (other
.m_adder
)
3411 m_adder
.m_num_living_values
++;
3413 value (value
&&other
)
3414 : m_adder (other
.m_adder
)
3416 m_adder
.m_num_living_values
++;
3418 value
&operator= (const value
&other
) = delete;
3419 value
&operator= (value
&&other
) = delete;
3422 m_adder
.m_num_living_values
--;
3425 void dump (FILE *out
) const final override
3427 fprintf (out
, "\"%s\"", m_adder
.m_name
);
3430 bool as_standard_tokens (pp_token_list
&out
) final override
3432 ASSERT_TRUE (m_adder
.m_num_living_values
> 0);
3433 out
.push_back
<pp_token_text
> (label_text::borrow (m_adder
.m_name
));
3437 custom_token_adder
&m_adder
;
3440 custom_token_adder (const char *name
)
3442 m_num_living_values (0)
3446 void add_to_phase_2 (pp_markup::context
&ctxt
) final override
3448 auto val_ptr
= make_unique
<value
> (*this);
3449 ctxt
.m_formatted_token_list
->push_back
<pp_token_custom_data
>
3450 (std::move (val_ptr
));
3454 int m_num_living_values
;
3457 custom_token_adder
e1 ("foo");
3458 custom_token_adder
e2 ("bar");
3459 ASSERT_EQ (e1
.m_num_living_values
, 0);
3460 ASSERT_EQ (e2
.m_num_living_values
, 0);
3463 pp_printf (&pp
, "before %e middle %e after", &e1
, &e2
);
3465 /* Verify that instances were cleaned up. */
3466 ASSERT_EQ (e1
.m_num_living_values
, 0);
3467 ASSERT_EQ (e2
.m_num_living_values
, 0);
3469 ASSERT_STREQ (pp_formatted_text (&pp
),
3470 "before foo middle bar after");
3473 /* Verify that we can create custom tokens that aren't lowered
3474 in phase 3, but instead are handled by a custom token_printer.
3475 Use this to verify the inputs seen by such token_printers. */
3478 test_custom_tokens_2 ()
3480 struct custom_token_adder
: public pp_element
3482 struct value
: public pp_token_custom_data::value
3485 value (custom_token_adder
&adder
)
3488 m_adder
.m_num_living_values
++;
3490 value (const value
&other
)
3491 : m_adder (other
.m_adder
)
3493 m_adder
.m_num_living_values
++;
3495 value (value
&&other
)
3496 : m_adder (other
.m_adder
)
3498 m_adder
.m_num_living_values
++;
3500 value
&operator= (const value
&other
) = delete;
3501 value
&operator= (value
&&other
) = delete;
3504 m_adder
.m_num_living_values
--;
3507 void dump (FILE *out
) const final override
3509 fprintf (out
, "\"%s\"", m_adder
.m_name
);
3512 bool as_standard_tokens (pp_token_list
&) final override
3517 custom_token_adder
&m_adder
;
3520 custom_token_adder (const char *name
)
3522 m_num_living_values (0)
3526 void add_to_phase_2 (pp_markup::context
&ctxt
) final override
3528 auto val_ptr
= make_unique
<value
> (*this);
3529 ctxt
.m_formatted_token_list
->push_back
<pp_token_custom_data
>
3530 (std::move (val_ptr
));
3534 int m_num_living_values
;
3537 class custom_token_printer
: public token_printer
3539 void print_tokens (pretty_printer
*pp
,
3540 const pp_token_list
&tokens
) final override
3542 /* Verify that TOKENS has:
3543 [TEXT("before "), CUSTOM("foo"), TEXT(" middle "), CUSTOM("bar"),
3545 pp_token
*tok_0
= tokens
.m_first
;
3546 ASSERT_NE (tok_0
, nullptr);
3547 ASSERT_EQ (tok_0
->m_kind
, pp_token::kind::text
);
3548 ASSERT_STREQ (as_a
<pp_token_text
*> (tok_0
)->m_value
.get (),
3551 pp_token
*tok_1
= tok_0
->m_next
;
3552 ASSERT_NE (tok_1
, nullptr);
3553 ASSERT_EQ (tok_1
->m_prev
, tok_0
);
3554 ASSERT_EQ (tok_1
->m_kind
, pp_token::kind::custom_data
);
3556 custom_token_adder::value
*v1
3557 = static_cast <custom_token_adder::value
*>
3558 (as_a
<pp_token_custom_data
*> (tok_1
)->m_value
.get ());
3559 ASSERT_STREQ (v1
->m_adder
.m_name
, "foo");
3560 ASSERT_TRUE (v1
->m_adder
.m_num_living_values
> 0);
3562 pp_token
*tok_2
= tok_1
->m_next
;
3563 ASSERT_NE (tok_2
, nullptr);
3564 ASSERT_EQ (tok_2
->m_prev
, tok_1
);
3565 ASSERT_EQ (tok_2
->m_kind
, pp_token::kind::text
);
3566 ASSERT_STREQ (as_a
<pp_token_text
*> (tok_2
)->m_value
.get (),
3569 pp_token
*tok_3
= tok_2
->m_next
;
3570 ASSERT_NE (tok_3
, nullptr);
3571 ASSERT_EQ (tok_3
->m_prev
, tok_2
);
3572 ASSERT_EQ (tok_3
->m_kind
, pp_token::kind::custom_data
);
3573 custom_token_adder::value
*v3
3574 = static_cast <custom_token_adder::value
*>
3575 (as_a
<pp_token_custom_data
*> (tok_3
)->m_value
.get ());
3576 ASSERT_STREQ (v3
->m_adder
.m_name
, "bar");
3577 ASSERT_TRUE (v3
->m_adder
.m_num_living_values
> 0);
3579 pp_token
*tok_4
= tok_3
->m_next
;
3580 ASSERT_NE (tok_4
, nullptr);
3581 ASSERT_EQ (tok_4
->m_prev
, tok_3
);
3582 ASSERT_EQ (tok_4
->m_kind
, pp_token::kind::text
);
3583 ASSERT_STREQ (as_a
<pp_token_text
*> (tok_4
)->m_value
.get (),
3585 ASSERT_EQ (tok_4
->m_next
, nullptr);
3587 /* Normally we'd loop over the tokens, printing them to PP
3588 and handling the custom tokens.
3589 Instead, print a message to PP to verify that we were called. */
3590 pp_string (pp
, "print_tokens was called");
3594 custom_token_adder
e1 ("foo");
3595 custom_token_adder
e2 ("bar");
3596 ASSERT_EQ (e1
.m_num_living_values
, 0);
3597 ASSERT_EQ (e2
.m_num_living_values
, 0);
3599 custom_token_printer tp
;
3601 pp
.set_token_printer (&tp
);
3602 pp_printf (&pp
, "before %e middle %e after", &e1
, &e2
);
3604 /* Verify that instances were cleaned up. */
3605 ASSERT_EQ (e1
.m_num_living_values
, 0);
3606 ASSERT_EQ (e2
.m_num_living_values
, 0);
3608 ASSERT_STREQ (pp_formatted_text (&pp
),
3609 "print_tokens was called");
3612 /* Helper subroutine for test_pp_format_stack.
3613 Call pp_format (phases 1 and 2), without calling phase 3. */
3616 push_pp_format (pretty_printer
*pp
, const char *msg
, ...)
3621 rich_location
rich_loc (line_table
, UNKNOWN_LOCATION
);
3622 text_info
ti (msg
, &ap
, 0, nullptr, &rich_loc
);
3623 pp_format (pp
, &ti
);
3627 #define ASSERT_TEXT_TOKEN(TOKEN, EXPECTED_TEXT) \
3628 SELFTEST_BEGIN_STMT \
3629 ASSERT_NE ((TOKEN), nullptr); \
3630 ASSERT_EQ ((TOKEN)->m_kind, pp_token::kind::text); \
3632 (as_a <const pp_token_text *> (TOKEN)->m_value.get (), \
3637 /* Verify that the stack of pp_formatted_chunks works as expected. */
3640 test_pp_format_stack ()
3642 auto_fix_quotes fix_quotes
;
3645 push_pp_format (&pp
, "unexpected foo: %i bar: %qs", 42, "test");
3646 push_pp_format (&pp
, "In function: %qs", "test_fn");
3648 /* Expect the top of the stack to have:
3649 (gdb) call top->dump()
3650 0: [TEXT("In function: ")]
3651 1: [BEGIN_QUOTE, TEXT("test_fn"), END_QUOTE]. */
3653 pp_formatted_chunks
*top
= pp_buffer (&pp
)->m_cur_formatted_chunks
;
3654 ASSERT_NE (top
, nullptr);
3655 ASSERT_TEXT_TOKEN (top
->get_token_lists ()[0]->m_first
, "In function: ");
3656 ASSERT_EQ (top
->get_token_lists ()[1]->m_first
->m_kind
,
3657 pp_token::kind::begin_quote
);
3658 ASSERT_EQ (top
->get_token_lists ()[2], nullptr);
3660 /* Expect an entry in the stack below it with:
3661 0: [TEXT("unexpected foo: ")]
3664 3: [BEGIN_QUOTE, TEXT("test"), END_QUOTE]. */
3665 pp_formatted_chunks
*prev
= top
->get_prev ();
3666 ASSERT_NE (prev
, nullptr);
3667 ASSERT_TEXT_TOKEN (prev
->get_token_lists ()[0]->m_first
, "unexpected foo: ");
3668 ASSERT_TEXT_TOKEN (prev
->get_token_lists ()[1]->m_first
, "42");
3669 ASSERT_TEXT_TOKEN (prev
->get_token_lists ()[2]->m_first
, " bar: ");
3670 ASSERT_EQ (prev
->get_token_lists ()[3]->m_first
->m_kind
,
3671 pp_token::kind::begin_quote
);
3672 ASSERT_EQ (prev
->get_token_lists ()[4], nullptr);
3674 ASSERT_EQ (prev
->get_prev (), nullptr);
3676 /* Pop the top of the stack. */
3677 pp_output_formatted_text (&pp
);
3678 ASSERT_EQ (pp_buffer (&pp
)->m_cur_formatted_chunks
, prev
);
3681 /* Pop the remaining entry from the stack. */
3682 pp_output_formatted_text (&pp
);
3683 ASSERT_EQ (pp_buffer (&pp
)->m_cur_formatted_chunks
, nullptr);
3685 ASSERT_STREQ (pp_formatted_text (&pp
),
3686 "In function: `test_fn'\nunexpected foo: 42 bar: `test'");
3689 /* Verify usage of pp_printf from within a pp_element's
3690 add_to_phase_2 vfunc. */
3692 test_pp_printf_within_pp_element ()
3694 class kv_element
: public pp_element
3697 kv_element (const char *key
, int value
)
3698 : m_key (key
), m_value (value
)
3702 void add_to_phase_2 (pp_markup::context
&ctxt
) final override
3704 /* We can't call pp_printf directly on ctxt.m_pp from within
3705 formatting. As a workaround, work with a clone of the pp. */
3706 std::unique_ptr
<pretty_printer
> pp (ctxt
.m_pp
.clone ());
3707 pp_printf (pp
.get (), "(%qs: %qi)", m_key
, m_value
);
3708 pp_string (&ctxt
.m_pp
, pp_formatted_text (pp
.get ()));
3716 auto_fix_quotes fix_quotes
;
3718 kv_element
e1 ("foo", 42);
3719 kv_element
e2 ("bar", 1066);
3720 ASSERT_PP_FORMAT_2 ("before (`foo': `42') (`bar': `1066') after",
3721 "before %e %e after",
3723 assert_pp_format_colored (SELFTEST_LOCATION
,
3725 "(`\33[01m\33[Kfoo\33[m\33[K'"
3727 "`\33[01m\33[K42\33[m\33[K')"
3729 "(`\33[01m\33[Kbar\33[m\33[K'"
3731 "`\33[01m\33[K1066\33[m\33[K')"
3733 "before %e %e after",
3737 /* A subclass of pretty_printer for use by test_prefixes_and_wrapping. */
3739 class test_pretty_printer
: public pretty_printer
3742 test_pretty_printer (enum diagnostic_prefixing_rule_t rule
,
3743 int max_line_length
)
3745 pp_set_prefix (this, xstrdup ("PREFIX: "));
3746 pp_prefixing_rule (this) = rule
;
3747 pp_set_line_maximum_length (this, max_line_length
);
3751 /* Verify that the various values of enum diagnostic_prefixing_rule_t work
3752 as expected, with and without line wrapping. */
3755 test_prefixes_and_wrapping ()
3757 /* Tests of the various prefixing rules, without wrapping.
3758 Newlines embedded in pp_string don't affect it; we have to
3759 explicitly call pp_newline. */
3761 test_pretty_printer
pp (DIAGNOSTICS_SHOW_PREFIX_ONCE
, 0);
3762 pp_string (&pp
, "the quick brown fox");
3764 pp_string (&pp
, "jumps over the lazy dog");
3766 ASSERT_STREQ (pp_formatted_text (&pp
),
3767 "PREFIX: the quick brown fox\n"
3768 " jumps over the lazy dog\n");
3771 test_pretty_printer
pp (DIAGNOSTICS_SHOW_PREFIX_NEVER
, 0);
3772 pp_string (&pp
, "the quick brown fox");
3774 pp_string (&pp
, "jumps over the lazy dog");
3776 ASSERT_STREQ (pp_formatted_text (&pp
),
3777 "the quick brown fox\n"
3778 "jumps over the lazy dog\n");
3781 test_pretty_printer
pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
, 0);
3782 pp_string (&pp
, "the quick brown fox");
3784 pp_string (&pp
, "jumps over the lazy dog");
3786 ASSERT_STREQ (pp_formatted_text (&pp
),
3787 "PREFIX: the quick brown fox\n"
3788 "PREFIX: jumps over the lazy dog\n");
3791 /* Tests of the various prefixing rules, with wrapping. */
3793 test_pretty_printer
pp (DIAGNOSTICS_SHOW_PREFIX_ONCE
, 20);
3794 pp_string (&pp
, "the quick brown fox jumps over the lazy dog");
3796 pp_string (&pp
, "able was I ere I saw elba");
3798 ASSERT_STREQ (pp_formatted_text (&pp
),
3799 "PREFIX: the quick \n"
3800 " brown fox jumps \n"
3803 " able was I ere I \n"
3807 test_pretty_printer
pp (DIAGNOSTICS_SHOW_PREFIX_NEVER
, 20);
3808 pp_string (&pp
, "the quick brown fox jumps over the lazy dog");
3810 pp_string (&pp
, "able was I ere I saw elba");
3812 ASSERT_STREQ (pp_formatted_text (&pp
),
3813 "the quick brown fox \n"
3814 "jumps over the lazy \n"
3816 "able was I ere I \n"
3820 test_pretty_printer
pp (DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE
, 20);
3821 pp_string (&pp
, "the quick brown fox jumps over the lazy dog");
3823 pp_string (&pp
, "able was I ere I saw elba");
3825 ASSERT_STREQ (pp_formatted_text (&pp
),
3826 "PREFIX: the quick brown fox jumps over the lazy dog\n"
3827 "PREFIX: able was I ere I saw elba\n");
3832 /* Verify that URL-printing works as expected. */
3839 pp
.set_url_format (URL_FORMAT_NONE
);
3840 pp_begin_url (&pp
, "http://example.com");
3841 pp_string (&pp
, "This is a link");
3843 ASSERT_STREQ ("This is a link",
3844 pp_formatted_text (&pp
));
3849 pp
.set_url_format (URL_FORMAT_ST
);
3850 pp_begin_url (&pp
, "http://example.com");
3851 pp_string (&pp
, "This is a link");
3853 ASSERT_STREQ ("\33]8;;http://example.com\33\\This is a link\33]8;;\33\\",
3854 pp_formatted_text (&pp
));
3859 pp
.set_url_format (URL_FORMAT_BEL
);
3860 pp_begin_url (&pp
, "http://example.com");
3861 pp_string (&pp
, "This is a link");
3863 ASSERT_STREQ ("\33]8;;http://example.com\aThis is a link\33]8;;\a",
3864 pp_formatted_text (&pp
));
3869 test_urls_from_braces ()
3873 pp
.set_url_format (URL_FORMAT_NONE
);
3874 pp_printf (&pp
, "before %{text%} after",
3875 "http://example.com");
3876 ASSERT_STREQ ("before text after",
3877 pp_formatted_text (&pp
));
3882 pp
.set_url_format (URL_FORMAT_ST
);
3883 pp_printf (&pp
, "before %{text%} after",
3884 "http://example.com");
3885 ASSERT_STREQ ("before \33]8;;http://example.com\33\\text\33]8;;\33\\ after",
3886 pp_formatted_text (&pp
));
3891 pp
.set_url_format (URL_FORMAT_BEL
);
3892 pp_printf (&pp
, "before %{text%} after",
3893 "http://example.com");
3894 ASSERT_STREQ ("before \33]8;;http://example.com\atext\33]8;;\a after",
3895 pp_formatted_text (&pp
));
3899 /* Verify that we gracefully reject null URLs. */
3906 pp
.set_url_format (URL_FORMAT_NONE
);
3907 pp_begin_url (&pp
, nullptr);
3908 pp_string (&pp
, "This isn't a link");
3910 ASSERT_STREQ ("This isn't a link",
3911 pp_formatted_text (&pp
));
3916 pp
.set_url_format (URL_FORMAT_ST
);
3917 pp_begin_url (&pp
, nullptr);
3918 pp_string (&pp
, "This isn't a link");
3920 ASSERT_STREQ ("This isn't a link",
3921 pp_formatted_text (&pp
));
3926 pp
.set_url_format (URL_FORMAT_BEL
);
3927 pp_begin_url (&pp
, nullptr);
3928 pp_string (&pp
, "This isn't a link");
3930 ASSERT_STREQ ("This isn't a link",
3931 pp_formatted_text (&pp
));
3935 /* Verify that URLification works as expected. */
3938 pp_printf_with_urlifier (pretty_printer
*pp
,
3939 const urlifier
*urlifier
,
3940 const char *msg
, ...)
3945 text_info
text (msg
, &ap
, errno
);
3946 pp_format (pp
, &text
);
3947 pp_output_formatted_text (pp
, urlifier
);
3952 test_urlification ()
3954 class test_urlifier
: public urlifier
3958 get_url_for_quoted_text (const char *p
, size_t sz
) const final override
3960 if (!strncmp (p
, "-foption", sz
))
3961 return xstrdup ("http://example.com");
3966 auto_fix_quotes fix_quotes
;
3967 const test_urlifier urlifier
;
3969 /* Uses of "%<" and "%>". */
3973 pp
.set_url_format (URL_FORMAT_NONE
);
3974 pp_printf_with_urlifier (&pp
, &urlifier
,
3975 "foo %<-foption%> %<unrecognized%> bar");
3976 ASSERT_STREQ ("foo `-foption' `unrecognized' bar",
3977 pp_formatted_text (&pp
));
3981 pp
.set_url_format (URL_FORMAT_ST
);
3982 pp_printf_with_urlifier (&pp
, &urlifier
,
3983 "foo %<-foption%> %<unrecognized%> bar");
3985 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
3986 " `unrecognized' bar",
3987 pp_formatted_text (&pp
));
3991 pp
.set_url_format (URL_FORMAT_BEL
);
3992 pp_printf_with_urlifier (&pp
, &urlifier
,
3993 "foo %<-foption%> %<unrecognized%> bar");
3995 ("foo `\33]8;;http://example.com\a-foption\33]8;;\a'"
3996 " `unrecognized' bar",
3997 pp_formatted_text (&pp
));
4004 pp
.set_url_format (URL_FORMAT_ST
);
4005 pp_printf_with_urlifier (&pp
, &urlifier
,
4007 "-foption", "unrecognized");
4009 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\'"
4010 " `unrecognized' bar",
4011 pp_formatted_text (&pp
));
4014 /* Mixed usage of %< and %s, where the quoted string is built between
4015 a mixture of phase 1 and phase 2. */
4018 pp
.set_url_format (URL_FORMAT_ST
);
4019 pp_printf_with_urlifier (&pp
, &urlifier
,
4023 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4024 pp_formatted_text (&pp
));
4027 /* Likewise, where there is trailing phase 1 content within the
4031 pp
.set_url_format (URL_FORMAT_ST
);
4032 pp_printf_with_urlifier (&pp
, &urlifier
,
4033 "foo %<-f%sion%> bar %<-f%sion%> baz",
4036 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
4037 pp_formatted_text (&pp
));
4043 pp
.set_url_format (URL_FORMAT_ST
);
4044 pp_printf_with_urlifier (&pp
, &urlifier
,
4045 "foo %<%sption%> bar %<-f%sion%> baz",
4048 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' baz",
4049 pp_formatted_text (&pp
));
4052 /* Another mixed usage of %< and %s, where the quoted string is built
4053 between a mixture of phase 1 and multiple phase 2. */
4056 pp
.set_url_format (URL_FORMAT_ST
);
4057 pp_printf_with_urlifier (&pp
, &urlifier
,
4058 "foo %<-f%s%s%> bar",
4061 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4062 pp_formatted_text (&pp
));
4065 /* Mixed usage of %< and %s with a prefix. */
4068 pp
.set_url_format (URL_FORMAT_ST
);
4069 pp_set_prefix (&pp
, xstrdup ("PREFIX"));
4070 pp_printf_with_urlifier (&pp
, &urlifier
,
4074 ("PREFIXfoo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4075 pp_formatted_text (&pp
));
4078 /* Example of mixed %< and %s with numbered args. */
4081 pp
.set_url_format (URL_FORMAT_ST
);
4082 pp_printf_with_urlifier (&pp
, &urlifier
,
4083 "foo %<-f%2$st%1$sn%> bar",
4086 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4087 pp_formatted_text (&pp
));
4090 /* Example of %e. */
4093 pp
.set_url_format (URL_FORMAT_ST
);
4094 test_element
elem ("-foption");
4095 pp_printf_with_urlifier (&pp
, &urlifier
,
4099 ("foo `\33]8;;http://example.com\33\\-foption\33]8;;\33\\' bar",
4100 pp_formatted_text (&pp
));
4103 /* Test the example from pretty-print-format-impl.h. */
4106 pp
.set_url_format (URL_FORMAT_ST
);
4107 pp_printf_with_urlifier (&pp
, &urlifier
,
4108 "foo: %i, bar: %s, option: %qs",
4109 42, "baz", "-foption");
4110 ASSERT_STREQ (pp_formatted_text (&pp
),
4111 "foo: 42, bar: baz, option:"
4112 " `\e]8;;http://example.com\e\\-foption\e]8;;\e\\'");
4116 /* Test multibyte awareness. */
4117 static void test_utf8 ()
4120 /* Check that pp_quoted_string leaves valid UTF-8 alone. */
4123 const char *s
= "\xf0\x9f\x98\x82";
4124 pp_quoted_string (&pp
, s
);
4125 ASSERT_STREQ (pp_formatted_text (&pp
), s
);
4128 /* Check that pp_quoted_string escapes non-UTF-8 nonprintable bytes. */
4131 pp_quoted_string (&pp
, "\xf0!\x9f\x98\x82");
4132 ASSERT_STREQ (pp_formatted_text (&pp
),
4133 "\\xf0!\\x9f\\x98\\x82");
4136 /* Check that pp_character will line-wrap at the beginning of a UTF-8
4137 sequence, but not in the middle. */
4139 pretty_printer
pp (3);
4140 const char s
[] = "---\xf0\x9f\x98\x82";
4141 for (int i
= 0; i
!= sizeof (s
) - 1; ++i
)
4142 pp_character (&pp
, s
[i
]);
4144 for (int i
= 1; i
!= sizeof (s
) - 1; ++i
)
4145 pp_character (&pp
, s
[i
]);
4146 pp_character (&pp
, '-');
4147 ASSERT_STREQ (pp_formatted_text (&pp
),
4149 "\xf0\x9f\x98\x82\n"
4150 "--\xf0\x9f\x98\x82\n"
4156 /* Verify that class comma_separated_quoted_strings works as expected. */
4159 test_comma_separated_quoted_strings ()
4161 auto_fix_quotes fix_quotes
;
4163 auto_vec
<const char *> none
;
4164 pp_markup::comma_separated_quoted_strings
e_none (none
);
4166 auto_vec
<const char *> one
;
4167 one
.safe_push ("one");
4168 pp_markup::comma_separated_quoted_strings
e_one (one
);
4170 auto_vec
<const char *> many
;
4171 many
.safe_push ("0");
4172 many
.safe_push ("1");
4173 many
.safe_push ("2");
4174 pp_markup::comma_separated_quoted_strings
e_many (many
);
4176 ASSERT_PP_FORMAT_3 ("none: () one: (`one') many: (`0', `1', `2')",
4177 "none: (%e) one: (%e) many: (%e)",
4178 &e_none
, &e_one
, &e_many
);
4179 assert_pp_format_colored (SELFTEST_LOCATION
,
4180 "one: (`\e[01m\e[Kone\e[m\e[K')",
4185 /* Run all of the selftests within this file. */
4188 pretty_print_cc_tests ()
4190 test_basic_printing ();
4192 test_merge_consecutive_text_tokens ();
4193 test_custom_tokens_1 ();
4194 test_custom_tokens_2 ();
4195 test_pp_format_stack ();
4196 test_pp_printf_within_pp_element ();
4197 test_prefixes_and_wrapping ();
4199 test_urls_from_braces ();
4201 test_urlification ();
4203 test_comma_separated_quoted_strings ();
4206 } // namespace selftest
4208 #endif /* CHECKING_P */