Line data Source code
1 : /* Capturing the results of pretty_print for later playback.
2 : Copyright (C) 2023-2026 Free Software Foundation, Inc.
3 : Contributed by David Malcolm <dmalcolm@redhat.com>.
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 : #include "config.h"
22 : #define INCLUDE_STRING
23 : #include "system.h"
24 : #include "coretypes.h"
25 : #include "intl.h"
26 : #include "pretty-print.h"
27 : #include "pretty-print-token-buffer.h"
28 : #include "selftest.h"
29 :
30 : /* A token_printer that makes a deep copy of the pp_token_list
31 : into another obstack. */
32 :
33 56 : class copying_token_printer : public token_printer
34 : {
35 : public:
36 56 : copying_token_printer (obstack &dst_obstack,
37 : pp_token_list &dst_token_list)
38 56 : : m_dst_obstack (dst_obstack),
39 56 : m_dst_token_list (dst_token_list)
40 : {
41 : }
42 :
43 : void
44 56 : print_tokens (pretty_printer *,
45 : const pp_token_list &tokens) final override
46 : {
47 488 : for (auto iter = tokens.m_first; iter; iter = iter->m_next)
48 432 : switch (iter->m_kind)
49 : {
50 0 : default:
51 0 : gcc_unreachable ();
52 :
53 208 : case pp_token::kind::text:
54 208 : {
55 208 : const pp_token_text *sub = as_a <const pp_token_text *> (iter);
56 : /* Copy the text, with null terminator. */
57 208 : obstack_grow (&m_dst_obstack, sub->m_value.get (),
58 : strlen (sub->m_value.get ()) + 1);
59 208 : m_dst_token_list.push_back_text
60 208 : (label_text::borrow (XOBFINISH (&m_dst_obstack,
61 : const char *)));
62 : }
63 208 : break;
64 :
65 0 : case pp_token::kind::begin_color:
66 0 : {
67 0 : pp_token_begin_color *sub = as_a <pp_token_begin_color *> (iter);
68 : /* Copy the color, with null terminator. */
69 0 : obstack_grow (&m_dst_obstack, sub->m_value.get (),
70 : strlen (sub->m_value.get ()) + 1);
71 0 : m_dst_token_list.push_back<pp_token_begin_color>
72 0 : (label_text::borrow (XOBFINISH (&m_dst_obstack,
73 : const char *)));
74 : }
75 0 : break;
76 0 : case pp_token::kind::end_color:
77 0 : m_dst_token_list.push_back<pp_token_end_color> ();
78 0 : break;
79 :
80 112 : case pp_token::kind::begin_quote:
81 112 : m_dst_token_list.push_back<pp_token_begin_quote> ();
82 112 : break;
83 112 : case pp_token::kind::end_quote:
84 112 : m_dst_token_list.push_back<pp_token_end_quote> ();
85 112 : break;
86 :
87 0 : case pp_token::kind::begin_url:
88 0 : {
89 0 : pp_token_begin_url *sub = as_a <pp_token_begin_url *> (iter);
90 : /* Copy the URL, with null terminator. */
91 0 : obstack_grow (&m_dst_obstack, sub->m_value.get (),
92 : strlen (sub->m_value.get ()) + 1);
93 0 : m_dst_token_list.push_back<pp_token_begin_url>
94 0 : (label_text::borrow (XOBFINISH (&m_dst_obstack,
95 : const char *)));
96 : }
97 0 : break;
98 0 : case pp_token::kind::end_url:
99 0 : m_dst_token_list.push_back<pp_token_end_url> ();
100 0 : break;
101 :
102 0 : case pp_token::kind::event_id:
103 0 : {
104 0 : pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
105 0 : m_dst_token_list.push_back<pp_token_event_id> (sub->m_event_id);
106 : }
107 0 : break;
108 :
109 0 : case pp_token::kind::custom_data:
110 : /* These should have been eliminated by replace_custom_tokens. */
111 0 : gcc_unreachable ();
112 432 : break;
113 : }
114 56 : }
115 :
116 : private:
117 : obstack &m_dst_obstack;
118 : pp_token_list &m_dst_token_list;
119 : };
120 :
121 4 : pretty_print_token_buffer::pretty_print_token_buffer ()
122 4 : : m_obstack (std::make_unique<auto_obstack> ()),
123 4 : m_tokens (*m_obstack.get ())
124 : {
125 4 : }
126 :
127 : /* Capture GMSGID and ARGS as a sequence of pretty_print tokens. */
128 :
129 56 : pretty_print_token_buffer::pretty_print_token_buffer (const char *gmsgid,
130 56 : va_list *args)
131 56 : : m_obstack (std::make_unique<auto_obstack> ()),
132 56 : m_tokens (*m_obstack.get ())
133 : {
134 56 : text_info text (gmsgid, args, errno);
135 56 : pretty_printer pp;
136 56 : pp.set_output_stream (nullptr);
137 56 : copying_token_printer tok_printer (*m_obstack.get (), m_tokens);
138 56 : pp.set_token_printer (&tok_printer);
139 56 : pp_format (&pp, &text);
140 56 : pp_output_formatted_text (&pp, nullptr);
141 56 : }
142 :
143 104 : pretty_print_token_buffer::
144 104 : pretty_print_token_buffer (pretty_print_token_buffer &&other)
145 104 : : m_obstack (std::move (other.m_obstack)),
146 104 : m_tokens (std::move (other.m_tokens))
147 : {
148 104 : }
149 :
150 : /* Convert to text, dropping colorization, URLs, etc. */
151 :
152 : std::string
153 52 : pretty_print_token_buffer::to_string () const
154 : {
155 52 : std::string result;
156 :
157 452 : for (auto iter = m_tokens.m_first; iter; iter = iter->m_next)
158 400 : switch (iter->m_kind)
159 : {
160 0 : default:
161 0 : gcc_unreachable ();
162 :
163 192 : case pp_token::kind::text:
164 192 : {
165 192 : pp_token_text *sub = as_a <pp_token_text *> (iter);
166 192 : result += sub->m_value.get ();
167 : }
168 192 : break;
169 :
170 : case pp_token::kind::begin_color:
171 : case pp_token::kind::end_color:
172 : // Skip
173 : break;
174 :
175 104 : case pp_token::kind::begin_quote:
176 104 : result += open_quote;
177 104 : break;
178 :
179 104 : case pp_token::kind::end_quote:
180 104 : result += close_quote;
181 104 : break;
182 :
183 : case pp_token::kind::begin_url:
184 : case pp_token::kind::end_url:
185 : // Skip
186 : break;
187 :
188 0 : case pp_token::kind::event_id:
189 0 : {
190 0 : pp_token_event_id *sub = as_a <pp_token_event_id *> (iter);
191 0 : gcc_assert (sub->m_event_id.known_p ());
192 0 : result += '(';
193 0 : result += std::to_string (sub->m_event_id.one_based ());
194 0 : result += ')';
195 : }
196 0 : break;
197 :
198 0 : case pp_token::kind::custom_data:
199 : /* We don't have a way of handling custom_data tokens here. */
200 0 : gcc_unreachable ();
201 400 : break;
202 : }
203 :
204 52 : return result;
205 : }
206 :
207 : // class pp_token_buffer_element : public pp_element
208 :
209 : void
210 8 : pp_token_buffer_element::add_to_phase_2 (pp_markup::context &ctxt)
211 : {
212 40 : for (auto iter = m_token_buf.m_tokens.m_first; iter; iter = iter->m_next)
213 32 : switch (iter->m_kind)
214 : {
215 0 : default:
216 0 : gcc_unreachable ();
217 :
218 16 : case pp_token::kind::text:
219 16 : {
220 16 : const pp_token_text *sub = as_a <const pp_token_text *> (iter);
221 16 : pp_string (&ctxt.m_pp, sub->m_value.get ());
222 16 : ctxt.push_back_any_text ();
223 : }
224 16 : break;
225 :
226 0 : case pp_token::kind::begin_color:
227 0 : {
228 0 : const pp_token_begin_color *sub
229 0 : = as_a <const pp_token_begin_color *> (iter);
230 0 : gcc_assert (sub->m_value.get ());
231 0 : ctxt.begin_highlight_color (sub->m_value.get ());
232 : }
233 0 : break;
234 :
235 0 : case pp_token::kind::end_color:
236 0 : ctxt.end_highlight_color ();
237 0 : break;
238 :
239 8 : case pp_token::kind::begin_quote:
240 8 : ctxt.begin_quote ();
241 8 : break;
242 :
243 8 : case pp_token::kind::end_quote:
244 8 : ctxt.end_quote ();
245 8 : break;
246 :
247 0 : case pp_token::kind::begin_url:
248 0 : {
249 0 : const pp_token_begin_url *sub
250 0 : = as_a <const pp_token_begin_url *> (iter);
251 0 : gcc_assert (sub->m_value.get ());
252 0 : ctxt.begin_url (sub->m_value.get ());
253 : }
254 0 : break;
255 :
256 0 : case pp_token::kind::end_url:
257 0 : ctxt.end_url ();
258 0 : break;
259 :
260 0 : case pp_token::kind::event_id:
261 0 : {
262 0 : const pp_token_event_id *sub
263 0 : = as_a <const pp_token_event_id *> (iter);
264 0 : gcc_assert (sub->m_event_id.known_p ());
265 0 : ctxt.add_event_id (sub->m_event_id);
266 : }
267 0 : break;
268 :
269 0 : case pp_token::kind::custom_data:
270 : /* We don't have a way of handling custom_data tokens here. */
271 0 : gcc_unreachable ();
272 32 : break;
273 : }
274 8 : }
275 :
276 : #if CHECKING_P
277 :
278 : namespace selftest {
279 :
280 : static pretty_print_token_buffer
281 4 : pp_printf_to_buf (const char *fmt, ...)
282 : {
283 4 : va_list args;
284 4 : va_start (args, fmt);
285 :
286 4 : pretty_print_token_buffer buf (fmt, &args);
287 :
288 4 : va_end (args);
289 :
290 4 : return buf;
291 : }
292 :
293 : static void
294 4 : test_empty ()
295 : {
296 4 : pretty_print_token_buffer buf;
297 4 : pp_token_buffer_element e (buf);
298 4 : pretty_printer pp;
299 4 : pp_printf (&pp, "before %e after", &e);
300 4 : ASSERT_STREQ (pp_formatted_text (&pp), "before after");
301 4 : }
302 :
303 : static void
304 4 : test_print ()
305 : {
306 4 : pretty_print_token_buffer buf
307 4 : = pp_printf_to_buf ("x: %qs y: %qs", "foo", "bar");
308 :
309 : // Check that the individual tokens are captured in "buf".
310 4 : pp_token *tok0 = buf.m_tokens.m_first;
311 4 : ASSERT_EQ (tok0->m_kind, pp_token::kind::text);
312 4 : ASSERT_STREQ (((pp_token_text *)tok0)->m_value.get (),
313 : "x: ");
314 :
315 4 : pp_token *tok1 = tok0->m_next;
316 4 : ASSERT_EQ (tok1->m_kind, pp_token::kind::begin_quote);
317 :
318 4 : pp_token *tok2 = tok1->m_next;
319 4 : ASSERT_EQ (tok2->m_kind, pp_token::kind::text);
320 4 : ASSERT_STREQ (((pp_token_text *)tok2)->m_value.get (),
321 : "foo");
322 :
323 4 : pp_token *tok3 = tok2->m_next;
324 4 : ASSERT_EQ (tok3->m_kind, pp_token::kind::end_quote);
325 :
326 4 : pp_token *tok4 = tok3->m_next;
327 4 : ASSERT_EQ (tok4->m_kind, pp_token::kind::text);
328 4 : ASSERT_STREQ (((pp_token_text *)tok4)->m_value.get (),
329 : " y: ");
330 :
331 4 : pp_token *tok5 = tok4->m_next;
332 4 : ASSERT_EQ (tok5->m_kind, pp_token::kind::begin_quote);
333 :
334 4 : pp_token *tok6 = tok5->m_next;
335 4 : ASSERT_EQ (tok6->m_kind, pp_token::kind::text);
336 4 : ASSERT_STREQ (((pp_token_text *)tok6)->m_value.get (),
337 : "bar");
338 :
339 4 : pp_token *tok7 = tok6->m_next;
340 4 : ASSERT_EQ (tok7->m_kind, pp_token::kind::end_quote);
341 4 : ASSERT_EQ (tok7->m_next, nullptr);
342 :
343 :
344 : // Check that we can replay buf via pp_token_buffer_element
345 4 : pp_token_buffer_element e (buf);
346 4 : pretty_printer pp;
347 4 : pp_printf (&pp, "before %e after", &e);
348 4 : ASSERT_STREQ (pp_formatted_text (&pp), "before x: 'foo' y: 'bar' after");
349 8 : }
350 :
351 : /* Run all of the selftests within this file. */
352 :
353 : void
354 4 : pretty_print_token_buffer_cc_tests ()
355 : {
356 4 : test_empty ();
357 4 : test_print ();
358 4 : }
359 :
360 : } // namespace selftest
361 :
362 : #endif /* CHECKING_P */
|