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