Branch data Line data Source code
1 : : /* Implementation detail of pp_format.
2 : : Copyright (C) 2002-2025 Free Software Foundation, Inc.
3 : : Contributed by Gabriel Dos Reis <gdr@integrable-solutions.net>
4 : :
5 : : This file is part of GCC.
6 : :
7 : : GCC is free software; you can redistribute it and/or modify it under
8 : : the terms of the GNU General Public License as published by the Free
9 : : Software Foundation; either version 3, or (at your option) any later
10 : : version.
11 : :
12 : : GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13 : : WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 : : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 : : for more details.
16 : :
17 : : You should have received a copy of the GNU General Public License
18 : : along with GCC; see the file COPYING3. If not see
19 : : <http://www.gnu.org/licenses/>. */
20 : :
21 : : #ifndef GCC_PRETTY_PRINT_FORMAT_IMPL_H
22 : : #define GCC_PRETTY_PRINT_FORMAT_IMPL_H
23 : :
24 : : #include "pretty-print.h"
25 : : #include "diagnostic-event-id.h"
26 : :
27 : : /* A struct representing a pending item to be printed within
28 : : pp_format.
29 : :
30 : : These can represent:
31 : : - a run of text within one of the output_buffers's obstacks
32 : : - begin/end named color
33 : : - open/close quote
34 : : - begin/end URL
35 : : - event IDs
36 : : - custom data (for the formatter, for the pretty_printer,
37 : : or the output format)
38 : :
39 : : These are built into pp_token_list instances.
40 : :
41 : : Doing so allows for interaction between:
42 : :
43 : : - pretty_printer formatting codes (such as C++'s %H and %I,
44 : : which can't be printed until we've seen both)
45 : :
46 : : - output formats, such as text vs SARIF (so each can handle URLs
47 : : and event IDs it its own way)
48 : :
49 : : - optimization records, where we want to stash data into the
50 : : formatted messages
51 : :
52 : : - urlifiers: these can be run in phase 3 of formatting
53 : :
54 : : without needing lots of fragile logic on char pointers.
55 : :
56 : : To avoid needing lots of heap allocation/deallocation, pp_token
57 : : instances are allocated in the pretty_printer's chunk_obstack:
58 : : they must not outlive phase 3 of formatting of the given
59 : : pp_formatted_chunks level. */
60 : :
61 : : struct pp_token
62 : : {
63 : : public:
64 : : enum class kind
65 : : {
66 : : text,
67 : :
68 : : begin_color,
69 : : end_color,
70 : :
71 : : begin_quote,
72 : : end_quote,
73 : :
74 : : begin_url,
75 : : end_url,
76 : :
77 : : event_id,
78 : :
79 : : custom_data,
80 : :
81 : : NUM_KINDS
82 : : };
83 : :
84 : : pp_token (enum kind k);
85 : :
86 : : pp_token (const pp_token &) = delete;
87 : : pp_token (pp_token &&) = delete;
88 : :
89 : 0 : virtual ~pp_token () = default;
90 : :
91 : : pp_token &operator= (const pp_token &) = delete;
92 : : pp_token &operator= (pp_token &&) = delete;
93 : :
94 : : void dump (FILE *out) const;
95 : 0 : void DEBUG_FUNCTION dump () const { dump (stderr); }
96 : :
97 : : static void *operator new (size_t sz, obstack &s);
98 : : static void operator delete (void *);
99 : :
100 : : enum kind m_kind;
101 : :
102 : : // Intrusive doubly-linked list
103 : : pp_token *m_prev;
104 : : pp_token *m_next;
105 : : };
106 : :
107 : : /* Subclasses of pp_token for the various kinds of token. */
108 : :
109 : : struct pp_token_text : public pp_token
110 : : {
111 : 46625924 : pp_token_text (label_text &&value)
112 : 46625924 : : pp_token (kind::text),
113 : 46625924 : m_value (std::move (value))
114 : : {
115 : 46625924 : gcc_assert (m_value.get ());
116 : 46625924 : }
117 : :
118 : : label_text m_value;
119 : : };
120 : :
121 : : template <>
122 : : template <>
123 : : inline bool
124 : 33603533 : is_a_helper <pp_token_text *>::test (pp_token *tok)
125 : : {
126 : 33603533 : return tok->m_kind == pp_token::kind::text;
127 : : }
128 : :
129 : : template <>
130 : : template <>
131 : : inline bool
132 : 1302 : is_a_helper <const pp_token_text *>::test (const pp_token *tok)
133 : : {
134 : 1302 : return tok->m_kind == pp_token::kind::text;
135 : : }
136 : :
137 : : struct pp_token_begin_color : public pp_token
138 : : {
139 : 22763 : pp_token_begin_color (label_text &&value)
140 : 22763 : : pp_token (kind::begin_color),
141 : 22763 : m_value (std::move (value))
142 : : {
143 : 22763 : gcc_assert (m_value.get ());
144 : 22763 : }
145 : :
146 : : label_text m_value;
147 : : };
148 : :
149 : : template <>
150 : : template <>
151 : : inline bool
152 : 22761 : is_a_helper <pp_token_begin_color *>::test (pp_token *tok)
153 : : {
154 : 22761 : return tok->m_kind == pp_token::kind::begin_color;
155 : : }
156 : :
157 : : template <>
158 : : template <>
159 : : inline bool
160 : 0 : is_a_helper <const pp_token_begin_color *>::test (const pp_token *tok)
161 : : {
162 : 0 : return tok->m_kind == pp_token::kind::begin_color;
163 : : }
164 : :
165 : : struct pp_token_end_color : public pp_token
166 : : {
167 : 22763 : pp_token_end_color ()
168 : 45526 : : pp_token (kind::end_color)
169 : : {
170 : : }
171 : : };
172 : :
173 : : struct pp_token_begin_quote : public pp_token
174 : : {
175 : 1061318 : pp_token_begin_quote ()
176 : 2122636 : : pp_token (kind::begin_quote)
177 : : {
178 : : }
179 : : };
180 : :
181 : : struct pp_token_end_quote : public pp_token
182 : : {
183 : 1036634 : pp_token_end_quote ()
184 : 2073268 : : pp_token (kind::end_quote)
185 : : {
186 : : }
187 : : };
188 : :
189 : : struct pp_token_begin_url : public pp_token
190 : : {
191 : 18333 : pp_token_begin_url (label_text &&value)
192 : 18333 : : pp_token (kind::begin_url),
193 : 18333 : m_value (std::move (value))
194 : : {
195 : 18333 : gcc_assert (m_value.get ());
196 : 18333 : }
197 : :
198 : : label_text m_value;
199 : : };
200 : :
201 : : template <>
202 : : template <>
203 : : inline bool
204 : 18333 : is_a_helper <pp_token_begin_url*>::test (pp_token *tok)
205 : : {
206 : 18333 : return tok->m_kind == pp_token::kind::begin_url;
207 : : }
208 : :
209 : : template <>
210 : : template <>
211 : : inline bool
212 : 0 : is_a_helper <const pp_token_begin_url*>::test (const pp_token *tok)
213 : : {
214 : 0 : return tok->m_kind == pp_token::kind::begin_url;
215 : : }
216 : :
217 : : struct pp_token_end_url : public pp_token
218 : : {
219 : 18333 : pp_token_end_url ()
220 : 36666 : : pp_token (kind::end_url)
221 : : {
222 : : }
223 : : };
224 : :
225 : : struct pp_token_event_id : public pp_token
226 : : {
227 : 22993 : pp_token_event_id (diagnostic_event_id_t event_id)
228 : 22993 : : pp_token (kind::event_id),
229 : 22993 : m_event_id (event_id)
230 : : {
231 : 22993 : gcc_assert (event_id.known_p ());
232 : 22993 : }
233 : :
234 : : diagnostic_event_id_t m_event_id;
235 : : };
236 : :
237 : : template <>
238 : : template <>
239 : : inline bool
240 : 22993 : is_a_helper <pp_token_event_id *>::test (pp_token *tok)
241 : : {
242 : 22993 : return tok->m_kind == pp_token::kind::event_id;
243 : : }
244 : :
245 : : template <>
246 : : template <>
247 : : inline bool
248 : 0 : is_a_helper <const pp_token_event_id *>::test (const pp_token *tok)
249 : : {
250 : 0 : return tok->m_kind == pp_token::kind::event_id;
251 : : }
252 : :
253 : : struct pp_token_custom_data : public pp_token
254 : : {
255 : 4740927 : class value
256 : : {
257 : : public:
258 : 16 : virtual ~value () {}
259 : : virtual void dump (FILE *out) const = 0;
260 : :
261 : : /* Hook for lowering a custom_data token to standard tokens.
262 : : Return true and write to OUT if possible.
263 : : Return false for custom_data that is to be handled by
264 : : the token_printer. */
265 : : virtual bool as_standard_tokens (pp_token_list &out) = 0;
266 : : };
267 : :
268 : 4740927 : pp_token_custom_data (std::unique_ptr<value> val)
269 : 4740927 : : pp_token (kind::custom_data),
270 : 4740927 : m_value (std::move (val))
271 : : {
272 : 4740927 : gcc_assert (m_value.get ());
273 : 4740927 : }
274 : :
275 : : std::unique_ptr<value> m_value;
276 : : };
277 : :
278 : : template <>
279 : : template <>
280 : : inline bool
281 : 9481846 : is_a_helper <pp_token_custom_data *>::test (pp_token *tok)
282 : : {
283 : 9481846 : return tok->m_kind == pp_token::kind::custom_data;
284 : : }
285 : :
286 : : template <>
287 : : template <>
288 : : inline bool
289 : 0 : is_a_helper <const pp_token_custom_data *>::test (const pp_token *tok)
290 : : {
291 : 0 : return tok->m_kind == pp_token::kind::custom_data;
292 : : }
293 : :
294 : : /* A list of pp_token, with ownership of the tokens, using
295 : : a particular obstack to allocate its tokens. These are
296 : : also allocated on the obstack during formatting (or, occasionally,
297 : : the stack). */
298 : :
299 : : class pp_token_list
300 : : {
301 : : public:
302 : : // Allocate a new pp_token_list within S.
303 : 54114845 : static pp_token_list *make (obstack &s)
304 : : {
305 : 54114845 : return new (s) pp_token_list (s);
306 : : }
307 : : static void *operator new (size_t sz, obstack &s);
308 : : static void operator delete (void *);
309 : :
310 : : pp_token_list (obstack &s);
311 : : pp_token_list (const pp_token_list &) = delete;
312 : : pp_token_list (pp_token_list &&);
313 : :
314 : : ~pp_token_list ();
315 : :
316 : : pp_token &operator= (const pp_token_list &) = delete;
317 : : pp_token &operator= (pp_token_list &&) = delete;
318 : :
319 : : /* Make a pp_token of the given subclass, using the relevant obstack to provide
320 : : the memory. The pp_token must therefore not outlive the current
321 : : pp_formatted_chunks level during formatting. */
322 : : template<typename Subclass, typename... Args>
323 : : std::unique_ptr<pp_token>
324 : 53569988 : make_token (Args&&... args)
325 : : {
326 : : return std::unique_ptr<pp_token>
327 : 53569988 : (new (m_obstack) Subclass (std::forward<Args> (args)...));
328 : : }
329 : :
330 : : template<typename Subclass, typename... Args>
331 : 53533394 : void push_back (Args&&... args)
332 : : {
333 : 53533394 : auto tok = make_token<Subclass> (std::forward<Args> (args)...);
334 : 53533394 : push_back (std::move (tok));
335 : 53533394 : }
336 : : void push_back_text (label_text &&text);
337 : : void push_back (std::unique_ptr<pp_token> tok);
338 : : void push_back_list (pp_token_list &&list);
339 : :
340 : : std::unique_ptr<pp_token> pop_front ();
341 : :
342 : : std::unique_ptr<pp_token> remove_token (pp_token *tok);
343 : :
344 : : void insert_after (std::unique_ptr<pp_token> new_tok,
345 : : pp_token *relative_tok);
346 : :
347 : : void replace_custom_tokens ();
348 : : void merge_consecutive_text_tokens ();
349 : : void apply_urlifier (const urlifier &urlifier);
350 : :
351 : : void dump (FILE *out) const;
352 : 0 : void DEBUG_FUNCTION dump () const { dump (stderr); }
353 : :
354 : : obstack &m_obstack;
355 : :
356 : : pp_token *m_first;
357 : : pp_token *m_end;
358 : : };
359 : :
360 : : /* The pp_formatted_chunks data structure forms a stack of the results from the
361 : : first phase of formatting (pp_format) which have not yet been
362 : : output (pp_output_formatted_text). A stack is necessary because
363 : : the diagnostic starter may decide to generate its own output by way
364 : : of the formatter. */
365 : : class pp_formatted_chunks
366 : : {
367 : : friend class pretty_printer;
368 : : friend class pp_markup::context;
369 : : friend class output_buffer;
370 : :
371 : : public:
372 : 14974731 : pp_token_list * const * get_token_lists () const { return m_args; }
373 : :
374 : : void append_formatted_chunk (obstack &s, const char *content);
375 : :
376 : : void dump (FILE *out, int indent) const;
377 : 0 : void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
378 : :
379 : : // For use in selftests
380 : 8 : pp_formatted_chunks *get_prev () const { return m_prev; }
381 : :
382 : : private:
383 : : /* Pointer to previous level on the stack. */
384 : : pp_formatted_chunks *m_prev;
385 : :
386 : : /* Array of chunks to output. Each chunk is a doubly-linked list of
387 : : pp_token.
388 : :
389 : : The chunks can be printed via pp_formatted_chunks::dump ().
390 : :
391 : : In the first phase of formatting, even-numbered chunks are
392 : : to be output verbatim, odd-numbered chunks are format specifiers.
393 : : For example, given:
394 : : pp_format (pp,
395 : : "foo: %i, bar: %s, opt: %qs",
396 : : 42, "baz", "-foption");
397 : :
398 : : after phase 1 we might have:
399 : : (gdb) call buffer->cur_chunk_array->dump()
400 : : 0: [TEXT("foo: ")]
401 : : 1: [TEXT("i")]
402 : : 2: [TEXT(", bar: ")]
403 : : 3: [TEXT("s")]
404 : : 4: [TEXT(", opt: ")]
405 : : 5: [TEXT("qs")]
406 : :
407 : : The second phase replaces all odd-numbered chunks with formatted
408 : : token lists. In the above example, after phase 2 we might have:
409 : : (gdb) call pp->m_buffer->cur_chunk_array->dump()
410 : : 0: [TEXT("foo: ")]
411 : : 1: [TEXT("42")]
412 : : 2: [TEXT(", bar: ")]
413 : : 3: [TEXT("baz")]
414 : : 4: [TEXT(", opt: ")]
415 : : 5: [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
416 : : For example the %qs has become the three tokens:
417 : : [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
418 : :
419 : : The third phase (in pp_output_formatted_text):
420 : :
421 : : (1) merges the tokens from all the chunks into one list,
422 : : giving e.g.
423 : : (gdb) call tokens.dump()
424 : : [TEXT("foo: "), TEXT("42"), TEXT(", bar: "), TEXT("baz"),
425 : : TEXT(", opt: "), BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
426 : :
427 : : (2) lowers some custom tokens into non-custom tokens
428 : :
429 : : (3) merges consecutive text tokens, giving e.g.:
430 : : (gdb) call tokens.dump()
431 : : [TEXT("foo: 42, bar: baz, option: "),
432 : : BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
433 : :
434 : : (4) if provided with a urlifier, tries to apply it to quoted text,
435 : : giving e.g:
436 : : (gdb) call tokens.dump()
437 : : [TEXT("foo: 42, bar: baz, option: "), BEGIN_QUOTE,
438 : : BEGIN_URL("http://example.com"), TEXT("-foption"), END_URL, END_QUOTE]
439 : :
440 : : (5) emits all tokens in sequence with appropriate line-wrapping. This
441 : : can be overridded via the pretty_printer's token_printer, allowing for
442 : : output formats to e.g. override how URLs are handled, or to handle
443 : : custom_data that wasn't lowered in (2) above, e.g. for handling JSON
444 : : output of optimization records. */
445 : : pp_token_list *m_args[PP_NL_ARGMAX * 2];
446 : :
447 : : /* The pp_tokens, pp_token_lists, and the accumulated text buffers are
448 : : allocated within the output_buffer's chunk_obstack. In the above
449 : : example, the in-memory layout of the chunk_obstack might look like
450 : : this after phase 1:
451 : :
452 : : + pp_formatted_chunks instance <--- START of pp_formatted_chunks level
453 : : |
454 : : + pp_token_list for chunk 0 (m_first: *)
455 : : | |
456 : : + "foo: \0" <-------------\ |
457 : : | | |
458 : : + pp_token_text (borrowed: *) <-------/
459 : : |
460 : : + pp_token_list for chunk 1
461 : : |
462 : : + "i\0" <------------------\
463 : : | |
464 : : + pp_token_text (borrowed: *)
465 : : |
466 : : + ...etc for chunks 2 to 4...
467 : : |
468 : : + pp_token_list for chunk 5
469 : : |
470 : : + "qs\0" <-----------------\
471 : : | |
472 : : + pp_token_text (borrowed: *)
473 : : |
474 : : |
475 : : V
476 : : obstack grows this way
477 : :
478 : : At each stage, allocation of additional text buffers, tokens, and lists
479 : : grow forwards in the obstack (though the internal pointers in linked
480 : : lists might point backwards to earlier objects within the same
481 : : pp_formatted_chunks level). */
482 : : };
483 : :
484 : : #endif /* GCC_PRETTY_PRINT_FORMAT_IMPL_H */
|