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