Line data Source code
1 : /* Implementation detail of pp_format.
2 : Copyright (C) 2002-2026 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 "diagnostics/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 42812441 : pp_token_text (label_text &&value)
112 42812441 : : pp_token (kind::text),
113 42812441 : m_value (std::move (value))
114 : {
115 42812441 : gcc_assert (m_value.get ());
116 42812441 : }
117 :
118 : label_text m_value;
119 : };
120 :
121 : template <>
122 : template <>
123 : inline bool
124 30543235 : is_a_helper <pp_token_text *>::test (pp_token *tok)
125 : {
126 30543235 : return tok->m_kind == pp_token::kind::text;
127 : }
128 :
129 : template <>
130 : template <>
131 : inline bool
132 1912 : is_a_helper <const pp_token_text *>::test (const pp_token *tok)
133 : {
134 1912 : return tok->m_kind == pp_token::kind::text;
135 : }
136 :
137 : struct pp_token_begin_color : public pp_token
138 : {
139 25831 : pp_token_begin_color (label_text &&value)
140 25831 : : pp_token (kind::begin_color),
141 25831 : m_value (std::move (value))
142 : {
143 25831 : gcc_assert (m_value.get ());
144 25831 : }
145 :
146 : label_text m_value;
147 : };
148 :
149 : template <>
150 : template <>
151 : inline bool
152 25829 : is_a_helper <pp_token_begin_color *>::test (pp_token *tok)
153 : {
154 25829 : 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 25831 : pp_token_end_color ()
168 51662 : : pp_token (kind::end_color)
169 : {
170 : }
171 : };
172 :
173 : struct pp_token_begin_quote : public pp_token
174 : {
175 1106532 : pp_token_begin_quote ()
176 2213064 : : pp_token (kind::begin_quote)
177 : {
178 : }
179 : };
180 :
181 : struct pp_token_end_quote : public pp_token
182 : {
183 1080898 : pp_token_end_quote ()
184 2161796 : : pp_token (kind::end_quote)
185 : {
186 : }
187 : };
188 :
189 : struct pp_token_begin_url : public pp_token
190 : {
191 18894 : pp_token_begin_url (label_text &&value)
192 18894 : : pp_token (kind::begin_url),
193 18894 : m_value (std::move (value))
194 : {
195 18894 : gcc_assert (m_value.get ());
196 18894 : }
197 :
198 : label_text m_value;
199 : };
200 :
201 : template <>
202 : template <>
203 : inline bool
204 18894 : is_a_helper <pp_token_begin_url*>::test (pp_token *tok)
205 : {
206 18894 : 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 18894 : pp_token_end_url ()
220 37788 : : pp_token (kind::end_url)
221 : {
222 : }
223 : };
224 :
225 : struct pp_token_event_id : public pp_token
226 : {
227 23235 : pp_token_event_id (diagnostics::paths::event_id_t event_id)
228 23235 : : pp_token (kind::event_id),
229 23235 : m_event_id (event_id)
230 : {
231 23235 : gcc_assert (event_id.known_p ());
232 23235 : }
233 :
234 : diagnostics::paths::event_id_t m_event_id;
235 : };
236 :
237 : template <>
238 : template <>
239 : inline bool
240 23235 : is_a_helper <pp_token_event_id *>::test (pp_token *tok)
241 : {
242 23235 : 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 3734471 : 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 3734471 : pp_token_custom_data (std::unique_ptr<value> val)
269 3734471 : : pp_token (kind::custom_data),
270 3734471 : m_value (std::move (val))
271 : {
272 3734471 : gcc_assert (m_value.get ());
273 3734471 : }
274 :
275 : std::unique_ptr<value> m_value;
276 : };
277 :
278 : template <>
279 : template <>
280 : inline bool
281 7468934 : is_a_helper <pp_token_custom_data *>::test (pp_token *tok)
282 : {
283 7468934 : 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 49991621 : static pp_token_list *make (obstack &s)
304 : {
305 49991621 : 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 48847027 : make_token (Args&&... args)
325 : {
326 : return std::unique_ptr<pp_token>
327 48847027 : (new (m_obstack) Subclass (std::forward<Args> (args)...));
328 : }
329 :
330 : template<typename Subclass, typename... Args>
331 48809311 : void push_back (Args&&... args)
332 : {
333 48809311 : auto tok = make_token<Subclass> (std::forward<Args> (args)...);
334 48809311 : push_back (std::move (tok));
335 48809311 : }
336 : void push_back_text (label_text &&text);
337 : void push_back_byte (char ch);
338 : void push_back (std::unique_ptr<pp_token> tok);
339 : void push_back_list (pp_token_list &&list);
340 :
341 : std::unique_ptr<pp_token> pop_front ();
342 :
343 : std::unique_ptr<pp_token> remove_token (pp_token *tok);
344 :
345 : void insert_after (std::unique_ptr<pp_token> new_tok,
346 : pp_token *relative_tok);
347 :
348 : void replace_custom_tokens ();
349 : void merge_consecutive_text_tokens ();
350 : void apply_urlifier (const urlifier &urlifier);
351 :
352 : void dump (FILE *out) const;
353 0 : void DEBUG_FUNCTION dump () const { dump (stderr); }
354 :
355 : obstack &m_obstack;
356 :
357 : pp_token *m_first;
358 : pp_token *m_end;
359 : };
360 :
361 : /* The pp_formatted_chunks data structure forms a stack of the results from the
362 : first phase of formatting (pp_format) which have not yet been
363 : output (pp_output_formatted_text). A stack is necessary because
364 : the diagnostic starter may decide to generate its own output by way
365 : of the formatter. */
366 : class pp_formatted_chunks
367 : {
368 : friend class pretty_printer;
369 : friend class pp_markup::context;
370 : friend class output_buffer;
371 :
372 : public:
373 13411645 : pp_token_list * const * get_token_lists () const { return m_args; }
374 :
375 : void append_formatted_chunk (obstack &s, const char *content);
376 :
377 : void dump (FILE *out, int indent) const;
378 0 : void DEBUG_FUNCTION dump () const { dump (stderr, 0); }
379 :
380 : // For use in selftests
381 8 : pp_formatted_chunks *get_prev () const { return m_prev; }
382 :
383 : private:
384 : /* Pointer to previous level on the stack. */
385 : pp_formatted_chunks *m_prev;
386 :
387 : /* Array of chunks to output. Each chunk is a doubly-linked list of
388 : pp_token.
389 :
390 : The chunks can be printed via pp_formatted_chunks::dump ().
391 :
392 : In the first phase of formatting, even-numbered chunks are
393 : to be output verbatim, odd-numbered chunks are format specifiers.
394 : For example, given:
395 : pp_format (pp,
396 : "foo: %i, bar: %s, opt: %qs",
397 : 42, "baz", "-foption");
398 :
399 : after phase 1 we might have:
400 : (gdb) call buffer->cur_chunk_array->dump()
401 : 0: [TEXT("foo: ")]
402 : 1: [TEXT("i")]
403 : 2: [TEXT(", bar: ")]
404 : 3: [TEXT("s")]
405 : 4: [TEXT(", opt: ")]
406 : 5: [TEXT("qs")]
407 :
408 : The second phase replaces all odd-numbered chunks with formatted
409 : token lists. In the above example, after phase 2 we might have:
410 : (gdb) call pp->m_buffer->cur_chunk_array->dump()
411 : 0: [TEXT("foo: ")]
412 : 1: [TEXT("42")]
413 : 2: [TEXT(", bar: ")]
414 : 3: [TEXT("baz")]
415 : 4: [TEXT(", opt: ")]
416 : 5: [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
417 : For example the %qs has become the three tokens:
418 : [BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
419 :
420 : The third phase (in pp_output_formatted_text):
421 :
422 : (1) merges the tokens from all the chunks into one list,
423 : giving e.g.
424 : (gdb) call tokens.dump()
425 : [TEXT("foo: "), TEXT("42"), TEXT(", bar: "), TEXT("baz"),
426 : TEXT(", opt: "), BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
427 :
428 : (2) lowers some custom tokens into non-custom tokens
429 :
430 : (3) merges consecutive text tokens, giving e.g.:
431 : (gdb) call tokens.dump()
432 : [TEXT("foo: 42, bar: baz, option: "),
433 : BEGIN_QUOTE, TEXT("-foption"), END_QUOTE]
434 :
435 : (4) if provided with a urlifier, tries to apply it to quoted text,
436 : giving e.g:
437 : (gdb) call tokens.dump()
438 : [TEXT("foo: 42, bar: baz, option: "), BEGIN_QUOTE,
439 : BEGIN_URL("http://example.com"), TEXT("-foption"), END_URL, END_QUOTE]
440 :
441 : (5) emits all tokens in sequence with appropriate line-wrapping. This
442 : can be overridded via the pretty_printer's token_printer, allowing for
443 : output formats to e.g. override how URLs are handled, or to handle
444 : custom_data that wasn't lowered in (2) above, e.g. for handling JSON
445 : output of optimization records. */
446 : pp_token_list *m_args[PP_NL_ARGMAX * 2];
447 :
448 : /* The pp_tokens, pp_token_lists, and the accumulated text buffers are
449 : allocated within the output_buffer's chunk_obstack. In the above
450 : example, the in-memory layout of the chunk_obstack might look like
451 : this after phase 1:
452 :
453 : + pp_formatted_chunks instance <--- START of pp_formatted_chunks level
454 : |
455 : + pp_token_list for chunk 0 (m_first: *)
456 : | |
457 : + "foo: \0" <-------------\ |
458 : | | |
459 : + pp_token_text (borrowed: *) <-------/
460 : |
461 : + pp_token_list for chunk 1
462 : |
463 : + "i\0" <------------------\
464 : | |
465 : + pp_token_text (borrowed: *)
466 : |
467 : + ...etc for chunks 2 to 4...
468 : |
469 : + pp_token_list for chunk 5
470 : |
471 : + "qs\0" <-----------------\
472 : | |
473 : + pp_token_text (borrowed: *)
474 : |
475 : |
476 : V
477 : obstack grows this way
478 :
479 : At each stage, allocation of additional text buffers, tokens, and lists
480 : grow forwards in the obstack (though the internal pointers in linked
481 : lists might point backwards to earlier objects within the same
482 : pp_formatted_chunks level). */
483 : };
484 :
485 : #endif /* GCC_PRETTY_PRINT_FORMAT_IMPL_H */
|