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