Branch data Line data Source code
1 : : /* Printing paths through the code associated with a diagnostic.
2 : : Copyright (C) 2019-2025 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_ALGORITHM
23 : : #define INCLUDE_MAP
24 : : #define INCLUDE_STRING
25 : : #define INCLUDE_VECTOR
26 : : #include "system.h"
27 : : #include "coretypes.h"
28 : : #include "diagnostics/macro-unwinding.h"
29 : : #include "intl.h"
30 : : #include "diagnostics/paths.h"
31 : : #include "gcc-rich-location.h"
32 : : #include "diagnostics/color.h"
33 : : #include "diagnostics/event-id.h"
34 : : #include "diagnostics/source-printing-effects.h"
35 : : #include "pretty-print-markup.h"
36 : : #include "selftest.h"
37 : : #include "diagnostics/selftest-context.h"
38 : : #include "diagnostics/selftest-paths.h"
39 : : #include "text-art/theme.h"
40 : : #include "diagnostics/text-sink.h"
41 : : #include "diagnostics/html-sink.h"
42 : : #include "xml.h"
43 : : #include "xml-printer.h"
44 : :
45 : : /* Disable warnings about missing quoting in GCC diagnostics for the print
46 : : calls below. */
47 : : #if __GNUC__ >= 10
48 : : # pragma GCC diagnostic push
49 : : # pragma GCC diagnostic ignored "-Wformat-diag"
50 : : #endif
51 : :
52 : : /* Anonymous namespace for path-printing code. */
53 : :
54 : : namespace {
55 : :
56 : : using namespace diagnostics;
57 : : using namespace diagnostics::paths;
58 : :
59 : : /* A bundle of state for printing a path. */
60 : :
61 : : class path_print_policy
62 : : {
63 : : public:
64 : 1513 : path_print_policy (const diagnostics::text_sink &text_output)
65 : 1513 : : m_source_policy (text_output.get_context ())
66 : : {
67 : : }
68 : :
69 : 3 : path_print_policy (const diagnostics::context &dc)
70 : 3 : : m_source_policy (dc)
71 : : {
72 : : }
73 : :
74 : : text_art::theme *
75 : 2651 : get_diagram_theme () const
76 : : {
77 : 2651 : return m_source_policy.get_diagram_theme ();
78 : : }
79 : :
80 : : const diagnostics::source_print_policy &
81 : 2297 : get_source_policy () const { return m_source_policy; }
82 : :
83 : : private:
84 : : diagnostics::source_print_policy m_source_policy;
85 : : };
86 : :
87 : : /* Subclass of range_label for showing a particular event
88 : : when showing a consecutive run of events within a diagnostic path as
89 : : labelled ranges within one gcc_rich_location. */
90 : :
91 : 2651 : class path_label : public range_label
92 : : {
93 : : public:
94 : 2651 : path_label (const path &path_,
95 : : const pretty_printer &ref_pp,
96 : : unsigned start_idx,
97 : : bool colorize,
98 : : bool allow_emojis)
99 : 2651 : : m_path (path_),
100 : 2651 : m_ref_pp (ref_pp),
101 : 2651 : m_start_idx (start_idx), m_effects (*this),
102 : 2651 : m_colorize (colorize), m_allow_emojis (allow_emojis)
103 : : {}
104 : :
105 : 4665 : label_text get_text (unsigned range_idx) const final override
106 : : {
107 : 4665 : unsigned event_idx = m_start_idx + range_idx;
108 : 4665 : const event &ev = m_path.get_event (event_idx);
109 : :
110 : 4665 : const event::meaning meaning (ev.get_meaning ());
111 : :
112 : 4665 : auto pp = m_ref_pp.clone ();
113 : 4665 : pp_show_color (pp.get ()) = m_colorize;
114 : 4665 : event_id_t event_id (event_idx);
115 : 4665 : pp_printf (pp.get (), "%@", &event_id);
116 : 4665 : pp_space (pp.get ());
117 : :
118 : 4665 : if (meaning.m_verb == event::verb::danger
119 : 200 : && m_allow_emojis)
120 : : {
121 : 2 : pp_unicode_character (pp.get (), 0x26A0); /* U+26A0 WARNING SIGN. */
122 : : /* Append U+FE0F VARIATION SELECTOR-16 to select the emoji
123 : : variation of the char. */
124 : 2 : pp_unicode_character (pp.get (), 0xFE0F);
125 : : /* U+26A0 WARNING SIGN has East_Asian_Width == Neutral, but in its
126 : : emoji variant is printed (by vte at least) with a 2nd half
127 : : overlapping the next char. Hence we add two spaces here: a space
128 : : to be covered by this overlap, plus another space of padding. */
129 : 2 : pp_string (pp.get (), " ");
130 : : }
131 : :
132 : 4665 : ev.print_desc (*pp.get ());
133 : :
134 : 4665 : label_text result
135 : 4665 : = label_text::take (xstrdup (pp_formatted_text (pp.get ())));
136 : 9330 : return result;
137 : 4665 : }
138 : :
139 : : const diagnostics::label_effects *
140 : 9330 : get_effects (unsigned /*range_idx*/) const final override
141 : : {
142 : 9330 : return &m_effects;
143 : : }
144 : :
145 : : private:
146 : 2651 : class path_label_effects : public label_effects
147 : : {
148 : : public:
149 : 2651 : path_label_effects (const path_label &path_label)
150 : 2651 : : m_path_label (path_label)
151 : : {
152 : : }
153 : 4665 : bool has_in_edge (unsigned range_idx) const final override
154 : : {
155 : 9330 : if (const event *prev_event
156 : 4665 : = m_path_label.get_prev_event (range_idx))
157 : 3250 : return prev_event->connect_to_next_event_p ();
158 : : return false;
159 : : }
160 : 4665 : bool has_out_edge (unsigned range_idx) const final override
161 : : {
162 : 9330 : const event &ev = m_path_label.get_event (range_idx);
163 : 4665 : return ev.connect_to_next_event_p ();
164 : : }
165 : :
166 : : private:
167 : : const path_label &m_path_label;
168 : : };
169 : :
170 : 4665 : const event &get_event (unsigned range_idx) const
171 : : {
172 : 4665 : unsigned event_idx = m_start_idx + range_idx;
173 : 4665 : return m_path.get_event (event_idx);
174 : : }
175 : :
176 : 4665 : const event *get_prev_event (unsigned range_idx) const
177 : : {
178 : 4665 : if (m_start_idx + range_idx == 0)
179 : : return nullptr;
180 : 3250 : unsigned event_idx = m_start_idx + range_idx - 1;
181 : 3250 : return &m_path.get_event (event_idx);
182 : : }
183 : :
184 : : const path &m_path;
185 : : const pretty_printer &m_ref_pp;
186 : : unsigned m_start_idx;
187 : : path_label_effects m_effects;
188 : : const bool m_colorize;
189 : : const bool m_allow_emojis;
190 : : };
191 : :
192 : : /* Return true if E1 and E2 can be consolidated into the same run of events
193 : : when printing a diagnostic path. */
194 : :
195 : : static bool
196 : 3564 : can_consolidate_events (const path &p,
197 : : const event &e1,
198 : : unsigned ev1_idx,
199 : : const event &e2,
200 : : unsigned ev2_idx,
201 : : bool check_locations)
202 : : {
203 : 3564 : if (e1.get_thread_id () != e2.get_thread_id ())
204 : : return false;
205 : :
206 : 3561 : if (!p.same_function_p (ev1_idx, ev2_idx))
207 : : return false;
208 : :
209 : 3154 : if (e1.get_stack_depth () != e2.get_stack_depth ())
210 : : return false;
211 : :
212 : 3122 : if (check_locations)
213 : : {
214 : 2998 : location_t loc1 = e1.get_location ();
215 : 2998 : location_t loc2 = e2.get_location ();
216 : :
217 : 2998 : if (loc1 < RESERVED_LOCATION_COUNT
218 : 2998 : || loc2 < RESERVED_LOCATION_COUNT)
219 : : return false;
220 : :
221 : : /* Neither can be macro-based. */
222 : 2872 : if (linemap_location_from_macro_expansion_p (line_table, loc1))
223 : : return false;
224 : 2838 : if (linemap_location_from_macro_expansion_p (line_table, loc2))
225 : : return false;
226 : : }
227 : :
228 : : /* Passed all the tests. */
229 : : return true;
230 : : }
231 : :
232 : : struct event_range;
233 : : struct path_summary;
234 : : class thread_event_printer;
235 : :
236 : : /* A bundle of information about all of the events in a diagnostic path
237 : : relating to a specific path, for use by path_summary. */
238 : :
239 : : class per_thread_summary
240 : : {
241 : : public:
242 : 1508 : per_thread_summary (const path &path_,
243 : : const logical_locations::manager &logical_loc_mgr,
244 : : label_text name, unsigned swimlane_idx)
245 : 1508 : : m_path (path_),
246 : 1508 : m_logical_loc_mgr (logical_loc_mgr),
247 : 1508 : m_name (std::move (name)),
248 : 1508 : m_swimlane_idx (swimlane_idx),
249 : 1508 : m_last_event (nullptr),
250 : 1508 : m_min_depth (INT_MAX),
251 : 1508 : m_max_depth (INT_MIN)
252 : : {}
253 : :
254 : 5071 : void update_depth_limits (int stack_depth)
255 : : {
256 : 5071 : if (stack_depth < m_min_depth)
257 : 1512 : m_min_depth = stack_depth;
258 : 5071 : if (stack_depth > m_max_depth)
259 : 1715 : m_max_depth = stack_depth;
260 : : }
261 : :
262 : 4 : const char *get_name () const { return m_name.get (); }
263 : 2651 : unsigned get_swimlane_index () const { return m_swimlane_idx; }
264 : :
265 : : bool interprocedural_p () const;
266 : :
267 : : private:
268 : : friend struct path_summary;
269 : : friend class thread_event_printer;
270 : : friend struct event_range;
271 : :
272 : : const path &m_path;
273 : : const logical_locations::manager &m_logical_loc_mgr;
274 : :
275 : : const label_text m_name;
276 : :
277 : : /* The "swimlane index" is the order in which this per_thread_summary
278 : : was created, for use when printing the events. */
279 : : const unsigned m_swimlane_idx;
280 : :
281 : : // The event ranges specific to this thread:
282 : : auto_vec<event_range *> m_event_ranges;
283 : :
284 : : const event *m_last_event;
285 : :
286 : : int m_min_depth;
287 : : int m_max_depth;
288 : : };
289 : :
290 : : /* A stack frame for use in HTML output, holding child stack frames,
291 : : and event ranges. */
292 : :
293 : : struct stack_frame
294 : : {
295 : 7 : stack_frame (std::unique_ptr<stack_frame> parent,
296 : : logical_locations::key logical_loc,
297 : : int stack_depth)
298 : 7 : : m_parent (std::move (parent)),
299 : 7 : m_logical_loc (logical_loc),
300 : 7 : m_stack_depth (stack_depth)
301 : : {}
302 : :
303 : : std::unique_ptr<stack_frame> m_parent;
304 : : logical_locations::key m_logical_loc;
305 : : const int m_stack_depth;
306 : : };
307 : :
308 : : /* Begin emitting content relating to a new stack frame within PARENT.
309 : : Allocated a new stack_frame and return it. */
310 : :
311 : : static std::unique_ptr<stack_frame>
312 : 7 : begin_html_stack_frame (xml::printer &xp,
313 : : std::unique_ptr<stack_frame> parent,
314 : : logical_locations::key logical_loc,
315 : : int stack_depth,
316 : : const logical_locations::manager *logical_loc_mgr)
317 : : {
318 : 7 : if (logical_loc)
319 : : {
320 : 6 : gcc_assert (logical_loc_mgr);
321 : 6 : xp.push_tag_with_class ("table", "stack-frame-with-margin", false);
322 : 6 : xp.push_tag ("tr", false);
323 : 6 : {
324 : 6 : xp.push_tag_with_class ("td", "interprocmargin", false);
325 : 6 : xp.set_attr ("style", "padding-left: 100px");
326 : 6 : xp.pop_tag ("td");
327 : : }
328 : 6 : xp.push_tag_with_class ("td", "stack-frame", false);
329 : 6 : label_text funcname
330 : 6 : = logical_loc_mgr->get_name_for_path_output (logical_loc);
331 : 6 : if (funcname.get ())
332 : : {
333 : 6 : xp.push_tag_with_class ("div", "frame-funcname", false);
334 : 6 : xp.push_tag ("span", true);
335 : 6 : xp.add_text (funcname.get ());
336 : 6 : xp.pop_tag ("span");
337 : 6 : xp.pop_tag ("div");
338 : : }
339 : 6 : }
340 : 7 : return std::make_unique<stack_frame> (std::move (parent),
341 : : logical_loc,
342 : 7 : stack_depth);
343 : : }
344 : :
345 : : /* Finish emitting content for FRAME and delete it.
346 : : Return parent. */
347 : :
348 : : static std::unique_ptr<stack_frame>
349 : 7 : end_html_stack_frame (xml::printer &xp,
350 : : std::unique_ptr<stack_frame> frame)
351 : : {
352 : 7 : auto parent = std::move (frame->m_parent);
353 : 7 : if (frame->m_logical_loc)
354 : : {
355 : 6 : xp.pop_tag ("td");
356 : 6 : xp.pop_tag ("tr");
357 : 6 : xp.pop_tag ("table");
358 : : }
359 : 7 : return parent;
360 : : }
361 : :
362 : : /* Append an HTML <div> element to XP containing an SVG arrow representing
363 : : a change in stack depth from OLD_DEPTH to NEW_DEPTH. */
364 : :
365 : : static void
366 : 4 : emit_svg_arrow (xml::printer &xp, int old_depth, int new_depth)
367 : : {
368 : 4 : const int pixels_per_depth = 100;
369 : 4 : const int min_depth = MIN (old_depth, new_depth);
370 : 4 : const int base_x = 20;
371 : 4 : const int excess = 30;
372 : 4 : const int last_x
373 : 4 : = base_x + (old_depth - min_depth) * pixels_per_depth;
374 : 4 : const int this_x
375 : 4 : = base_x + (new_depth - min_depth) * pixels_per_depth;
376 : 4 : pretty_printer tmp_pp;
377 : 4 : pretty_printer *pp = &tmp_pp;
378 : 5 : pp_printf (pp, "<div class=\"%s\">\n",
379 : : old_depth < new_depth
380 : : ? "between-ranges-call" : "between-ranges-return");
381 : 4 : pp_printf (pp, " <svg height=\"30\" width=\"%i\">\n",
382 : : MAX (last_x, this_x) + excess);
383 : 4 : pp_string
384 : 4 : (pp,
385 : : " <defs>\n"
386 : : " <marker id=\"arrowhead\" markerWidth=\"10\" markerHeight=\"7\"\n"
387 : : " refX=\"0\" refY=\"3.5\" orient=\"auto\" stroke=\"#0088ce\" fill=\"#0088ce\">\n"
388 : : " <polygon points=\"0 0, 10 3.5, 0 7\"/>\n"
389 : : " </marker>\n"
390 : : " </defs>\n");
391 : 4 : pp_printf (pp,
392 : : " <polyline points=\"%i,0 %i,10 %i,10 %i,20\"\n",
393 : : last_x, last_x, this_x, this_x);
394 : 4 : pp_string
395 : 4 : (pp,
396 : : " style=\"fill:none;stroke: #0088ce\"\n"
397 : : " marker-end=\"url(#arrowhead)\"/>\n"
398 : : " </svg>\n"
399 : : "</div>\n\n");
400 : 4 : xp.add_raw (pp_formatted_text (pp));
401 : 4 : }
402 : :
403 : : /* A range of consecutive events within a diagnostic path, all within the
404 : : same thread, and with the same fndecl and stack_depth, and which are suitable
405 : : to print with a single call to diagnostic_show_locus. */
406 : 2651 : struct event_range
407 : : {
408 : : /* A struct for tracking the mergability of labels on a particular
409 : : source line. In particular, track information about links between
410 : : labels to ensure that we only consolidate events involving links
411 : : that the source-printing code is able to handle (splitting them
412 : : otherwise). */
413 : : struct per_source_line_info
414 : : {
415 : 3801 : void init (int line)
416 : : {
417 : 3801 : m_line = line;
418 : 3801 : m_has_in_edge = false;
419 : 3801 : m_has_out_edge = false;
420 : 3801 : m_min_label_source_column = INT_MAX;
421 : 3801 : m_max_label_source_column = INT_MIN;
422 : 3801 : }
423 : :
424 : : /* Return true if our source-printing/labelling/linking code can handle
425 : : the events already on this source line, *and* a new event at COLUMN. */
426 : : bool
427 : 2287 : can_add_label_for_event_p (bool has_in_edge,
428 : : const event *prev_event,
429 : : bool has_out_edge,
430 : : int column) const
431 : : {
432 : : /* Any existing in-edge has to be the left-most label on its
433 : : source line. */
434 : 2287 : if (m_has_in_edge && column < m_min_label_source_column)
435 : : return false;
436 : : /* Any existing out-edge has to be the right-most label on its
437 : : source line. */
438 : 2171 : if (m_has_out_edge && column > m_max_label_source_column)
439 : : return false;
440 : : /* Can't have more than one in-edge. */
441 : 2051 : if (m_has_in_edge && has_in_edge)
442 : : return false;
443 : : /* Can't have more than one out-edge. */
444 : 2051 : if (m_has_out_edge && has_out_edge)
445 : : return false;
446 : :
447 : 1995 : if (has_in_edge)
448 : : {
449 : : /* Any new in-edge needs to be the left-most label on its
450 : : source line. */
451 : 1186 : if (column > m_min_label_source_column)
452 : 228 : return false;
453 : :
454 : 1186 : gcc_assert (prev_event);
455 : 1186 : const location_t prev_loc = prev_event->get_location ();
456 : 1186 : expanded_location prev_exploc
457 : : = linemap_client_expand_location_to_spelling_point
458 : 1186 : (line_table, prev_loc, location_aspect::caret);
459 : : /* The destination in-edge's line number has to be <= the
460 : : source out-edge's line number (if any). */
461 : 1186 : if (prev_exploc.line >= m_line)
462 : : return false;
463 : : }
464 : :
465 : : /* Any new out-edge needs to be the right-most label on its
466 : : source line. */
467 : 1767 : if (has_out_edge)
468 : 291 : if (column < m_max_label_source_column)
469 : : return false;
470 : :
471 : : /* All checks passed; we can add the new event at COLUMN. */
472 : : return true;
473 : : }
474 : :
475 : : void
476 : 3806 : add_label_for_event (bool has_in_edge, bool has_out_edge, int column)
477 : : {
478 : 3806 : if (has_in_edge)
479 : 1486 : m_has_in_edge = true;
480 : 3806 : if (has_out_edge)
481 : 1486 : m_has_out_edge = true;
482 : 3806 : m_min_label_source_column = std::min (m_min_label_source_column, column);
483 : 3806 : m_max_label_source_column = std::max (m_max_label_source_column, column);
484 : : }
485 : :
486 : : int m_line;
487 : : bool m_has_in_edge;
488 : : bool m_has_out_edge;
489 : : int m_min_label_source_column;
490 : : int m_max_label_source_column;
491 : : };
492 : :
493 : 2651 : event_range (const path &path_,
494 : : const pretty_printer &ref_pp,
495 : : unsigned start_idx,
496 : : const event &initial_event,
497 : : per_thread_summary &t,
498 : : bool show_event_links,
499 : : bool colorize_labels,
500 : : bool allow_emojis)
501 : 2651 : : m_path (path_),
502 : 2651 : m_initial_event (initial_event),
503 : 2651 : m_logical_loc (initial_event.get_logical_location ()),
504 : 2651 : m_stack_depth (initial_event.get_stack_depth ()),
505 : 2651 : m_start_idx (start_idx), m_end_idx (start_idx),
506 : 2651 : m_path_label (path_, ref_pp,
507 : : start_idx, colorize_labels, allow_emojis),
508 : 2651 : m_richloc (initial_event.get_location (), &m_path_label, nullptr),
509 : 2651 : m_thread_id (initial_event.get_thread_id ()),
510 : 2651 : m_per_thread_summary (t),
511 : 2651 : m_show_event_links (show_event_links)
512 : : {
513 : 2651 : if (m_show_event_links)
514 : : {
515 : 2039 : expanded_location exploc
516 : : = linemap_client_expand_location_to_spelling_point
517 : 2039 : (line_table, initial_event.get_location (),
518 : : location_aspect::caret);
519 : 2039 : per_source_line_info &source_line_info
520 : 2039 : = get_per_source_line_info (exploc.line);
521 : :
522 : 2039 : const event *prev_thread_event = t.m_last_event;
523 : 2039 : const bool has_in_edge
524 : : = (prev_thread_event
525 : 2039 : ? prev_thread_event->connect_to_next_event_p ()
526 : 2039 : : false);
527 : 2039 : const bool has_out_edge = initial_event.connect_to_next_event_p ();
528 : :
529 : 2039 : source_line_info.add_label_for_event
530 : 4078 : (has_in_edge, has_out_edge, exploc.column);
531 : : }
532 : 2651 : }
533 : :
534 : : per_source_line_info &
535 : 4980 : get_per_source_line_info (int source_line)
536 : : {
537 : 4980 : bool existed = false;
538 : 4980 : per_source_line_info &result
539 : 4980 : = m_source_line_info_map.get_or_insert (source_line, &existed);
540 : 4980 : if (!existed)
541 : 3801 : result.init (source_line);
542 : 4980 : return result;
543 : : }
544 : :
545 : 3564 : bool maybe_add_event (const path_print_policy &policy,
546 : : const event &new_ev,
547 : : unsigned new_ev_idx,
548 : : bool check_rich_locations)
549 : : {
550 : 3564 : if (!can_consolidate_events (m_path,
551 : : m_initial_event, m_start_idx,
552 : : new_ev, new_ev_idx,
553 : : check_rich_locations))
554 : : return false;
555 : :
556 : : /* Verify compatibility of the new label and existing labels
557 : : with respect to the link-printing code. */
558 : 2941 : expanded_location exploc
559 : : = linemap_client_expand_location_to_spelling_point
560 : 2941 : (line_table, new_ev.get_location (), location_aspect::caret);
561 : 2941 : per_source_line_info &source_line_info
562 : 2941 : = get_per_source_line_info (exploc.line);
563 : 2941 : const event *prev_event = nullptr;
564 : 2941 : if (new_ev_idx > 0)
565 : 2941 : prev_event = &m_path.get_event (new_ev_idx - 1);
566 : 2941 : const bool has_in_edge = (prev_event
567 : 2941 : ? prev_event->connect_to_next_event_p ()
568 : 2941 : : false);
569 : 2941 : const bool has_out_edge = new_ev.connect_to_next_event_p ();
570 : 2941 : if (m_show_event_links)
571 : 2287 : if (!source_line_info.can_add_label_for_event_p
572 : 2287 : (has_in_edge, prev_event,
573 : : has_out_edge, exploc.column))
574 : : return false;
575 : :
576 : : /* Potentially verify that the locations are sufficiently close. */
577 : 2421 : if (check_rich_locations)
578 : 2297 : if (!m_richloc.add_location_if_nearby (policy.get_source_policy (),
579 : 2297 : new_ev.get_location (),
580 : : false, &m_path_label))
581 : : return false;
582 : :
583 : 2420 : m_end_idx = new_ev_idx;
584 : 2420 : m_per_thread_summary.m_last_event = &new_ev;
585 : :
586 : 2420 : if (m_show_event_links)
587 : 1767 : source_line_info.add_label_for_event
588 : 3534 : (has_in_edge, has_out_edge, exploc.column);
589 : :
590 : : return true;
591 : : }
592 : :
593 : : /* Print the events in this range to PP, typically as a single
594 : : call to diagnostic_show_locus. */
595 : :
596 : 2644 : void print_as_text (pretty_printer &pp,
597 : : diagnostics::text_sink &text_output,
598 : : diagnostics::source_effect_info *effect_info)
599 : : {
600 : 2644 : location_t initial_loc = m_initial_event.get_location ();
601 : :
602 : 2644 : diagnostics::context &dc = text_output.get_context ();
603 : :
604 : : /* Emit a span indicating the filename (and line/column) if the
605 : : line has changed relative to the last call to
606 : : diagnostic_show_locus. */
607 : 2644 : if (dc.get_source_printing_options ().enabled)
608 : : {
609 : 2644 : expanded_location exploc
610 : : = linemap_client_expand_location_to_spelling_point
611 : 2644 : (line_table, initial_loc, location_aspect::caret);
612 : 2644 : if (exploc.file != LOCATION_FILE (dc.m_last_location))
613 : : {
614 : 1330 : diagnostics::location_print_policy loc_policy (text_output);
615 : 1330 : loc_policy.print_text_span_start (dc, pp, exploc);
616 : : }
617 : : }
618 : :
619 : : /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
620 : : primary location for an event, diagnostic_show_locus won't print
621 : : anything.
622 : :
623 : : In particular the label for the event won't get printed.
624 : : Fail more gracefully in this case by showing the event
625 : : index and text, at no particular location. */
626 : 2644 : if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
627 : : {
628 : 686 : for (unsigned i = m_start_idx; i <= m_end_idx; i++)
629 : : {
630 : 405 : const event &iter_event = m_path.get_event (i);
631 : 405 : diagnostic_event_id_t event_id (i);
632 : 405 : pp_printf (&pp, " %@: ", &event_id);
633 : 405 : iter_event.print_desc (pp);
634 : 405 : pp_newline (&pp);
635 : : }
636 : : return;
637 : : }
638 : :
639 : : /* Call diagnostic_show_locus to show the events using labels. */
640 : 2363 : diagnostic_show_locus (&dc, text_output.get_source_printing_options (),
641 : : &m_richloc, diagnostics::kind::path, &pp,
642 : : effect_info);
643 : :
644 : : /* If we have a macro expansion, show the expansion to the user. */
645 : 2363 : if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
646 : : {
647 : 44 : gcc_assert (m_start_idx == m_end_idx);
648 : 44 : maybe_unwind_expanded_macro_loc (text_output, initial_loc);
649 : : }
650 : : }
651 : :
652 : : /* Print the events in this range to XP, typically as a single
653 : : call to diagnostic_show_locus_as_html. */
654 : :
655 : 7 : void print_as_html (xml::printer &xp,
656 : : diagnostics::context &dc,
657 : : diagnostics::source_effect_info *effect_info,
658 : : html_label_writer *event_label_writer)
659 : : {
660 : 7 : location_t initial_loc = m_initial_event.get_location ();
661 : :
662 : : /* Emit a span indicating the filename (and line/column) if the
663 : : line has changed relative to the last call to
664 : : diagnostic_show_locus. */
665 : 7 : if (dc.get_source_printing_options ().enabled)
666 : : {
667 : 7 : expanded_location exploc
668 : : = linemap_client_expand_location_to_spelling_point
669 : 7 : (line_table, initial_loc, location_aspect::caret);
670 : 7 : if (exploc.file != LOCATION_FILE (dc.m_last_location))
671 : : {
672 : 1 : diagnostics::location_print_policy loc_policy (dc);
673 : 1 : loc_policy.print_html_span_start (dc, xp, exploc);
674 : : }
675 : : }
676 : :
677 : : /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
678 : : primary location for an event, diagnostic_show_locus_as_html won't print
679 : : anything.
680 : :
681 : : In particular the label for the event won't get printed.
682 : : Fail more gracefully in this case by showing the event
683 : : index and text, at no particular location. */
684 : 7 : if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
685 : : {
686 : 2 : for (unsigned i = m_start_idx; i <= m_end_idx; i++)
687 : : {
688 : 1 : const event &iter_event = m_path.get_event (i);
689 : 1 : diagnostic_event_id_t event_id (i);
690 : 1 : pretty_printer pp;
691 : 1 : pp_printf (&pp, " %@: ", &event_id);
692 : 1 : iter_event.print_desc (pp);
693 : 1 : if (event_label_writer)
694 : 1 : event_label_writer->begin_label ();
695 : 1 : xp.add_text_from_pp (pp);
696 : 1 : if (event_label_writer)
697 : 1 : event_label_writer->end_label ();
698 : 1 : }
699 : : return;
700 : : }
701 : :
702 : : /* Call diagnostic_show_locus_as_html to show the source,
703 : : showing events using labels. */
704 : 6 : diagnostic_show_locus_as_html (&dc, dc.get_source_printing_options (),
705 : : &m_richloc, diagnostics::kind::path, xp,
706 : : effect_info, event_label_writer);
707 : :
708 : : // TODO: show macro expansions
709 : : }
710 : :
711 : : const path &m_path;
712 : : const event &m_initial_event;
713 : : logical_locations::key m_logical_loc;
714 : : int m_stack_depth;
715 : : unsigned m_start_idx;
716 : : unsigned m_end_idx;
717 : : path_label m_path_label;
718 : : gcc_rich_location m_richloc;
719 : : thread_id_t m_thread_id;
720 : : per_thread_summary &m_per_thread_summary;
721 : : hash_map<int_hash<int, -1, -2>,
722 : : per_source_line_info> m_source_line_info_map;
723 : : bool m_show_event_links;
724 : : };
725 : :
726 : : /* A struct for grouping together the events in a path into
727 : : ranges of events, partitioned by thread and by stack frame (i.e. by fndecl
728 : : and stack depth). */
729 : :
730 : : struct path_summary
731 : : {
732 : : path_summary (const path_print_policy &policy,
733 : : const pretty_printer &ref_pp,
734 : : const path &path_,
735 : : bool check_rich_locations,
736 : : bool colorize = false,
737 : : bool show_event_links = true);
738 : :
739 : 2644 : const logical_locations::manager &get_logical_location_manager () const
740 : : {
741 : 2644 : return m_logical_loc_mgr;
742 : : }
743 : 40 : unsigned get_num_ranges () const { return m_ranges.length (); }
744 : 7932 : bool multithreaded_p () const { return m_per_thread_summary.length () > 1; }
745 : :
746 : : const per_thread_summary &get_events_for_thread_id (thread_id_t tid)
747 : : {
748 : : per_thread_summary **slot = m_thread_id_to_events.get (tid);
749 : : gcc_assert (slot);
750 : : gcc_assert (*slot);
751 : : return **slot;
752 : : }
753 : :
754 : : const logical_locations::manager &m_logical_loc_mgr;
755 : : auto_delete_vec <event_range> m_ranges;
756 : : auto_delete_vec <per_thread_summary> m_per_thread_summary;
757 : : hash_map<int_hash<thread_id_t, -1, -2>,
758 : : per_thread_summary *> m_thread_id_to_events;
759 : :
760 : : private:
761 : : per_thread_summary &
762 : 5071 : get_or_create_events_for_thread_id (const path &path_,
763 : : thread_id_t tid)
764 : : {
765 : 5071 : if (per_thread_summary **slot = m_thread_id_to_events.get (tid))
766 : 3563 : return **slot;
767 : :
768 : 1508 : const thread &thread = path_.get_thread (tid);
769 : 1508 : per_thread_summary *pts
770 : : = new per_thread_summary (path_,
771 : : m_logical_loc_mgr,
772 : 3016 : thread.get_name (false),
773 : 1509 : m_per_thread_summary.length ());
774 : 1508 : m_thread_id_to_events.put (tid, pts);
775 : 1508 : m_per_thread_summary.safe_push (pts);
776 : 1508 : return *pts;
777 : : }
778 : : };
779 : :
780 : : /* Return true iff there is more than one stack frame used by the events
781 : : of this thread. */
782 : :
783 : : bool
784 : 2644 : per_thread_summary::interprocedural_p () const
785 : : {
786 : 4677 : if (m_event_ranges.is_empty ())
787 : : return false;
788 : 2644 : int first_stack_depth = m_event_ranges[0]->m_stack_depth;
789 : 6962 : for (auto range : m_event_ranges)
790 : : {
791 : 4929 : if (!m_path.same_function_p (m_event_ranges[0]->m_start_idx,
792 : 4929 : range->m_start_idx))
793 : : return true;
794 : 4373 : if (range->m_stack_depth != first_stack_depth)
795 : : return true;
796 : : }
797 : : return false;
798 : : }
799 : :
800 : : /* path_summary's ctor. */
801 : :
802 : 1516 : path_summary::path_summary (const path_print_policy &policy,
803 : : const pretty_printer &ref_pp,
804 : : const path &path_,
805 : : bool check_rich_locations,
806 : : bool colorize,
807 : 1516 : bool show_event_links)
808 : 1516 : : m_logical_loc_mgr (path_.get_logical_location_manager ())
809 : : {
810 : 1516 : const unsigned num_events = path_.num_events ();
811 : :
812 : 1516 : event_range *cur_event_range = nullptr;
813 : 6587 : for (unsigned idx = 0; idx < num_events; idx++)
814 : : {
815 : 5071 : const event &ev = path_.get_event (idx);
816 : 5071 : const thread_id_t thread_id = ev.get_thread_id ();
817 : 5071 : per_thread_summary &pts
818 : 5071 : = get_or_create_events_for_thread_id (path_, thread_id);
819 : :
820 : 5071 : pts.update_depth_limits (ev.get_stack_depth ());
821 : :
822 : 5071 : if (cur_event_range)
823 : 3564 : if (cur_event_range->maybe_add_event (policy,
824 : : ev,
825 : : idx, check_rich_locations))
826 : 2420 : continue;
827 : :
828 : 2651 : auto theme = policy.get_diagram_theme ();
829 : 2651 : const bool allow_emojis = theme ? theme->emojis_p () : false;
830 : 5302 : cur_event_range = new event_range (path_, ref_pp,
831 : : idx, ev, pts,
832 : : show_event_links,
833 : : colorize,
834 : 2651 : allow_emojis);
835 : 2651 : m_ranges.safe_push (cur_event_range);
836 : 2651 : pts.m_event_ranges.safe_push (cur_event_range);
837 : 2651 : pts.m_last_event = &ev;
838 : : }
839 : 1516 : }
840 : :
841 : : /* Write SPACES to PP. */
842 : :
843 : : static void
844 : 4697 : write_indent (pretty_printer *pp, int spaces)
845 : : {
846 : 32127 : for (int i = 0; i < spaces; i++)
847 : 27430 : pp_space (pp);
848 : 0 : }
849 : :
850 : : static const int base_indent = 2;
851 : : static const int per_frame_indent = 2;
852 : :
853 : : /* A bundle of state for printing event_range instances for a particular
854 : : thread. */
855 : :
856 : 1509 : class thread_event_printer
857 : : {
858 : : public:
859 : 1508 : thread_event_printer (const per_thread_summary &t, bool show_depths)
860 : 1508 : : m_per_thread_summary (t),
861 : 1508 : m_show_depths (show_depths),
862 : 1508 : m_cur_indent (base_indent),
863 : 1508 : m_vbar_column_for_depth (),
864 : 1508 : m_num_printed (0)
865 : : {
866 : 1508 : }
867 : :
868 : : /* Get the previous event_range within this thread, if any. */
869 : 2644 : const event_range *get_any_prev_range () const
870 : : {
871 : 2644 : if (m_num_printed > 0)
872 : 1139 : return m_per_thread_summary.m_event_ranges[m_num_printed - 1];
873 : : else
874 : : return nullptr;
875 : : }
876 : :
877 : : /* Get the next event_range within this thread, if any. */
878 : 2644 : const event_range *get_any_next_range () const
879 : : {
880 : 5288 : if (m_num_printed < m_per_thread_summary.m_event_ranges.length () - 1)
881 : 1139 : return m_per_thread_summary.m_event_ranges[m_num_printed + 1];
882 : : else
883 : : return nullptr;
884 : : }
885 : :
886 : : void
887 : 2644 : print_swimlane_for_event_range_as_text (diagnostics::text_sink &text_output,
888 : : pretty_printer *pp,
889 : : const logical_locations::manager &logical_loc_mgr,
890 : : event_range *range,
891 : : diagnostics::source_effect_info *effect_info)
892 : : {
893 : 2644 : gcc_assert (pp);
894 : 2644 : const char *const line_color = "path";
895 : 2644 : const char *start_line_color
896 : 2644 : = colorize_start (pp_show_color (pp), line_color);
897 : 2644 : const char *end_line_color = colorize_stop (pp_show_color (pp));
898 : :
899 : 2644 : text_art::ascii_theme fallback_theme;
900 : 2644 : text_art::theme *theme = text_output.get_diagram_theme ();
901 : 2644 : if (!theme)
902 : 588 : theme = &fallback_theme;
903 : :
904 : 2644 : cppchar_t depth_marker_char = theme->get_cppchar
905 : 2644 : (text_art::theme::cell_kind::INTERPROCEDURAL_DEPTH_MARKER);
906 : : /* e.g. "|". */
907 : :
908 : 2644 : const bool interprocedural_p = m_per_thread_summary.interprocedural_p ();
909 : :
910 : 2644 : write_indent (pp, m_cur_indent);
911 : 2644 : if (const event_range *prev_range = get_any_prev_range ())
912 : : {
913 : 1139 : if (range->m_stack_depth > prev_range->m_stack_depth)
914 : : {
915 : 322 : gcc_assert (interprocedural_p);
916 : : /* Show pushed stack frame(s). */
917 : 322 : cppchar_t left = theme->get_cppchar
918 : 322 : (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_LEFT);
919 : 322 : cppchar_t middle = theme->get_cppchar
920 : 322 : (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_MIDDLE);
921 : 322 : cppchar_t right = theme->get_cppchar
922 : 322 : (text_art::theme::cell_kind::INTERPROCEDURAL_PUSH_FRAME_RIGHT);
923 : : /* e.g. "+--> ". */
924 : 322 : pp_string (pp, start_line_color);
925 : 322 : pp_unicode_character (pp, left);
926 : 322 : pp_unicode_character (pp, middle);
927 : 322 : pp_unicode_character (pp, middle);
928 : 322 : pp_unicode_character (pp, right);
929 : 322 : pp_space (pp);
930 : 322 : pp_string (pp, end_line_color);
931 : 322 : m_cur_indent += 5;
932 : : }
933 : : }
934 : 2644 : if (range->m_logical_loc)
935 : : {
936 : 745 : label_text name
937 : 745 : (logical_loc_mgr.get_name_for_path_output (range->m_logical_loc));
938 : 745 : if (name.get ())
939 : 745 : pp_printf (pp, "%qs: ", name.get ());
940 : 745 : }
941 : 2644 : if (range->m_start_idx == range->m_end_idx)
942 : 1107 : pp_printf (pp, "event %i",
943 : : range->m_start_idx + 1);
944 : : else
945 : 1537 : pp_printf (pp, "events %i-%i",
946 : : range->m_start_idx + 1, range->m_end_idx + 1);
947 : 2644 : if (m_show_depths)
948 : 325 : pp_printf (pp, " (depth %i)", range->m_stack_depth);
949 : 2644 : pp_newline (pp);
950 : :
951 : : /* Print a run of events. */
952 : 2644 : if (interprocedural_p)
953 : : {
954 : 611 : write_indent (pp, m_cur_indent + per_frame_indent);
955 : 611 : pp_string (pp, start_line_color);
956 : 611 : pp_unicode_character (pp, depth_marker_char);
957 : 611 : pp_string (pp, end_line_color);
958 : 611 : pp_newline (pp);
959 : :
960 : 611 : char *saved_prefix = pp_take_prefix (pp);
961 : 611 : char *prefix;
962 : 611 : {
963 : 611 : pretty_printer tmp_pp;
964 : 611 : write_indent (&tmp_pp, m_cur_indent + per_frame_indent);
965 : 611 : pp_string (&tmp_pp, start_line_color);
966 : 611 : pp_unicode_character (&tmp_pp, depth_marker_char);
967 : 611 : pp_string (&tmp_pp, end_line_color);
968 : 611 : prefix = xstrdup (pp_formatted_text (&tmp_pp));
969 : 611 : }
970 : 611 : pp_set_prefix (pp, prefix);
971 : 611 : pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
972 : 611 : range->print_as_text (*pp, text_output, effect_info);
973 : 611 : pp_set_prefix (pp, saved_prefix);
974 : :
975 : 611 : write_indent (pp, m_cur_indent + per_frame_indent);
976 : 611 : pp_string (pp, start_line_color);
977 : 611 : pp_unicode_character (pp, depth_marker_char);
978 : 611 : pp_string (pp, end_line_color);
979 : 611 : pp_newline (pp);
980 : : }
981 : : else
982 : 2033 : range->print_as_text (*pp, text_output, effect_info);
983 : :
984 : 2644 : if (const event_range *next_range = get_any_next_range ())
985 : : {
986 : 1139 : if (range->m_stack_depth > next_range->m_stack_depth)
987 : : {
988 : 113 : if (m_vbar_column_for_depth.get (next_range->m_stack_depth))
989 : : {
990 : : /* Show returning from stack frame(s), by printing
991 : : something like:
992 : : " |\n"
993 : : " <-------------+\n"
994 : : " |\n". */
995 : 110 : gcc_assert (interprocedural_p);
996 : 110 : cppchar_t left = theme->get_cppchar
997 : 110 : (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_LEFT);
998 : 110 : cppchar_t middle = theme->get_cppchar
999 : 110 : (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_MIDDLE);
1000 : 110 : cppchar_t right = theme->get_cppchar
1001 : 110 : (text_art::theme::cell_kind::INTERPROCEDURAL_POP_FRAMES_RIGHT);
1002 : 110 : int vbar_for_next_frame
1003 : 110 : = *m_vbar_column_for_depth.get (next_range->m_stack_depth);
1004 : :
1005 : 110 : int indent_for_next_frame
1006 : : = vbar_for_next_frame - per_frame_indent;
1007 : 110 : write_indent (pp, vbar_for_next_frame);
1008 : 110 : pp_string (pp, start_line_color);
1009 : 110 : pp_unicode_character (pp, left);
1010 : 1022 : for (int i = indent_for_next_frame + per_frame_indent;
1011 : 1022 : i < m_cur_indent + per_frame_indent - 1; i++)
1012 : 912 : pp_unicode_character (pp, middle);
1013 : 110 : pp_unicode_character (pp, right);
1014 : 110 : pp_string (pp, end_line_color);
1015 : 110 : pp_newline (pp);
1016 : 110 : m_cur_indent = indent_for_next_frame;
1017 : :
1018 : 110 : write_indent (pp, vbar_for_next_frame);
1019 : 110 : pp_string (pp, start_line_color);
1020 : 110 : pp_unicode_character (pp, depth_marker_char);
1021 : 110 : pp_string (pp, end_line_color);
1022 : 110 : pp_newline (pp);
1023 : : }
1024 : : else
1025 : : {
1026 : : /* Handle disjoint paths (e.g. a callback at some later
1027 : : time). */
1028 : 3 : m_cur_indent = base_indent;
1029 : : }
1030 : : }
1031 : 1026 : else if (range->m_stack_depth < next_range->m_stack_depth)
1032 : : {
1033 : : /* Prepare to show pushed stack frame. */
1034 : 322 : gcc_assert (interprocedural_p);
1035 : 322 : gcc_assert (range->m_stack_depth != EMPTY);
1036 : 322 : gcc_assert (range->m_stack_depth != DELETED);
1037 : 322 : m_vbar_column_for_depth.put (range->m_stack_depth,
1038 : 322 : m_cur_indent + per_frame_indent);
1039 : 322 : m_cur_indent += per_frame_indent;
1040 : : }
1041 : : }
1042 : :
1043 : 2644 : m_num_printed++;
1044 : 2644 : }
1045 : :
1046 : : void
1047 : 7 : print_swimlane_for_event_range_as_html (diagnostics::context &dc,
1048 : : xml::printer &xp,
1049 : : html_label_writer *event_label_writer,
1050 : : event_range *range,
1051 : : diagnostics::source_effect_info *effect_info)
1052 : : {
1053 : 7 : range->print_as_html (xp, dc, effect_info, event_label_writer);
1054 : 7 : m_num_printed++;
1055 : : }
1056 : :
1057 : : int get_cur_indent () const { return m_cur_indent; }
1058 : :
1059 : : private:
1060 : : const per_thread_summary &m_per_thread_summary;
1061 : : bool m_show_depths;
1062 : :
1063 : : /* Print the ranges. */
1064 : : int m_cur_indent;
1065 : :
1066 : : /* Keep track of column numbers of existing '|' characters for
1067 : : stack depths we've already printed. */
1068 : : static const int EMPTY = -1;
1069 : : static const int DELETED = -2;
1070 : : typedef int_hash <int, EMPTY, DELETED> vbar_hash;
1071 : : hash_map <vbar_hash, int> m_vbar_column_for_depth;
1072 : :
1073 : : /* How many event ranges within this swimlane have we printed.
1074 : : This is the index of the next event_range to print. */
1075 : : unsigned m_num_printed;
1076 : : };
1077 : :
1078 : : /* Print path_summary PS to TEXT_OUTPUT, giving an overview of the
1079 : : interprocedural calls and returns.
1080 : :
1081 : : Print the event descriptions in a nested form, printing the event
1082 : : descriptions within calls to diagnostic_show_locus, using labels to
1083 : : show the events:
1084 : :
1085 : : 'foo' (events 1-2)
1086 : : | NN |
1087 : : | |
1088 : : +--> 'bar' (events 3-4)
1089 : : | NN |
1090 : : | |
1091 : : +--> 'baz' (events 5-6)
1092 : : | NN |
1093 : : | |
1094 : : <------------ +
1095 : : |
1096 : : 'foo' (events 7-8)
1097 : : | NN |
1098 : : | |
1099 : : +--> 'bar' (events 9-10)
1100 : : | NN |
1101 : : | |
1102 : : +--> 'baz' (events 11-12)
1103 : : | NN |
1104 : : | |
1105 : :
1106 : : If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
1107 : : of events.
1108 : :
1109 : : For events with UNKNOWN_LOCATION, print a summary of each the event. */
1110 : :
1111 : : static void
1112 : 1513 : print_path_summary_as_text (const path_summary &ps,
1113 : : diagnostics::text_sink &text_output,
1114 : : bool show_depths)
1115 : : {
1116 : 1513 : pretty_printer *const pp = text_output.get_printer ();
1117 : :
1118 : 1513 : std::vector<thread_event_printer> thread_event_printers;
1119 : 6026 : for (auto t : ps.m_per_thread_summary)
1120 : 1505 : thread_event_printers.push_back (thread_event_printer (*t, show_depths));
1121 : :
1122 : : unsigned i;
1123 : : event_range *range;
1124 : : int last_out_edge_column = -1;
1125 : 4157 : FOR_EACH_VEC_ELT (ps.m_ranges, i, range)
1126 : : {
1127 : 2644 : const int swimlane_idx
1128 : 2644 : = range->m_per_thread_summary.get_swimlane_index ();
1129 : 2644 : if (ps.multithreaded_p ())
1130 : 6 : if (i == 0 || ps.m_ranges[i - 1]->m_thread_id != range->m_thread_id)
1131 : : {
1132 : 4 : if (i > 0)
1133 : 3 : pp_newline (pp);
1134 : 4 : pp_printf (pp, "Thread: %qs",
1135 : 4 : range->m_per_thread_summary.get_name ());
1136 : 4 : pp_newline (pp);
1137 : : }
1138 : 2644 : thread_event_printer &tep = thread_event_printers[swimlane_idx];
1139 : : /* Wire up any trailing out-edge from previous range to leading in-edge
1140 : : of this range. */
1141 : 2644 : diagnostics::source_effect_info effect_info;
1142 : 2644 : effect_info.m_leading_in_edge_column = last_out_edge_column;
1143 : 2644 : tep.print_swimlane_for_event_range_as_text
1144 : 2644 : (text_output, pp,
1145 : : ps.get_logical_location_manager (),
1146 : : range, &effect_info);
1147 : 2644 : last_out_edge_column = effect_info.m_trailing_out_edge_column;
1148 : : }
1149 : 1513 : }
1150 : :
1151 : : /* Print PS as HTML to XP, using DC and, if non-null EVENT_LABEL_WRITER. */
1152 : :
1153 : : static void
1154 : 3 : print_path_summary_as_html (const path_summary &ps,
1155 : : diagnostics::context &dc,
1156 : : xml::printer &xp,
1157 : : html_label_writer *event_label_writer,
1158 : : bool show_depths)
1159 : : {
1160 : 3 : std::vector<thread_event_printer> thread_event_printers;
1161 : 12 : for (auto t : ps.m_per_thread_summary)
1162 : 3 : thread_event_printers.push_back (thread_event_printer (*t, show_depths));
1163 : :
1164 : 3 : const logical_locations::manager *logical_loc_mgr
1165 : 3 : = dc.get_logical_location_manager ();
1166 : :
1167 : 3 : xp.push_tag_with_class ("div", "event-ranges", false);
1168 : :
1169 : : /* Group the ranges into stack frames. */
1170 : 3 : std::unique_ptr<stack_frame> curr_frame;
1171 : 3 : unsigned i;
1172 : 3 : event_range *range;
1173 : 3 : int last_out_edge_column = -1;
1174 : 10 : FOR_EACH_VEC_ELT (ps.m_ranges, i, range)
1175 : : {
1176 : 7 : const int swimlane_idx
1177 : 7 : = range->m_per_thread_summary.get_swimlane_index ();
1178 : :
1179 : 7 : const logical_locations::key this_logical_loc = range->m_logical_loc;
1180 : 7 : const int this_depth = range->m_stack_depth;
1181 : 7 : if (curr_frame)
1182 : : {
1183 : 4 : int old_stack_depth = curr_frame->m_stack_depth;
1184 : 4 : if (this_depth > curr_frame->m_stack_depth)
1185 : : {
1186 : 3 : emit_svg_arrow (xp, old_stack_depth, this_depth);
1187 : 3 : curr_frame
1188 : 6 : = begin_html_stack_frame (xp,
1189 : : std::move (curr_frame),
1190 : : range->m_logical_loc,
1191 : : range->m_stack_depth,
1192 : 3 : logical_loc_mgr);
1193 : : }
1194 : : else
1195 : : {
1196 : 2 : while (this_depth < curr_frame->m_stack_depth
1197 : 2 : || this_logical_loc != curr_frame->m_logical_loc)
1198 : : {
1199 : 2 : curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
1200 : 2 : if (curr_frame == nullptr)
1201 : : {
1202 : 1 : curr_frame
1203 : 1 : = begin_html_stack_frame (xp,
1204 : 2 : nullptr,
1205 : : range->m_logical_loc,
1206 : : range->m_stack_depth,
1207 : 1 : logical_loc_mgr);
1208 : 1 : break;
1209 : : }
1210 : : }
1211 : 1 : emit_svg_arrow (xp, old_stack_depth, this_depth);
1212 : : }
1213 : : }
1214 : : else
1215 : : {
1216 : 3 : curr_frame = begin_html_stack_frame (xp,
1217 : 6 : nullptr,
1218 : : range->m_logical_loc,
1219 : : range->m_stack_depth,
1220 : 3 : logical_loc_mgr);
1221 : : }
1222 : :
1223 : 7 : xp.push_tag_with_class ("table", "event-range-with-margin", false);
1224 : 7 : xp.push_tag ("tr", false);
1225 : 7 : xp.push_tag_with_class ("td", "event-range", false);
1226 : 7 : xp.push_tag_with_class ("div", "events-hdr", true);
1227 : 7 : if (range->m_logical_loc)
1228 : : {
1229 : 6 : gcc_assert (logical_loc_mgr);
1230 : 6 : label_text funcname
1231 : 6 : = logical_loc_mgr->get_name_for_path_output (range->m_logical_loc);
1232 : 6 : if (funcname.get ())
1233 : : {
1234 : 6 : xp.push_tag_with_class ("span", "funcname", true);
1235 : 6 : xp.add_text (funcname.get ());
1236 : 6 : xp.pop_tag ("span");
1237 : 6 : xp.add_text (": ");
1238 : : }
1239 : 6 : }
1240 : 7 : {
1241 : 7 : xp.push_tag_with_class ("span", "event-ids", true);
1242 : 7 : pretty_printer pp;
1243 : 7 : if (range->m_start_idx == range->m_end_idx)
1244 : 2 : pp_printf (&pp, "event %i",
1245 : : range->m_start_idx + 1);
1246 : : else
1247 : 5 : pp_printf (&pp, "events %i-%i",
1248 : : range->m_start_idx + 1, range->m_end_idx + 1);
1249 : 7 : xp.add_text_from_pp (pp);
1250 : 7 : xp.pop_tag ("span");
1251 : 7 : }
1252 : 7 : if (show_depths)
1253 : : {
1254 : 0 : xp.add_text (" ");
1255 : 0 : xp.push_tag_with_class ("span", "depth", true);
1256 : 0 : pretty_printer pp;
1257 : 0 : pp_printf (&pp, "(depth %i)", range->m_stack_depth);
1258 : 0 : xp.add_text_from_pp (pp);
1259 : 0 : xp.pop_tag ("span");
1260 : 0 : }
1261 : 7 : xp.pop_tag ("div");
1262 : :
1263 : : /* Print a run of events. */
1264 : 7 : thread_event_printer &tep = thread_event_printers[swimlane_idx];
1265 : : /* Wire up any trailing out-edge from previous range to leading in-edge
1266 : : of this range. */
1267 : 7 : diagnostics::source_effect_info effect_info;
1268 : 7 : effect_info.m_leading_in_edge_column = last_out_edge_column;
1269 : 7 : tep.print_swimlane_for_event_range_as_html (dc, xp, event_label_writer,
1270 : : range, &effect_info);
1271 : 7 : last_out_edge_column = effect_info.m_trailing_out_edge_column;
1272 : :
1273 : 7 : xp.pop_tag ("td");
1274 : 7 : xp.pop_tag ("tr");
1275 : 7 : xp.pop_tag ("table");
1276 : : }
1277 : :
1278 : : /* Close outstanding frames. */
1279 : 8 : while (curr_frame)
1280 : 5 : curr_frame = end_html_stack_frame (xp, std::move (curr_frame));
1281 : :
1282 : 3 : xp.pop_tag ("div");
1283 : 3 : }
1284 : :
1285 : : } /* end of anonymous namespace for path-printing code. */
1286 : :
1287 : 15322 : class element_event_desc : public pp_element
1288 : : {
1289 : : public:
1290 : 15322 : element_event_desc (const event &event_)
1291 : 15322 : : m_event (event_)
1292 : : {
1293 : : }
1294 : :
1295 : 15350 : void add_to_phase_2 (pp_markup::context &ctxt) final override
1296 : : {
1297 : 15350 : auto pp = ctxt.m_pp.clone ();
1298 : 15350 : m_event.print_desc (*pp.get ());
1299 : 15350 : pp_string (&ctxt.m_pp, pp_formatted_text (pp.get ()));
1300 : 15350 : }
1301 : :
1302 : : private:
1303 : : const event &m_event;
1304 : : };
1305 : :
1306 : : /* Print PATH according to the context's path_format. */
1307 : :
1308 : : void
1309 : 9658 : diagnostics::text_sink::print_path (const path &path_)
1310 : : {
1311 : 9658 : const unsigned num_events = path_.num_events ();
1312 : :
1313 : 9658 : switch (get_context ().get_path_format ())
1314 : : {
1315 : : case DPF_NONE:
1316 : : /* Do nothing. */
1317 : : return;
1318 : :
1319 : 9420 : case DPF_SEPARATE_EVENTS:
1320 : 9420 : {
1321 : : /* A note per event. */
1322 : 9420 : auto &logical_loc_mgr = path_.get_logical_location_manager ();
1323 : 24742 : for (unsigned i = 0; i < num_events; i++)
1324 : : {
1325 : 15322 : const event &ev = path_.get_event (i);
1326 : 15322 : element_event_desc e_event_desc (ev);
1327 : 15322 : diagnostic_event_id_t event_id (i);
1328 : 15322 : if (get_context ().show_path_depths_p ())
1329 : : {
1330 : 172 : int stack_depth = ev.get_stack_depth ();
1331 : : /* -fdiagnostics-path-format=separate-events doesn't print
1332 : : fndecl information, so with -fdiagnostics-show-path-depths
1333 : : print the fndecls too, if any. */
1334 : 172 : if (logical_locations::key logical_loc
1335 : 172 : = ev.get_logical_location ())
1336 : : {
1337 : 172 : label_text name
1338 : 172 : (logical_loc_mgr.get_name_for_path_output (logical_loc));
1339 : 172 : inform (ev.get_location (),
1340 : : "%@ %e (fndecl %qs, depth %i)",
1341 : : &event_id, &e_event_desc,
1342 : : name.get (), stack_depth);
1343 : 172 : }
1344 : : else
1345 : 0 : inform (ev.get_location (),
1346 : : "%@ %e (depth %i)",
1347 : : &event_id, &e_event_desc,
1348 : : stack_depth);
1349 : : }
1350 : : else
1351 : 15150 : inform (ev.get_location (),
1352 : : "%@ %e", &event_id, &e_event_desc);
1353 : 15322 : }
1354 : : }
1355 : : break;
1356 : :
1357 : 229 : case DPF_INLINE_EVENTS:
1358 : 229 : {
1359 : : /* Consolidate related events. */
1360 : 229 : path_print_policy policy (*this);
1361 : 229 : pretty_printer *const pp = get_printer ();
1362 : 229 : const bool check_rich_locations = true;
1363 : 229 : const bool colorize = pp_show_color (pp);
1364 : 229 : const bool show_event_links = m_source_printing.show_event_links_p;
1365 : 229 : path_summary summary (policy,
1366 : : *pp,
1367 : : path_,
1368 : : check_rich_locations,
1369 : : colorize,
1370 : 229 : show_event_links);
1371 : 229 : char *saved_prefix = pp_take_prefix (pp);
1372 : 229 : pp_set_prefix (pp, nullptr);
1373 : 229 : print_path_summary_as_text (summary, *this,
1374 : 229 : get_context ().show_path_depths_p ());
1375 : 229 : pp_flush (pp);
1376 : 229 : pp_set_prefix (pp, saved_prefix);
1377 : 229 : }
1378 : 229 : break;
1379 : : }
1380 : : }
1381 : :
1382 : : /* Print PATH_ as HTML to XP, using DC and DSPP for settings.
1383 : : If non-null, use EVENT_LABEL_WRITER when writing events. */
1384 : :
1385 : : void
1386 : 3 : diagnostics::print_path_as_html (xml::printer &xp,
1387 : : const path &path_,
1388 : : context &dc,
1389 : : html_label_writer *event_label_writer,
1390 : : const source_print_policy &dspp)
1391 : : {
1392 : 3 : path_print_policy policy (dc);
1393 : 3 : const bool check_rich_locations = true;
1394 : 3 : const bool colorize = false;
1395 : 3 : const source_printing_options &source_printing_opts
1396 : 3 : = dspp.get_options ();
1397 : 3 : const bool show_event_links = source_printing_opts.show_event_links_p;
1398 : 3 : path_summary summary (policy,
1399 : 3 : *dc.get_reference_printer (),
1400 : : path_,
1401 : : check_rich_locations,
1402 : : colorize,
1403 : 3 : show_event_links);
1404 : 3 : print_path_summary_as_html (summary, dc, xp, event_label_writer,
1405 : 3 : dc.show_path_depths_p ());
1406 : 3 : }
1407 : :
1408 : : #if CHECKING_P
1409 : :
1410 : : namespace diagnostics {
1411 : : namespace paths {
1412 : : namespace selftest {
1413 : :
1414 : : using location = ::selftest::location;
1415 : : using line_table_case = ::selftest::line_table_case;
1416 : : using line_table_test = ::selftest::line_table_test;
1417 : : using temp_source_file = ::selftest::temp_source_file;
1418 : :
1419 : : using test_context = diagnostics::selftest::test_context;
1420 : :
1421 : : /* Return true iff all events in PATH_ have locations for which column data
1422 : : is available, so that selftests that require precise string output can
1423 : : bail out for awkward line_table cases. */
1424 : :
1425 : : static bool
1426 : 1632 : path_events_have_column_data_p (const path &path_)
1427 : : {
1428 : 4388 : for (unsigned idx = 0; idx < path_.num_events (); idx++)
1429 : : {
1430 : 3416 : location_t event_loc = path_.get_event (idx).get_location ();
1431 : 3416 : if (line_table->get_pure_location (event_loc)
1432 : : > LINE_MAP_MAX_LOCATION_WITH_COLS)
1433 : : return false;
1434 : 2756 : if (line_table->get_start (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
1435 : : return false;
1436 : 2756 : if (line_table->get_finish (event_loc) > LINE_MAP_MAX_LOCATION_WITH_COLS)
1437 : : return false;
1438 : : }
1439 : : return true;
1440 : : }
1441 : :
1442 : : /* Verify that empty paths are handled gracefully. */
1443 : :
1444 : : static void
1445 : 4 : test_empty_path (pretty_printer *event_pp)
1446 : : {
1447 : 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1448 : 4 : test_path path (logical_loc_mgr, event_pp);
1449 : 4 : ASSERT_FALSE (path.interprocedural_p ());
1450 : :
1451 : 4 : test_context dc;
1452 : 4 : text_sink text_output (dc);
1453 : 4 : path_print_policy policy (text_output);
1454 : 4 : path_summary summary (policy, *event_pp, path, false);
1455 : 4 : ASSERT_EQ (summary.get_num_ranges (), 0);
1456 : :
1457 : 4 : print_path_summary_as_text (summary, text_output, true);
1458 : 4 : ASSERT_STREQ ("",
1459 : : pp_formatted_text (text_output.get_printer ()));
1460 : 4 : }
1461 : :
1462 : : /* Verify that print_path_summary works on a purely intraprocedural path. */
1463 : :
1464 : : static void
1465 : 4 : test_intraprocedural_path (pretty_printer *event_pp)
1466 : : {
1467 : 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1468 : 4 : test_path path (logical_loc_mgr, event_pp);
1469 : 4 : const char *const funcname = "foo";
1470 : 4 : path.add_event (UNKNOWN_LOCATION, funcname, 0, "first %qs", "free");
1471 : 4 : path.add_event (UNKNOWN_LOCATION, funcname, 0, "double %qs", "free");
1472 : :
1473 : 4 : ASSERT_FALSE (path.interprocedural_p ());
1474 : :
1475 : 4 : selftest::test_context dc;
1476 : 4 : text_sink text_output (dc);
1477 : 4 : path_print_policy policy (text_output);
1478 : 4 : path_summary summary (policy, *event_pp, path, false, false, false);
1479 : 4 : ASSERT_EQ (summary.get_num_ranges (), 1);
1480 : :
1481 : 4 : print_path_summary_as_text (summary, text_output, true);
1482 : 4 : ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
1483 : : " (1): first `free'\n"
1484 : : " (2): double `free'\n",
1485 : : pp_formatted_text (text_output.get_printer ()));
1486 : 4 : }
1487 : :
1488 : : /* Verify that print_path_summary works on an interprocedural path. */
1489 : :
1490 : : static void
1491 : 4 : test_interprocedural_path_1 (pretty_printer *event_pp)
1492 : : {
1493 : 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1494 : 4 : test_path path (logical_loc_mgr, event_pp);
1495 : 4 : path.add_entry ("test", 0);
1496 : 4 : path.add_call ("test", 0, "make_boxed_int");
1497 : 4 : path.add_call ("make_boxed_int", 1, "wrapped_malloc");
1498 : 4 : path.add_event (UNKNOWN_LOCATION,
1499 : : "wrapped_malloc", 2, "calling malloc");
1500 : 4 : path.add_return ("test", 0);
1501 : 4 : path.add_call ("test", 0, "free_boxed_int");
1502 : 4 : path.add_call ("free_boxed_int", 1, "wrapped_free");
1503 : 4 : path.add_event (UNKNOWN_LOCATION, "wrapped_free", 2, "calling free");
1504 : 4 : path.add_return ("test", 0);
1505 : 4 : path.add_call ("test", 0, "free_boxed_int");
1506 : 4 : path.add_call ("free_boxed_int", 1, "wrapped_free");
1507 : 4 : path.add_event (UNKNOWN_LOCATION, "wrapped_free", 2, "calling free");
1508 : 4 : ASSERT_EQ (path.num_events (), 18);
1509 : :
1510 : 4 : ASSERT_TRUE (path.interprocedural_p ());
1511 : :
1512 : 4 : {
1513 : 4 : selftest::test_context dc;
1514 : 4 : text_sink text_output (dc, nullptr, false);
1515 : 4 : path_print_policy policy (text_output);
1516 : 4 : path_summary summary (policy, *event_pp, path, false);
1517 : 4 : ASSERT_EQ (summary.get_num_ranges (), 9);
1518 : :
1519 : 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1520 : 4 : print_path_summary_as_text (summary, text_output, true);
1521 : 4 : ASSERT_STREQ
1522 : : (" `test': events 1-2 (depth 0)\n"
1523 : : " |\n"
1524 : : " | (1): entering `test'\n"
1525 : : " | (2): calling `make_boxed_int'\n"
1526 : : " |\n"
1527 : : " +--> `make_boxed_int': events 3-4 (depth 1)\n"
1528 : : " |\n"
1529 : : " | (3): entering `make_boxed_int'\n"
1530 : : " | (4): calling `wrapped_malloc'\n"
1531 : : " |\n"
1532 : : " +--> `wrapped_malloc': events 5-6 (depth 2)\n"
1533 : : " |\n"
1534 : : " | (5): entering `wrapped_malloc'\n"
1535 : : " | (6): calling malloc\n"
1536 : : " |\n"
1537 : : " <-------------+\n"
1538 : : " |\n"
1539 : : " `test': events 7-8 (depth 0)\n"
1540 : : " |\n"
1541 : : " | (7): returning to `test'\n"
1542 : : " | (8): calling `free_boxed_int'\n"
1543 : : " |\n"
1544 : : " +--> `free_boxed_int': events 9-10 (depth 1)\n"
1545 : : " |\n"
1546 : : " | (9): entering `free_boxed_int'\n"
1547 : : " | (10): calling `wrapped_free'\n"
1548 : : " |\n"
1549 : : " +--> `wrapped_free': events 11-12 (depth 2)\n"
1550 : : " |\n"
1551 : : " | (11): entering `wrapped_free'\n"
1552 : : " | (12): calling free\n"
1553 : : " |\n"
1554 : : " <-------------+\n"
1555 : : " |\n"
1556 : : " `test': events 13-14 (depth 0)\n"
1557 : : " |\n"
1558 : : " | (13): returning to `test'\n"
1559 : : " | (14): calling `free_boxed_int'\n"
1560 : : " |\n"
1561 : : " +--> `free_boxed_int': events 15-16 (depth 1)\n"
1562 : : " |\n"
1563 : : " | (15): entering `free_boxed_int'\n"
1564 : : " | (16): calling `wrapped_free'\n"
1565 : : " |\n"
1566 : : " +--> `wrapped_free': events 17-18 (depth 2)\n"
1567 : : " |\n"
1568 : : " | (17): entering `wrapped_free'\n"
1569 : : " | (18): calling free\n"
1570 : : " |\n",
1571 : : pp_formatted_text (text_output.get_printer ()));
1572 : 4 : }
1573 : 4 : {
1574 : 4 : selftest::test_context dc;
1575 : 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
1576 : 4 : text_sink text_output (dc);
1577 : 4 : path_print_policy policy (text_output);
1578 : 4 : path_summary summary (policy, *event_pp, path, false);
1579 : 4 : print_path_summary_as_text (summary, text_output, true);
1580 : 4 : ASSERT_STREQ
1581 : : (" `test': events 1-2 (depth 0)\n"
1582 : : " │\n"
1583 : : " │ (1): entering `test'\n"
1584 : : " │ (2): calling `make_boxed_int'\n"
1585 : : " │\n"
1586 : : " └──> `make_boxed_int': events 3-4 (depth 1)\n"
1587 : : " │\n"
1588 : : " │ (3): entering `make_boxed_int'\n"
1589 : : " │ (4): calling `wrapped_malloc'\n"
1590 : : " │\n"
1591 : : " └──> `wrapped_malloc': events 5-6 (depth 2)\n"
1592 : : " │\n"
1593 : : " │ (5): entering `wrapped_malloc'\n"
1594 : : " │ (6): calling malloc\n"
1595 : : " │\n"
1596 : : " <─────────────┘\n"
1597 : : " │\n"
1598 : : " `test': events 7-8 (depth 0)\n"
1599 : : " │\n"
1600 : : " │ (7): returning to `test'\n"
1601 : : " │ (8): calling `free_boxed_int'\n"
1602 : : " │\n"
1603 : : " └──> `free_boxed_int': events 9-10 (depth 1)\n"
1604 : : " │\n"
1605 : : " │ (9): entering `free_boxed_int'\n"
1606 : : " │ (10): calling `wrapped_free'\n"
1607 : : " │\n"
1608 : : " └──> `wrapped_free': events 11-12 (depth 2)\n"
1609 : : " │\n"
1610 : : " │ (11): entering `wrapped_free'\n"
1611 : : " │ (12): calling free\n"
1612 : : " │\n"
1613 : : " <─────────────┘\n"
1614 : : " │\n"
1615 : : " `test': events 13-14 (depth 0)\n"
1616 : : " │\n"
1617 : : " │ (13): returning to `test'\n"
1618 : : " │ (14): calling `free_boxed_int'\n"
1619 : : " │\n"
1620 : : " └──> `free_boxed_int': events 15-16 (depth 1)\n"
1621 : : " │\n"
1622 : : " │ (15): entering `free_boxed_int'\n"
1623 : : " │ (16): calling `wrapped_free'\n"
1624 : : " │\n"
1625 : : " └──> `wrapped_free': events 17-18 (depth 2)\n"
1626 : : " │\n"
1627 : : " │ (17): entering `wrapped_free'\n"
1628 : : " │ (18): calling free\n"
1629 : : " │\n",
1630 : : pp_formatted_text (text_output.get_printer ()));
1631 : 4 : }
1632 : :
1633 : 4 : }
1634 : :
1635 : : /* Example where we pop the stack to an intermediate frame, rather than the
1636 : : initial one. */
1637 : :
1638 : : static void
1639 : 4 : test_interprocedural_path_2 (pretty_printer *event_pp)
1640 : : {
1641 : 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1642 : 4 : test_path path (logical_loc_mgr, event_pp);
1643 : 4 : path.add_entry ("foo", 0);
1644 : 4 : path.add_call ("foo", 0, "bar");
1645 : 4 : path.add_call ("bar", 1, "baz");
1646 : 4 : path.add_return ("bar", 1);
1647 : 4 : path.add_call ("bar", 1, "baz");
1648 : 4 : ASSERT_EQ (path.num_events (), 8);
1649 : :
1650 : 4 : ASSERT_TRUE (path.interprocedural_p ());
1651 : :
1652 : 4 : {
1653 : 4 : selftest::test_context dc;
1654 : 4 : text_sink text_output (dc);
1655 : 4 : path_print_policy policy (text_output);
1656 : 4 : path_summary summary (policy, *event_pp, path, false);
1657 : 4 : ASSERT_EQ (summary.get_num_ranges (), 5);
1658 : 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1659 : 4 : print_path_summary_as_text (summary, text_output, true);
1660 : 4 : ASSERT_STREQ
1661 : : (" `foo': events 1-2 (depth 0)\n"
1662 : : " |\n"
1663 : : " | (1): entering `foo'\n"
1664 : : " | (2): calling `bar'\n"
1665 : : " |\n"
1666 : : " +--> `bar': events 3-4 (depth 1)\n"
1667 : : " |\n"
1668 : : " | (3): entering `bar'\n"
1669 : : " | (4): calling `baz'\n"
1670 : : " |\n"
1671 : : " +--> `baz': event 5 (depth 2)\n"
1672 : : " |\n"
1673 : : " | (5): entering `baz'\n"
1674 : : " |\n"
1675 : : " <------+\n"
1676 : : " |\n"
1677 : : " `bar': events 6-7 (depth 1)\n"
1678 : : " |\n"
1679 : : " | (6): returning to `bar'\n"
1680 : : " | (7): calling `baz'\n"
1681 : : " |\n"
1682 : : " +--> `baz': event 8 (depth 2)\n"
1683 : : " |\n"
1684 : : " | (8): entering `baz'\n"
1685 : : " |\n",
1686 : : pp_formatted_text (text_output.get_printer ()));
1687 : 4 : }
1688 : 4 : {
1689 : 4 : selftest::test_context dc;
1690 : 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
1691 : 4 : text_sink text_output (dc);
1692 : 4 : path_print_policy policy (text_output);
1693 : 4 : path_summary summary (policy, *event_pp, path, false);
1694 : 4 : print_path_summary_as_text (summary, text_output, true);
1695 : 4 : ASSERT_STREQ
1696 : : (" `foo': events 1-2 (depth 0)\n"
1697 : : " │\n"
1698 : : " │ (1): entering `foo'\n"
1699 : : " │ (2): calling `bar'\n"
1700 : : " │\n"
1701 : : " └──> `bar': events 3-4 (depth 1)\n"
1702 : : " │\n"
1703 : : " │ (3): entering `bar'\n"
1704 : : " │ (4): calling `baz'\n"
1705 : : " │\n"
1706 : : " └──> `baz': event 5 (depth 2)\n"
1707 : : " │\n"
1708 : : " │ (5): entering `baz'\n"
1709 : : " │\n"
1710 : : " <──────┘\n"
1711 : : " │\n"
1712 : : " `bar': events 6-7 (depth 1)\n"
1713 : : " │\n"
1714 : : " │ (6): returning to `bar'\n"
1715 : : " │ (7): calling `baz'\n"
1716 : : " │\n"
1717 : : " └──> `baz': event 8 (depth 2)\n"
1718 : : " │\n"
1719 : : " │ (8): entering `baz'\n"
1720 : : " │\n",
1721 : : pp_formatted_text (text_output.get_printer ()));
1722 : 4 : }
1723 : 4 : }
1724 : :
1725 : : /* Verify that print_path_summary is sane in the face of a recursive
1726 : : diagnostic path. */
1727 : :
1728 : : static void
1729 : 4 : test_recursion (pretty_printer *event_pp)
1730 : : {
1731 : 4 : logical_locations::selftest::test_manager logical_loc_mgr;
1732 : 4 : test_path path (logical_loc_mgr, event_pp);
1733 : 4 : path.add_entry ("factorial", 0);
1734 : 16 : for (int depth = 0; depth < 3; depth++)
1735 : 12 : path.add_call ("factorial", depth, "factorial");
1736 : 4 : ASSERT_EQ (path.num_events (), 7);
1737 : :
1738 : 4 : ASSERT_TRUE (path.interprocedural_p ());
1739 : :
1740 : 4 : {
1741 : 4 : selftest::test_context dc;
1742 : 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1743 : :
1744 : 4 : text_sink text_output (dc);
1745 : 4 : path_print_policy policy (text_output);
1746 : 4 : path_summary summary (policy, *event_pp, path, false);
1747 : 4 : ASSERT_EQ (summary.get_num_ranges (), 4);
1748 : :
1749 : 4 : print_path_summary_as_text (summary, text_output, true);
1750 : 4 : ASSERT_STREQ
1751 : : (" `factorial': events 1-2 (depth 0)\n"
1752 : : " |\n"
1753 : : " | (1): entering `factorial'\n"
1754 : : " | (2): calling `factorial'\n"
1755 : : " |\n"
1756 : : " +--> `factorial': events 3-4 (depth 1)\n"
1757 : : " |\n"
1758 : : " | (3): entering `factorial'\n"
1759 : : " | (4): calling `factorial'\n"
1760 : : " |\n"
1761 : : " +--> `factorial': events 5-6 (depth 2)\n"
1762 : : " |\n"
1763 : : " | (5): entering `factorial'\n"
1764 : : " | (6): calling `factorial'\n"
1765 : : " |\n"
1766 : : " +--> `factorial': event 7 (depth 3)\n"
1767 : : " |\n"
1768 : : " | (7): entering `factorial'\n"
1769 : : " |\n",
1770 : : pp_formatted_text (text_output.get_printer ()));
1771 : 4 : }
1772 : 4 : {
1773 : 4 : selftest::test_context dc;
1774 : 4 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
1775 : :
1776 : 4 : text_sink text_output (dc);
1777 : 4 : path_print_policy policy (text_output);
1778 : 4 : path_summary summary (policy, *event_pp, path, false);
1779 : 4 : print_path_summary_as_text (summary, text_output, true);
1780 : 4 : ASSERT_STREQ
1781 : : (" `factorial': events 1-2 (depth 0)\n"
1782 : : " │\n"
1783 : : " │ (1): entering `factorial'\n"
1784 : : " │ (2): calling `factorial'\n"
1785 : : " │\n"
1786 : : " └──> `factorial': events 3-4 (depth 1)\n"
1787 : : " │\n"
1788 : : " │ (3): entering `factorial'\n"
1789 : : " │ (4): calling `factorial'\n"
1790 : : " │\n"
1791 : : " └──> `factorial': events 5-6 (depth 2)\n"
1792 : : " │\n"
1793 : : " │ (5): entering `factorial'\n"
1794 : : " │ (6): calling `factorial'\n"
1795 : : " │\n"
1796 : : " └──> `factorial': event 7 (depth 3)\n"
1797 : : " │\n"
1798 : : " │ (7): entering `factorial'\n"
1799 : : " │\n",
1800 : : pp_formatted_text (text_output.get_printer ()));
1801 : 4 : }
1802 : 4 : }
1803 : :
1804 : : /* Helper class for writing tests of control flow visualization. */
1805 : :
1806 : 576 : class control_flow_test
1807 : : {
1808 : : public:
1809 : 576 : control_flow_test (const selftest::location &loc,
1810 : : const line_table_case &case_,
1811 : : const char *content)
1812 : 576 : : m_tmp_file (loc, ".c", content,
1813 : : /* gcc_rich_location::add_location_if_nearby implicitly
1814 : : uses global_dc's file_cache, so we need to evict
1815 : : tmp when we're done. */
1816 : 576 : &global_dc->get_file_cache ()),
1817 : 576 : m_ltt (case_)
1818 : : {
1819 : 576 : m_ord_map
1820 : 576 : = linemap_check_ordinary (linemap_add (line_table, LC_ENTER, false,
1821 : : m_tmp_file.get_filename (), 0));
1822 : 576 : linemap_line_start (line_table, 1, 100);
1823 : 576 : }
1824 : :
1825 : 10272 : location_t get_line_and_column (int line, int column)
1826 : : {
1827 : 10272 : return linemap_position_for_line_and_column (line_table, m_ord_map,
1828 : : line, column);
1829 : : }
1830 : :
1831 : 2592 : location_t get_line_and_columns (int line, int first_column, int last_column)
1832 : : {
1833 : 2592 : return get_line_and_columns (line,
1834 : : first_column, first_column, last_column);
1835 : : }
1836 : :
1837 : 3264 : location_t get_line_and_columns (int line,
1838 : : int first_column,
1839 : : int caret_column,
1840 : : int last_column)
1841 : : {
1842 : 3264 : return make_location (get_line_and_column (line, caret_column),
1843 : : get_line_and_column (line, first_column),
1844 : 3264 : get_line_and_column (line, last_column));
1845 : : }
1846 : :
1847 : : private:
1848 : : temp_source_file m_tmp_file;
1849 : : line_table_test m_ltt;
1850 : : const line_map_ordinary *m_ord_map;
1851 : : };
1852 : :
1853 : : /* Example of event edges where all events can go in the same layout,
1854 : : testing the 6 combinations of:
1855 : : - ASCII vs Unicode vs event links off
1856 : : - line numbering on and off. */
1857 : :
1858 : : static void
1859 : 96 : test_control_flow_1 (const line_table_case &case_,
1860 : : pretty_printer *event_pp)
1861 : : {
1862 : : /* Create a tempfile and write some text to it.
1863 : : ...000000000111111111122222222223333333333.
1864 : : ...123456789012345678901234567890123456789. */
1865 : 96 : const char *content
1866 : : = ("int test (int *p)\n" /* line 1. */
1867 : : "{\n" /* line 2. */
1868 : : " if (p)\n" /* line 3. */
1869 : : " return 0;\n" /* line 4. */
1870 : : " return *p;\n" /* line 5. */
1871 : : "}\n"); /* line 6. */
1872 : :
1873 : 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
1874 : :
1875 : 96 : const location_t conditional = t.get_line_and_column (3, 7);
1876 : 96 : const location_t cfg_dest = t.get_line_and_column (5, 10);
1877 : :
1878 : 96 : logical_locations::selftest::test_manager logical_loc_mgr;
1879 : 96 : test_path path (logical_loc_mgr, event_pp);
1880 : 96 : path.add_event (conditional, nullptr, 0,
1881 : : "following %qs branch (when %qs is NULL)...",
1882 : : "false", "p");
1883 : 96 : path.connect_to_next_event ();
1884 : :
1885 : 96 : path.add_event (cfg_dest, nullptr, 0,
1886 : : "...to here");
1887 : 96 : path.add_event (cfg_dest, nullptr, 0,
1888 : : "dereference of NULL %qs",
1889 : : "p");
1890 : :
1891 : 96 : if (!path_events_have_column_data_p (path))
1892 : 40 : return;
1893 : :
1894 : :
1895 : 56 : {
1896 : 56 : selftest::test_context dc;
1897 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1898 : 56 : dc.show_event_links (true);
1899 : 56 : text_sink text_output (dc);
1900 : 56 : path_print_policy policy (text_output);
1901 : 56 : path_summary summary (policy, *event_pp, path, true);
1902 : 56 : print_path_summary_as_text (summary, text_output, false);
1903 : 56 : ASSERT_STREQ
1904 : : (" events 1-3\n"
1905 : : "FILENAME:3:7:\n"
1906 : : " if (p)\n"
1907 : : " ^\n"
1908 : : " |\n"
1909 : : " (1) following `false' branch (when `p' is NULL)... ->-+\n"
1910 : : " |\n"
1911 : : "FILENAME:5:10:\n"
1912 : : " |\n"
1913 : : "+------------------------------------------------------------+\n"
1914 : : "| return *p;\n"
1915 : : "| ~\n"
1916 : : "| |\n"
1917 : : "+-------->(2) ...to here\n"
1918 : : " (3) dereference of NULL `p'\n",
1919 : : pp_formatted_text (text_output.get_printer ()));
1920 : 56 : }
1921 : 56 : {
1922 : 56 : selftest::test_context dc;
1923 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1924 : 56 : dc.show_event_links (false);
1925 : 56 : text_sink text_output (dc);
1926 : 56 : path_print_policy policy (text_output);
1927 : 56 : path_summary summary (policy, *event_pp, path, true);
1928 : 56 : print_path_summary_as_text (summary, text_output, false);
1929 : 56 : ASSERT_STREQ
1930 : : (" events 1-3\n"
1931 : : "FILENAME:3:7:\n"
1932 : : " if (p)\n"
1933 : : " ^\n"
1934 : : " |\n"
1935 : : " (1) following `false' branch (when `p' is NULL)...\n"
1936 : : "FILENAME:5:10:\n"
1937 : : " return *p;\n"
1938 : : " ~\n"
1939 : : " |\n"
1940 : : " (2) ...to here\n"
1941 : : " (3) dereference of NULL `p'\n",
1942 : : pp_formatted_text (text_output.get_printer ()));
1943 : 56 : }
1944 : 56 : {
1945 : 56 : selftest::test_context dc;
1946 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1947 : 56 : dc.show_line_numbers (true);
1948 : 56 : dc.show_event_links (true);
1949 : 56 : text_sink text_output (dc);
1950 : 56 : path_print_policy policy (text_output);
1951 : 56 : path_summary summary (policy, *event_pp, path, true);
1952 : 56 : print_path_summary_as_text (summary, text_output, false);
1953 : 56 : ASSERT_STREQ
1954 : : (" events 1-3\n"
1955 : : "FILENAME:3:7:\n"
1956 : : " 3 | if (p)\n"
1957 : : " | ^\n"
1958 : : " | |\n"
1959 : : " | (1) following `false' branch (when `p' is NULL)... ->-+\n"
1960 : : " | |\n"
1961 : : " | |\n"
1962 : : " |+------------------------------------------------------------+\n"
1963 : : " 4 || return 0;\n"
1964 : : " 5 || return *p;\n"
1965 : : " || ~\n"
1966 : : " || |\n"
1967 : : " |+-------->(2) ...to here\n"
1968 : : " | (3) dereference of NULL `p'\n",
1969 : : pp_formatted_text (text_output.get_printer ()));
1970 : 56 : }
1971 : 56 : {
1972 : 56 : selftest::test_context dc;
1973 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
1974 : 56 : dc.show_line_numbers (true);
1975 : 56 : dc.show_event_links (false);
1976 : 56 : text_sink text_output (dc);
1977 : 56 : path_print_policy policy (text_output);
1978 : 56 : path_summary summary (policy, *event_pp, path, true);
1979 : 56 : print_path_summary_as_text (summary, text_output, false);
1980 : 56 : ASSERT_STREQ
1981 : : (" events 1-3\n"
1982 : : "FILENAME:3:7:\n"
1983 : : " 3 | if (p)\n"
1984 : : " | ^\n"
1985 : : " | |\n"
1986 : : " | (1) following `false' branch (when `p' is NULL)...\n"
1987 : : " 4 | return 0;\n"
1988 : : " 5 | return *p;\n"
1989 : : " | ~\n"
1990 : : " | |\n"
1991 : : " | (2) ...to here\n"
1992 : : " | (3) dereference of NULL `p'\n",
1993 : : pp_formatted_text (text_output.get_printer ()));
1994 : 56 : }
1995 : 56 : {
1996 : 56 : selftest::test_context dc;
1997 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
1998 : 56 : dc.show_event_links (true);
1999 : 56 : text_sink text_output (dc);
2000 : 56 : path_print_policy policy (text_output);
2001 : 56 : path_summary summary (policy, *event_pp, path, true);
2002 : 56 : print_path_summary_as_text (summary, text_output, false);
2003 : 56 : ASSERT_STREQ
2004 : : (" events 1-3\n"
2005 : : "FILENAME:3:7:\n"
2006 : : " if (p)\n"
2007 : : " ^\n"
2008 : : " |\n"
2009 : : " (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
2010 : : " │\n"
2011 : : "FILENAME:5:10:\n"
2012 : : " │\n"
2013 : : "┌────────────────────────────────────────────────────────────┘\n"
2014 : : "│ return *p;\n"
2015 : : "│ ~\n"
2016 : : "│ |\n"
2017 : : "└────────>(2) ...to here\n"
2018 : : " (3) dereference of NULL `p'\n",
2019 : : pp_formatted_text (text_output.get_printer ()));
2020 : 56 : }
2021 : 56 : {
2022 : 56 : selftest::test_context dc;
2023 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_UNICODE);
2024 : 56 : dc.show_event_links (true);
2025 : 56 : dc.show_line_numbers (true);
2026 : 56 : text_sink text_output (dc);
2027 : 56 : path_print_policy policy (text_output);
2028 : 56 : path_summary summary (policy, *event_pp, path, true);
2029 : 56 : print_path_summary_as_text (summary, text_output, false);
2030 : 56 : ASSERT_STREQ
2031 : : (" events 1-3\n"
2032 : : "FILENAME:3:7:\n"
2033 : : " 3 | if (p)\n"
2034 : : " | ^\n"
2035 : : " | |\n"
2036 : : " | (1) following `false' branch (when `p' is NULL)... ─>─┐\n"
2037 : : " | │\n"
2038 : : " | │\n"
2039 : : " |┌────────────────────────────────────────────────────────────┘\n"
2040 : : " 4 |│ return 0;\n"
2041 : : " 5 |│ return *p;\n"
2042 : : " |│ ~\n"
2043 : : " |│ |\n"
2044 : : " |└────────>(2) ...to here\n"
2045 : : " | (3) dereference of NULL `p'\n",
2046 : : pp_formatted_text (text_output.get_printer ()));
2047 : 56 : }
2048 : 192 : }
2049 : :
2050 : : /* Complex example involving a backedge. */
2051 : :
2052 : : static void
2053 : 96 : test_control_flow_2 (const line_table_case &case_,
2054 : : pretty_printer *event_pp)
2055 : : {
2056 : : /* Create a tempfile and write some text to it.
2057 : : ...000000000111111111122222222223333333333.
2058 : : ...123456789012345678901234567890123456789. */
2059 : 96 : const char *content
2060 : : = ("int for_loop_noop_next (struct node *n)\n" /* <--------- line 1. */
2061 : : "{\n" /* <----------------------------------------------- line 2. */
2062 : : " int sum = 0;\n" /* <---------------------------------- line 3. */
2063 : : " for (struct node *iter = n; iter; iter->next)\n" /* <- line 4. */
2064 : : " sum += n->val;\n" /* <------------------------------ line 5. */
2065 : : " return sum;\n" /* <----------------------------------- line 6. */
2066 : : "}\n"); /* <-------------------------------------------- line 7. */
2067 : : /* Adapted from infinite-loop-linked-list.c where
2068 : : "iter->next" should be "iter = iter->next". */
2069 : :
2070 : 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2071 : :
2072 : 96 : const location_t iter_test = t.get_line_and_columns (4, 31, 34);
2073 : 96 : const location_t loop_body_start = t.get_line_and_columns (5, 12, 17);
2074 : 96 : const location_t loop_body_end = t.get_line_and_columns (5, 5, 9, 17);
2075 : :
2076 : 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2077 : 96 : test_path path (logical_loc_mgr, event_pp);
2078 : 96 : path.add_event (iter_test, nullptr, 0, "infinite loop here");
2079 : :
2080 : 96 : path.add_event (iter_test, nullptr, 0, "looping from here...");
2081 : 96 : path.connect_to_next_event ();
2082 : :
2083 : 96 : path.add_event (loop_body_start, nullptr, 0, "...to here");
2084 : :
2085 : 96 : path.add_event (loop_body_end, nullptr, 0, "looping back...");
2086 : 96 : path.connect_to_next_event ();
2087 : :
2088 : 96 : path.add_event (iter_test, nullptr, 0, "...to here");
2089 : :
2090 : 96 : if (!path_events_have_column_data_p (path))
2091 : 40 : return;
2092 : :
2093 : 56 : {
2094 : 56 : selftest::test_context dc;
2095 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2096 : 56 : dc.show_event_links (true);
2097 : 56 : dc.show_line_numbers (true);
2098 : 56 : text_sink text_output (dc);
2099 : 56 : path_print_policy policy (text_output);
2100 : 56 : path_summary summary (policy, *event_pp, path, true);
2101 : 56 : print_path_summary_as_text (summary, text_output, false);
2102 : 56 : ASSERT_STREQ
2103 : : (" events 1-3\n"
2104 : : "FILENAME:4:31:\n"
2105 : : " 4 | for (struct node *iter = n; iter; iter->next)\n"
2106 : : " | ^~~~\n"
2107 : : " | |\n"
2108 : : " | (1) infinite loop here\n"
2109 : : " | (2) looping from here... ->-+\n"
2110 : : " | |\n"
2111 : : " | |\n"
2112 : : " |+----------------------------------------------------------+\n"
2113 : : " 5 || sum += n->val;\n"
2114 : : " || ~~~~~~ \n"
2115 : : " || |\n"
2116 : : " |+---------->(3) ...to here\n"
2117 : : /* We need to start an new event_range here as event (4) is to the
2118 : : left of event (3), and thus (4) would mess up the in-edge to (3). */
2119 : : " event 4\n"
2120 : : " 5 | sum += n->val;\n"
2121 : : " | ~~~~^~~~~~~~~\n"
2122 : : " | |\n"
2123 : : " | (4) looping back... ->-+\n"
2124 : : " | |\n"
2125 : : /* We need to start an new event_range here as event (4) with an
2126 : : out-edge is on a later line (line 5) than its destination event (5),
2127 : : on line 4. */
2128 : : " event 5\n"
2129 : : " | |\n"
2130 : : " |+-------------------------------+\n"
2131 : : " 4 || for (struct node *iter = n; iter; iter->next)\n"
2132 : : " || ^~~~\n"
2133 : : " || |\n"
2134 : : " |+----------------------------->(5) ...to here\n",
2135 : : pp_formatted_text (text_output.get_printer ()));
2136 : 56 : }
2137 : 192 : }
2138 : :
2139 : : /* Complex example involving a backedge and both an in-edge and out-edge
2140 : : on the same line. */
2141 : :
2142 : : static void
2143 : 96 : test_control_flow_3 (const line_table_case &case_,
2144 : : pretty_printer *event_pp)
2145 : : {
2146 : : /* Create a tempfile and write some text to it.
2147 : : ...000000000111111111122222222223333333333.
2148 : : ...123456789012345678901234567890123456789. */
2149 : 96 : const char *content
2150 : : = ("void test_missing_comparison_in_for_condition_1 (int n)\n"
2151 : : "{\n" /* <------------------------- line 2. */
2152 : : " for (int i = 0; n; i++)\n" /* <- line 3. */
2153 : : " {\n" /* <--------------------- line 4. */
2154 : : " }\n" /* <--------------------- line 5. */
2155 : : "}\n"); /* <----------------------- line 6. */
2156 : : /* Adapted from infinite-loop-1.c where the condition should have been
2157 : : "i < n", rather than just "n". */
2158 : :
2159 : 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2160 : :
2161 : 96 : const location_t iter_test = t.get_line_and_column (3, 19);
2162 : 96 : const location_t iter_next = t.get_line_and_columns (3, 22, 24);
2163 : :
2164 : 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2165 : 96 : test_path path (logical_loc_mgr, event_pp);
2166 : 96 : path.add_event (iter_test, nullptr, 0, "infinite loop here");
2167 : :
2168 : 96 : path.add_event (iter_test, nullptr, 0, "looping from here...");
2169 : 96 : path.connect_to_next_event ();
2170 : :
2171 : 96 : path.add_event (iter_next, nullptr, 0, "...to here");
2172 : :
2173 : 96 : path.add_event (iter_next, nullptr, 0, "looping back...");
2174 : 96 : path.connect_to_next_event ();
2175 : :
2176 : 96 : path.add_event (iter_test, nullptr, 0, "...to here");
2177 : :
2178 : 96 : if (!path_events_have_column_data_p (path))
2179 : 36 : return;
2180 : :
2181 : 60 : {
2182 : 60 : selftest::test_context dc;
2183 : 60 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2184 : 60 : dc.show_event_links (true);
2185 : 60 : dc.show_line_numbers (true);
2186 : 60 : text_sink text_output (dc);
2187 : 60 : path_print_policy policy (text_output);
2188 : 60 : path_summary summary (policy, *event_pp, path, true);
2189 : 60 : print_path_summary_as_text (summary, text_output, false);
2190 : 60 : ASSERT_STREQ
2191 : : (" events 1-2\n"
2192 : : "FILENAME:3:19:\n"
2193 : : " 3 | for (int i = 0; n; i++)\n"
2194 : : " | ^\n"
2195 : : " | |\n"
2196 : : " | (1) infinite loop here\n"
2197 : : " | (2) looping from here... ->-+\n"
2198 : : " | |\n"
2199 : : " events 3-4\n"
2200 : : " | |\n"
2201 : : " |+----------------------------------------------+\n"
2202 : : " 3 || for (int i = 0; n; i++)\n"
2203 : : " || ^~~\n"
2204 : : " || |\n"
2205 : : " |+-------------------->(3) ...to here\n"
2206 : : " | (4) looping back... ->-+\n"
2207 : : " | |\n"
2208 : : /* We need to start an new event_range here as event (4) with an
2209 : : out-edge is on the same line as its destination event (5), but
2210 : : to the right, which we can't handle as a single event_range. */
2211 : : " event 5\n"
2212 : : " | |\n"
2213 : : " |+--------------------------------------------+\n"
2214 : : " 3 || for (int i = 0; n; i++)\n"
2215 : : " || ^\n"
2216 : : " || |\n"
2217 : : " |+----------------->(5) ...to here\n",
2218 : : pp_formatted_text (text_output.get_printer ()));
2219 : 60 : }
2220 : 192 : }
2221 : :
2222 : : /* Implementation of ASSERT_CFG_EDGE_PATH_STREQ. */
2223 : :
2224 : : static void
2225 : 1152 : assert_cfg_edge_path_streq (const location &loc,
2226 : : pretty_printer *event_pp,
2227 : : const location_t src_loc,
2228 : : const location_t dst_loc,
2229 : : const char *expected_str)
2230 : : {
2231 : 1152 : logical_locations::selftest::test_manager logical_loc_mgr;
2232 : 1152 : test_path path (logical_loc_mgr, event_pp);
2233 : 1152 : path.add_event (src_loc, nullptr, 0, "from here...");
2234 : 1152 : path.connect_to_next_event ();
2235 : :
2236 : 1152 : path.add_event (dst_loc, nullptr, 0, "...to here");
2237 : :
2238 : 1152 : if (!path_events_have_column_data_p (path))
2239 : 464 : return;
2240 : :
2241 : 688 : selftest::test_context dc;
2242 : 688 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2243 : 688 : dc.show_event_links (true);
2244 : 688 : dc.show_line_numbers (true);
2245 : 688 : text_sink text_output (dc);
2246 : 688 : path_print_policy policy (text_output);
2247 : 688 : path_summary summary (policy, *event_pp, path, true);
2248 : 688 : print_path_summary_as_text (summary, text_output, false);
2249 : 688 : ASSERT_STREQ_AT (loc, expected_str,
2250 : : pp_formatted_text (text_output.get_printer ()));
2251 : 1152 : }
2252 : :
2253 : : /* Assert that if we make a path with an event with "from here..." at SRC_LOC
2254 : : leading to an event "...to here" at DST_LOC that we print the path
2255 : : as EXPECTED_STR. */
2256 : :
2257 : : #define ASSERT_CFG_EDGE_PATH_STREQ(SRC_LOC, DST_LOC, EXPECTED_STR) \
2258 : : assert_cfg_edge_path_streq ((SELFTEST_LOCATION), (event_pp), \
2259 : : (SRC_LOC), (DST_LOC), (EXPECTED_STR))
2260 : :
2261 : : /* Various examples of edge, trying to cover all combinations of:
2262 : : - relative x positive of src label and dst label
2263 : : - relative y position of labels:
2264 : : - on same line
2265 : : - on next line
2266 : : - on line after next
2267 : : - big gap, where src is before dst
2268 : : - big gap, where src is after dst
2269 : : and other awkward cases. */
2270 : :
2271 : : static void
2272 : 96 : test_control_flow_4 (const line_table_case &case_,
2273 : : pretty_printer *event_pp)
2274 : : {
2275 : 96 : std::string many_lines;
2276 : 9696 : for (int i = 1; i <= 100; i++)
2277 : : /* ............000000000111
2278 : : ............123456789012. */
2279 : 9600 : many_lines += "LHS RHS\n";
2280 : 96 : control_flow_test t (SELFTEST_LOCATION, case_, many_lines.c_str ());
2281 : :
2282 : : /* Same line. */
2283 : 96 : {
2284 : : /* LHS -> RHS. */
2285 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2286 : : (t.get_line_and_columns (3, 1, 3),
2287 : : t.get_line_and_columns (3, 10, 12),
2288 : : (" event 1\n"
2289 : : "FILENAME:3:1:\n"
2290 : : " 3 | LHS RHS\n"
2291 : : " | ^~~\n"
2292 : : " | |\n"
2293 : : " | (1) from here... ->-+\n"
2294 : : " | |\n"
2295 : : " event 2\n"
2296 : : " | |\n"
2297 : : " |+--------------------+\n"
2298 : : " 3 ||LHS RHS\n"
2299 : : " || ^~~\n"
2300 : : " || |\n"
2301 : : " |+-------->(2) ...to here\n"));
2302 : :
2303 : : /* RHS -> LHS. */
2304 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2305 : : (t.get_line_and_columns (3, 10, 12),
2306 : : t.get_line_and_columns (3, 1, 3),
2307 : : (" event 1\n"
2308 : : "FILENAME:3:10:\n"
2309 : : " 3 | LHS RHS\n"
2310 : : " | ^~~\n"
2311 : : " | |\n"
2312 : : " | (1) from here... ->-+\n"
2313 : : " | |\n"
2314 : : " event 2\n"
2315 : : " | |\n"
2316 : : " |+-----------------------------+\n"
2317 : : " 3 ||LHS RHS\n"
2318 : : " ||^~~\n"
2319 : : " |||\n"
2320 : : " |+(2) ...to here\n"));
2321 : : }
2322 : :
2323 : : /* Next line. */
2324 : 96 : {
2325 : : /* LHS -> RHS. */
2326 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2327 : : (t.get_line_and_columns (3, 1, 3),
2328 : : t.get_line_and_columns (4, 5, 7),
2329 : : (" events 1-2\n"
2330 : : "FILENAME:3:1:\n"
2331 : : " 3 | LHS RHS\n"
2332 : : " | ^~~\n"
2333 : : " | |\n"
2334 : : " | (1) from here... ->-+\n"
2335 : : " | |\n"
2336 : : " | |\n"
2337 : : " |+--------------------+\n"
2338 : : " 4 ||LHS RHS\n"
2339 : : " || ~~~\n"
2340 : : " || |\n"
2341 : : " |+--->(2) ...to here\n"));
2342 : :
2343 : : /* RHS -> LHS. */
2344 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2345 : : (t.get_line_and_columns (3, 10, 12),
2346 : : t.get_line_and_columns (4, 1, 3),
2347 : : (" events 1-2\n"
2348 : : "FILENAME:3:10:\n"
2349 : : " 3 | LHS RHS\n"
2350 : : " | ^~~\n"
2351 : : " | |\n"
2352 : : " | (1) from here... ->-+\n"
2353 : : " | |\n"
2354 : : " | |\n"
2355 : : " |+-----------------------------+\n"
2356 : : " 4 ||LHS RHS\n"
2357 : : " ||~~~ \n"
2358 : : " |||\n"
2359 : : " |+(2) ...to here\n"));
2360 : : }
2361 : :
2362 : : /* Line after next. */
2363 : 96 : {
2364 : : /* LHS -> RHS. */
2365 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2366 : : (t.get_line_and_columns (3, 1, 3),
2367 : : t.get_line_and_columns (5, 10, 12),
2368 : : (" events 1-2\n"
2369 : : "FILENAME:3:1:\n"
2370 : : " 3 | LHS RHS\n"
2371 : : " | ^~~\n"
2372 : : " | |\n"
2373 : : " | (1) from here... ->-+\n"
2374 : : " | |\n"
2375 : : " | |\n"
2376 : : " |+--------------------+\n"
2377 : : " 4 ||LHS RHS\n"
2378 : : " 5 ||LHS RHS\n"
2379 : : " || ~~~\n"
2380 : : " || |\n"
2381 : : " |+-------->(2) ...to here\n"));
2382 : :
2383 : : /* RHS -> LHS. */
2384 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2385 : : (t.get_line_and_columns (3, 10, 12),
2386 : : t.get_line_and_columns (5, 1, 3),
2387 : : (" events 1-2\n"
2388 : : "FILENAME:3:10:\n"
2389 : : " 3 | LHS RHS\n"
2390 : : " | ^~~\n"
2391 : : " | |\n"
2392 : : " | (1) from here... ->-+\n"
2393 : : " | |\n"
2394 : : " | |\n"
2395 : : " |+-----------------------------+\n"
2396 : : " 4 ||LHS RHS\n"
2397 : : " 5 ||LHS RHS\n"
2398 : : " ||~~~ \n"
2399 : : " |||\n"
2400 : : " |+(2) ...to here\n"));
2401 : : }
2402 : :
2403 : : /* Big gap, increasing line number. */
2404 : 96 : {
2405 : : /* LHS -> RHS. */
2406 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2407 : : (t.get_line_and_columns (3, 1, 3),
2408 : : t.get_line_and_columns (97, 10, 12),
2409 : : (" events 1-2\n"
2410 : : "FILENAME:3:1:\n"
2411 : : " 3 | LHS RHS\n"
2412 : : " | ^~~\n"
2413 : : " | |\n"
2414 : : " | (1) from here... ->-+\n"
2415 : : " | |\n"
2416 : : "......\n"
2417 : : " | |\n"
2418 : : " |+--------------------+\n"
2419 : : " 97 ||LHS RHS\n"
2420 : : " || ~~~\n"
2421 : : " || |\n"
2422 : : " |+-------->(2) ...to here\n"));
2423 : :
2424 : : /* RHS -> LHS. */
2425 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2426 : : (t.get_line_and_columns (3, 10, 12),
2427 : : t.get_line_and_columns (97, 1, 3),
2428 : : (" events 1-2\n"
2429 : : "FILENAME:3:10:\n"
2430 : : " 3 | LHS RHS\n"
2431 : : " | ^~~\n"
2432 : : " | |\n"
2433 : : " | (1) from here... ->-+\n"
2434 : : " | |\n"
2435 : : "......\n"
2436 : : " | |\n"
2437 : : " |+-----------------------------+\n"
2438 : : " 97 ||LHS RHS\n"
2439 : : " ||~~~ \n"
2440 : : " |||\n"
2441 : : " |+(2) ...to here\n"));
2442 : : }
2443 : :
2444 : : /* Big gap, decreasing line number. */
2445 : 96 : {
2446 : : /* LHS -> RHS. */
2447 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2448 : : (t.get_line_and_columns (97, 1, 3),
2449 : : t.get_line_and_columns (3, 10, 12),
2450 : : (" event 1\n"
2451 : : "FILENAME:97:1:\n"
2452 : : " 97 | LHS RHS\n"
2453 : : " | ^~~\n"
2454 : : " | |\n"
2455 : : " | (1) from here... ->-+\n"
2456 : : " | |\n"
2457 : : " event 2\n"
2458 : : " | |\n"
2459 : : " |+--------------------+\n"
2460 : : " 3 ||LHS RHS\n"
2461 : : " || ^~~\n"
2462 : : " || |\n"
2463 : : " |+-------->(2) ...to here\n"));
2464 : :
2465 : : /* RHS -> LHS. */
2466 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2467 : : (t.get_line_and_columns (97, 10, 12),
2468 : : t.get_line_and_columns (3, 1, 3),
2469 : : (" event 1\n"
2470 : : "FILENAME:97:10:\n"
2471 : : " 97 | LHS RHS\n"
2472 : : " | ^~~\n"
2473 : : " | |\n"
2474 : : " | (1) from here... ->-+\n"
2475 : : " | |\n"
2476 : : " event 2\n"
2477 : : " | |\n"
2478 : : " |+-----------------------------+\n"
2479 : : " 3 ||LHS RHS\n"
2480 : : " ||^~~\n"
2481 : : " |||\n"
2482 : : " |+(2) ...to here\n"));
2483 : : }
2484 : :
2485 : : /* Unknown src. */
2486 : 96 : {
2487 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2488 : : (UNKNOWN_LOCATION,
2489 : : t.get_line_and_columns (3, 10, 12),
2490 : : (" event 1\n"
2491 : : " (1): from here...\n"
2492 : : " event 2\n"
2493 : : "FILENAME:3:10:\n"
2494 : : " 3 | LHS RHS\n"
2495 : : " | ^~~\n"
2496 : : " | |\n"
2497 : : " |+-------->(2) ...to here\n"));
2498 : : }
2499 : :
2500 : : /* Unknown dst. */
2501 : 96 : {
2502 : 96 : ASSERT_CFG_EDGE_PATH_STREQ
2503 : : (t.get_line_and_columns (3, 1, 3),
2504 : : UNKNOWN_LOCATION,
2505 : : (" event 1\n"
2506 : : "FILENAME:3:1:\n"
2507 : : " 3 | LHS RHS\n"
2508 : : " | ^~~\n"
2509 : : " | |\n"
2510 : : " | (1) from here... ->-+\n"
2511 : : " | |\n"
2512 : : " event 2\n"
2513 : : "FILENAME:\n"
2514 : : " (2): ...to here\n"));
2515 : : }
2516 : 96 : }
2517 : :
2518 : : /* Another complex example, adapted from data-model-20.c. */
2519 : :
2520 : : static void
2521 : 96 : test_control_flow_5 (const line_table_case &case_,
2522 : : pretty_printer *event_pp)
2523 : : {
2524 : : /* Create a tempfile and write some text to it.
2525 : : ...000000000111111111122222222223333333333444444444455555555556666666666.
2526 : : ...123456789012345678901234567890123456789012345678901234567890123456789. */
2527 : 96 : const char *content
2528 : : = (" if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
2529 : : " return NULL;\n" /* <------------------------- line 2. */
2530 : : "\n" /* <----------------------------------------- line 3. */
2531 : : " for (i = 0; i < n; i++) {\n" /* <-------------- line 4. */
2532 : : " if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n");
2533 : :
2534 : 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2535 : :
2536 : 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2537 : 96 : test_path path (logical_loc_mgr, event_pp);
2538 : : /* (1) */
2539 : 96 : path.add_event (t.get_line_and_column (1, 6), nullptr, 0,
2540 : : "following %qs branch (when %qs is non-NULL)...",
2541 : : "false", "arr");
2542 : 96 : path.connect_to_next_event ();
2543 : :
2544 : : /* (2) */
2545 : 96 : path.add_event (t.get_line_and_columns (4, 8, 10, 12), nullptr, 0,
2546 : : "...to here");
2547 : :
2548 : : /* (3) */
2549 : 96 : path.add_event (t.get_line_and_columns (4, 15, 17, 19), nullptr, 0,
2550 : : "following %qs branch (when %qs)...",
2551 : : "true", "i < n");
2552 : 96 : path.connect_to_next_event ();
2553 : :
2554 : : /* (4) */
2555 : 96 : path.add_event (t.get_line_and_column (5, 13), nullptr, 0,
2556 : : "...to here");
2557 : :
2558 : : /* (5) */
2559 : 96 : path.add_event (t.get_line_and_columns (5, 33, 58), nullptr, 0,
2560 : : "allocated here");
2561 : :
2562 : 96 : if (!path_events_have_column_data_p (path))
2563 : 40 : return;
2564 : :
2565 : 56 : {
2566 : 56 : selftest::test_context dc;
2567 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2568 : 56 : dc.show_event_links (true);
2569 : 56 : dc.show_line_numbers (true);
2570 : 56 : text_sink text_output (dc);
2571 : 56 : path_print_policy policy (text_output);
2572 : 56 : path_summary summary (policy, *event_pp, path, true);
2573 : 56 : print_path_summary_as_text (summary, text_output, false);
2574 : 56 : ASSERT_STREQ
2575 : : (" events 1-5\n"
2576 : : "FILENAME:1:6:\n"
2577 : : " 1 | if ((arr = (struct foo **)malloc(n * sizeof(struct foo *))) == NULL)\n"
2578 : : " | ^\n"
2579 : : " | |\n"
2580 : : " | (1) following `false' branch (when `arr' is non-NULL)... ->-+\n"
2581 : : " | |\n"
2582 : : "......\n"
2583 : : " | |\n"
2584 : : " |+-----------------------------------------------------------------+\n"
2585 : : " 4 || for (i = 0; i < n; i++) {\n"
2586 : : " || ~~~~~ ~~~~~\n"
2587 : : " || | |\n"
2588 : : " || | (3) following `true' branch (when `i < n')... ->-+\n"
2589 : : " |+-------->(2) ...to here |\n"
2590 : : " | |\n"
2591 : : " | |\n"
2592 : : " |+-----------------------------------------------------------------+\n"
2593 : : " 5 || if ((arr[i] = (struct foo *)malloc(sizeof(struct foo))) == NULL) {\n"
2594 : : " || ~ ~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
2595 : : " || | |\n"
2596 : : " |+----------->(4) ...to here (5) allocated here\n",
2597 : : pp_formatted_text (text_output.get_printer ()));
2598 : 56 : }
2599 : 192 : }
2600 : :
2601 : : /* Another complex example, adapted from loop-3.c. */
2602 : :
2603 : : static void
2604 : 96 : test_control_flow_6 (const line_table_case &case_,
2605 : : pretty_printer *event_pp)
2606 : : {
2607 : : /* Create a tempfile and write some text to it.
2608 : : ...000000000111111111122222222223333333.
2609 : : ...123456789012345678901234567890123456. */
2610 : 96 : const char *content
2611 : : = ("#include <stdlib.h>\n" /* <------------------ line 1. */
2612 : : "\n" /* <------------------------------------- line 2. */
2613 : : "void test(int c)\n" /* <--------------------- line 3. */
2614 : : "{\n" /* <------------------------------------ line 4. */
2615 : : " int i;\n" /* <----------------------------- line 5. */
2616 : : " char *buffer = (char*)malloc(256);\n" /* <- line 6. */
2617 : : "\n" /* <------------------------------------- line 7. */
2618 : : " for (i=0; i<255; i++) {\n" /* <------------ line 8. */
2619 : : " buffer[i] = c;\n" /* <------------------- line 9. */
2620 : : "\n" /* <------------------------------------- line 10. */
2621 : : " free(buffer);\n" /* <-------------------- line 11. */
2622 : : " }\n"); /* <-------------------------------- line 12. */
2623 : :
2624 : 96 : control_flow_test t (SELFTEST_LOCATION, case_, content);
2625 : :
2626 : 96 : logical_locations::selftest::test_manager logical_loc_mgr;
2627 : 96 : test_path path (logical_loc_mgr, event_pp);
2628 : : /* (1) */
2629 : 96 : path.add_event (t.get_line_and_columns (6, 25, 35), nullptr, 0,
2630 : : "allocated here");
2631 : :
2632 : : /* (2) */
2633 : 96 : path.add_event (t.get_line_and_columns (8, 13, 14, 17), nullptr, 0,
2634 : : "following %qs branch (when %qs)...",
2635 : : "true", "i <= 254");
2636 : 96 : path.connect_to_next_event ();
2637 : :
2638 : : /* (3) */
2639 : 96 : path.add_event (t.get_line_and_columns (9, 5, 15, 17), nullptr, 0,
2640 : : "...to here");
2641 : :
2642 : : /* (4) */
2643 : 96 : path.add_event (t.get_line_and_columns (8, 13, 14, 17), nullptr, 0,
2644 : : "following %qs branch (when %qs)...",
2645 : : "true", "i <= 254");
2646 : 96 : path.connect_to_next_event ();
2647 : :
2648 : : /* (5) */
2649 : 96 : path.add_event (t.get_line_and_columns (9, 5, 15, 17), nullptr, 0,
2650 : : "...to here");
2651 : :
2652 : 96 : if (!path_events_have_column_data_p (path))
2653 : 40 : return;
2654 : :
2655 : 56 : {
2656 : 56 : selftest::test_context dc;
2657 : 56 : dc.set_text_art_charset (DIAGNOSTICS_TEXT_ART_CHARSET_ASCII);
2658 : 56 : dc.show_event_links (true);
2659 : 56 : dc.show_line_numbers (true);
2660 : 56 : text_sink text_output (dc);
2661 : 56 : path_print_policy policy (text_output);
2662 : 56 : path_summary summary (policy, *event_pp, path, true);
2663 : 56 : print_path_summary_as_text (summary, text_output, false);
2664 : 56 : ASSERT_STREQ
2665 : : (" events 1-3\n"
2666 : : "FILENAME:6:25:\n"
2667 : : " 6 | char *buffer = (char*)malloc(256);\n"
2668 : : " | ^~~~~~~~~~~\n"
2669 : : " | |\n"
2670 : : " | (1) allocated here\n"
2671 : : " 7 | \n"
2672 : : " 8 | for (i=0; i<255; i++) {\n"
2673 : : " | ~~~~~ \n"
2674 : : " | |\n"
2675 : : " | (2) following `true' branch (when `i <= 254')... ->-+\n"
2676 : : " | |\n"
2677 : : " | |\n"
2678 : : " |+-----------------------------------------------------------------+\n"
2679 : : " 9 || buffer[i] = c;\n"
2680 : : " || ~~~~~~~~~~~~~ \n"
2681 : : " || |\n"
2682 : : " |+------------->(3) ...to here\n"
2683 : : " events 4-5\n"
2684 : : " 8 | for (i=0; i<255; i++) {\n"
2685 : : " | ~^~~~\n"
2686 : : " | |\n"
2687 : : " | (4) following `true' branch (when `i <= 254')... ->-+\n"
2688 : : " | |\n"
2689 : : " | |\n"
2690 : : " |+-----------------------------------------------------------------+\n"
2691 : : " 9 || buffer[i] = c;\n"
2692 : : " || ~~~~~~~~~~~~~\n"
2693 : : " || |\n"
2694 : : " |+------------->(5) ...to here\n",
2695 : : pp_formatted_text (text_output.get_printer ()));
2696 : 56 : }
2697 : 192 : }
2698 : :
2699 : : static void
2700 : 96 : control_flow_tests (const line_table_case &case_)
2701 : : {
2702 : 96 : pretty_printer pp;
2703 : 96 : pp_show_color (&pp) = false;
2704 : :
2705 : 96 : test_control_flow_1 (case_, &pp);
2706 : 96 : test_control_flow_2 (case_, &pp);
2707 : 96 : test_control_flow_3 (case_, &pp);
2708 : 96 : test_control_flow_4 (case_, &pp);
2709 : 96 : test_control_flow_5 (case_, &pp);
2710 : 96 : test_control_flow_6 (case_, &pp);
2711 : 96 : }
2712 : :
2713 : : } // namespace diagnostics::paths::selftest
2714 : : } // namespace diagnostics::paths
2715 : :
2716 : : namespace selftest { // diagnostics::selftest
2717 : :
2718 : : /* Run all of the selftests within this file. */
2719 : :
2720 : : void
2721 : 4 : paths_output_cc_tests ()
2722 : : {
2723 : 4 : pretty_printer pp;
2724 : 4 : pp_show_color (&pp) = false;
2725 : :
2726 : 4 : ::selftest::auto_fix_quotes fix_quotes;
2727 : 4 : diagnostics::paths::selftest::test_empty_path (&pp);
2728 : 4 : diagnostics::paths::selftest::test_intraprocedural_path (&pp);
2729 : 4 : diagnostics::paths::selftest::test_interprocedural_path_1 (&pp);
2730 : 4 : diagnostics::paths::selftest::test_interprocedural_path_2 (&pp);
2731 : 4 : diagnostics::paths::selftest::test_recursion (&pp);
2732 : 4 : for_each_line_table_case (diagnostics::paths::selftest::control_flow_tests);
2733 : 4 : }
2734 : :
2735 : : } // namespace diagnostics::selftest
2736 : : } // namespace diagnostics
2737 : :
2738 : : #if __GNUC__ >= 10
2739 : : # pragma GCC diagnostic pop
2740 : : #endif
2741 : :
2742 : : #endif /* #if CHECKING_P */
|