]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/tree-diagnostic-path.cc
aarch64: Avoid using mismatched ZERO ZA sizes
[thirdparty/gcc.git] / gcc / tree-diagnostic-path.cc
CommitLineData
4bc1899b 1/* Paths through the code associated with a diagnostic.
a945c346 2 Copyright (C) 2019-2024 Free Software Foundation, Inc.
4bc1899b
DM
3 Contributed by David Malcolm <dmalcolm@redhat.com>
4
5This file is part of GCC.
6
7GCC is free software; you can redistribute it and/or modify it under
8the terms of the GNU General Public License as published by the Free
9Software Foundation; either version 3, or (at your option) any later
10version.
11
12GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13WARRANTY; without even the implied warranty of MERCHANTABILITY or
14FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15for more details.
16
17You should have received a copy of the GNU General Public License
18along with GCC; see the file COPYING3. If not see
19<http://www.gnu.org/licenses/>. */
20
21#include "config.h"
3a1e9f3e 22#define INCLUDE_VECTOR
4bc1899b
DM
23#include "system.h"
24#include "coretypes.h"
25#include "tree.h"
26#include "diagnostic.h"
27#include "tree-pretty-print.h"
28#include "gimple-pretty-print.h"
29#include "tree-diagnostic.h"
30#include "langhooks.h"
31#include "intl.h"
32#include "diagnostic-path.h"
33#include "json.h"
34#include "gcc-rich-location.h"
35#include "diagnostic-color.h"
36#include "diagnostic-event-id.h"
37#include "selftest.h"
38#include "selftest-diagnostic.h"
39
40/* Anonymous namespace for path-printing code. */
41
42namespace {
43
44/* Subclass of range_label for showing a particular event
45 when showing a consecutive run of events within a diagnostic_path as
46 labelled ranges within one gcc_rich_location. */
47
48class path_label : public range_label
49{
50 public:
51 path_label (const diagnostic_path *path, unsigned start_idx)
52 : m_path (path), m_start_idx (start_idx)
53 {}
54
ff171cb1 55 label_text get_text (unsigned range_idx) const final override
4bc1899b
DM
56 {
57 unsigned event_idx = m_start_idx + range_idx;
58 const diagnostic_event &event = m_path->get_event (event_idx);
59
60 /* Get the description of the event, perhaps with colorization:
61 normally, we don't colorize within a range_label, but this
62 is special-cased for diagnostic paths. */
63 bool colorize = pp_show_color (global_dc->printer);
64 label_text event_text (event.get_desc (colorize));
f858fe7a 65 gcc_assert (event_text.get ());
4bc1899b
DM
66 pretty_printer pp;
67 pp_show_color (&pp) = pp_show_color (global_dc->printer);
68 diagnostic_event_id_t event_id (event_idx);
f858fe7a 69 pp_printf (&pp, "%@ %s", &event_id, event_text.get ());
4bc1899b
DM
70 label_text result = label_text::take (xstrdup (pp_formatted_text (&pp)));
71 return result;
72 }
73
74 private:
75 const diagnostic_path *m_path;
76 unsigned m_start_idx;
77};
78
79/* Return true if E1 and E2 can be consolidated into the same run of events
80 when printing a diagnostic_path. */
81
82static bool
83can_consolidate_events (const diagnostic_event &e1,
84 const diagnostic_event &e2,
85 bool check_locations)
86{
3a1e9f3e
DM
87 if (e1.get_thread_id () != e2.get_thread_id ())
88 return false;
89
4bc1899b
DM
90 if (e1.get_fndecl () != e2.get_fndecl ())
91 return false;
92
93 if (e1.get_stack_depth () != e2.get_stack_depth ())
94 return false;
95
96 if (check_locations)
97 {
98 location_t loc1 = e1.get_location ();
99 location_t loc2 = e2.get_location ();
100
101 if (loc1 < RESERVED_LOCATION_COUNT
102 || loc2 < RESERVED_LOCATION_COUNT)
103 return false;
104
105 /* Neither can be macro-based. */
106 if (linemap_location_from_macro_expansion_p (line_table, loc1))
107 return false;
108 if (linemap_location_from_macro_expansion_p (line_table, loc2))
109 return false;
110 }
111
112 /* Passed all the tests. */
113 return true;
114}
115
3a1e9f3e
DM
116struct event_range;
117struct path_summary;
118class thread_event_printer;
119
120/* A bundle of information about all of the events in a diagnostic_path
121 relating to a specific path, for use by path_summary. */
122
123class per_thread_summary
124{
125public:
126 per_thread_summary (label_text name, unsigned swimlane_idx)
127 : m_name (std::move (name)),
128 m_swimlane_idx (swimlane_idx),
129 m_min_depth (INT_MAX),
130 m_max_depth (INT_MIN)
131 {}
132
133 void update_depth_limits (int stack_depth)
134 {
135 if (stack_depth < m_min_depth)
136 m_min_depth = stack_depth;
137 if (stack_depth > m_max_depth)
138 m_max_depth = stack_depth;
139 }
140
141 const char *get_name () const { return m_name.get (); }
142 unsigned get_swimlane_index () const { return m_swimlane_idx; }
143
144private:
145 friend struct path_summary;
146 friend class thread_event_printer;
147
148 const label_text m_name;
149
150 /* The "swimlane index" is the order in which this per_thread_summary
151 was created, for use when printing the events. */
152 const unsigned m_swimlane_idx;
153
154 // The event ranges specific to this thread:
155 auto_vec<event_range *> m_event_ranges;
156 int m_min_depth;
157 int m_max_depth;
158};
159
160/* A range of consecutive events within a diagnostic_path, all within the
161 same thread, and with the same fndecl and stack_depth, and which are suitable
f8cc59ef
DM
162 to print with a single call to diagnostic_show_locus. */
163struct event_range
4bc1899b 164{
f8cc59ef 165 event_range (const diagnostic_path *path, unsigned start_idx,
3a1e9f3e
DM
166 const diagnostic_event &initial_event,
167 const per_thread_summary &t)
f8cc59ef
DM
168 : m_path (path),
169 m_initial_event (initial_event),
170 m_fndecl (initial_event.get_fndecl ()),
171 m_stack_depth (initial_event.get_stack_depth ()),
172 m_start_idx (start_idx), m_end_idx (start_idx),
173 m_path_label (path, start_idx),
3a1e9f3e
DM
174 m_richloc (initial_event.get_location (), &m_path_label),
175 m_thread_id (initial_event.get_thread_id ()),
176 m_per_thread_summary (t)
f8cc59ef
DM
177 {}
178
179 bool maybe_add_event (const diagnostic_event &new_ev, unsigned idx,
180 bool check_rich_locations)
4bc1899b 181 {
f8cc59ef
DM
182 if (!can_consolidate_events (m_initial_event, new_ev,
183 check_rich_locations))
184 return false;
185 if (check_rich_locations)
186 if (!m_richloc.add_location_if_nearby (new_ev.get_location (),
187 false, &m_path_label))
4bc1899b 188 return false;
f8cc59ef
DM
189 m_end_idx = idx;
190 return true;
191 }
4bc1899b 192
f8cc59ef
DM
193 /* Print the events in this range to DC, typically as a single
194 call to the printer's diagnostic_show_locus. */
4bc1899b 195
3a1e9f3e 196 void print (diagnostic_context *dc, pretty_printer *pp)
f8cc59ef
DM
197 {
198 location_t initial_loc = m_initial_event.get_location ();
4bc1899b 199
f8cc59ef
DM
200 /* Emit a span indicating the filename (and line/column) if the
201 line has changed relative to the last call to
202 diagnostic_show_locus. */
c5c565ef 203 if (dc->m_source_printing.enabled)
f8cc59ef
DM
204 {
205 expanded_location exploc
206 = linemap_client_expand_location_to_spelling_point
8625aa24 207 (line_table, initial_loc, LOCATION_ASPECT_CARET);
8200cd97 208 if (exploc.file != LOCATION_FILE (dc->m_last_location))
07e568d7 209 diagnostic_start_span (dc) (dc, exploc);
f8cc59ef 210 }
4bc1899b 211
f8cc59ef
DM
212 /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
213 primary location for an event, diagnostic_show_locus won't print
214 anything.
4bc1899b 215
f8cc59ef
DM
216 In particular the label for the event won't get printed.
217 Fail more gracefully in this case by showing the event
218 index and text, at no particular location. */
219 if (get_pure_location (initial_loc) <= BUILTINS_LOCATION)
220 {
221 for (unsigned i = m_start_idx; i <= m_end_idx; i++)
222 {
223 const diagnostic_event &iter_event = m_path->get_event (i);
224 diagnostic_event_id_t event_id (i);
225 label_text event_text (iter_event.get_desc (true));
f858fe7a 226 pp_printf (pp, " %@: %s", &event_id, event_text.get ());
f8cc59ef 227 pp_newline (pp);
f8cc59ef
DM
228 }
229 return;
230 }
4bc1899b 231
f8cc59ef 232 /* Call diagnostic_show_locus to show the events using labels. */
3a1e9f3e 233 diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH, pp);
4bc1899b 234
f8cc59ef
DM
235 /* If we have a macro expansion, show the expansion to the user. */
236 if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
237 {
238 gcc_assert (m_start_idx == m_end_idx);
239 maybe_unwind_expanded_macro_loc (dc, initial_loc);
240 }
241 }
4bc1899b 242
f8cc59ef
DM
243 const diagnostic_path *m_path;
244 const diagnostic_event &m_initial_event;
245 tree m_fndecl;
246 int m_stack_depth;
247 unsigned m_start_idx;
248 unsigned m_end_idx;
249 path_label m_path_label;
250 gcc_rich_location m_richloc;
3a1e9f3e
DM
251 diagnostic_thread_id_t m_thread_id;
252 const per_thread_summary &m_per_thread_summary;
f8cc59ef 253};
4bc1899b 254
f8cc59ef 255/* A struct for grouping together the events in a diagnostic_path into
3a1e9f3e
DM
256 ranges of events, partitioned by thread and by stack frame (i.e. by fndecl
257 and stack depth). */
4bc1899b 258
f8cc59ef
DM
259struct path_summary
260{
261 path_summary (const diagnostic_path &path, bool check_rich_locations);
4bc1899b
DM
262
263 unsigned get_num_ranges () const { return m_ranges.length (); }
3a1e9f3e
DM
264 bool multithreaded_p () const { return m_per_thread_summary.length () > 1; }
265
266 const per_thread_summary &get_events_for_thread_id (diagnostic_thread_id_t tid)
267 {
268 per_thread_summary **slot = m_thread_id_to_events.get (tid);
269 gcc_assert (slot);
270 gcc_assert (*slot);
271 return **slot;
272 }
4bc1899b 273
4bc1899b 274 auto_delete_vec <event_range> m_ranges;
3a1e9f3e
DM
275 auto_delete_vec <per_thread_summary> m_per_thread_summary;
276 hash_map<int_hash<diagnostic_thread_id_t, -1, -2>,
277 per_thread_summary *> m_thread_id_to_events;
278
279private:
280 per_thread_summary &
281 get_or_create_events_for_thread_id (const diagnostic_path &path,
282 diagnostic_thread_id_t tid)
283 {
284 if (per_thread_summary **slot = m_thread_id_to_events.get (tid))
285 return **slot;
286
287 const diagnostic_thread &thread = path.get_thread (tid);
288 per_thread_summary *pts = new per_thread_summary (thread.get_name (false),
289 m_per_thread_summary.length ());
290 m_thread_id_to_events.put (tid, pts);
291 m_per_thread_summary.safe_push (pts);
292 return *pts;
293 }
4bc1899b
DM
294};
295
296/* path_summary's ctor. */
297
298path_summary::path_summary (const diagnostic_path &path,
299 bool check_rich_locations)
300{
301 const unsigned num_events = path.num_events ();
302
303 event_range *cur_event_range = NULL;
304 for (unsigned idx = 0; idx < num_events; idx++)
305 {
306 const diagnostic_event &event = path.get_event (idx);
3a1e9f3e
DM
307 const diagnostic_thread_id_t thread_id = event.get_thread_id ();
308 per_thread_summary &pts
309 = get_or_create_events_for_thread_id (path, thread_id);
310
311 pts.update_depth_limits (event.get_stack_depth ());
312
4bc1899b
DM
313 if (cur_event_range)
314 if (cur_event_range->maybe_add_event (event, idx, check_rich_locations))
315 continue;
316
3a1e9f3e 317 cur_event_range = new event_range (&path, idx, event, pts);
4bc1899b 318 m_ranges.safe_push (cur_event_range);
3a1e9f3e 319 pts.m_event_ranges.safe_push (cur_event_range);
4bc1899b
DM
320 }
321}
322
323/* Write SPACES to PP. */
324
325static void
326write_indent (pretty_printer *pp, int spaces)
327{
328 for (int i = 0; i < spaces; i++)
329 pp_space (pp);
330}
331
332/* Print FNDDECL to PP, quoting it if QUOTED is true.
333
334 We can't use "%qE" here since we can't guarantee the capabilities
335 of PP. */
336
337static void
338print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
339{
340 const char *n = DECL_NAME (fndecl)
341 ? identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2))
342 : _("<anonymous>");
343 if (quoted)
344 pp_printf (pp, "%qs", n);
345 else
346 pp_string (pp, n);
347}
348
3a1e9f3e
DM
349static const int base_indent = 2;
350static const int per_frame_indent = 2;
351
352/* A bundle of state for printing event_range instances for a particular
353 thread. */
354
355class thread_event_printer
356{
357public:
358 thread_event_printer (const per_thread_summary &t, bool show_depths)
359 : m_per_thread_summary (t),
360 m_show_depths (show_depths),
361 m_cur_indent (base_indent),
362 m_vbar_column_for_depth (),
363 m_num_printed (0)
364 {
365 }
366
367 /* Get the previous event_range within this thread, if any. */
368 const event_range *get_any_prev_range () const
369 {
370 if (m_num_printed > 0)
371 return m_per_thread_summary.m_event_ranges[m_num_printed - 1];
372 else
373 return nullptr;
374 }
375
376 /* Get the next event_range within this thread, if any. */
377 const event_range *get_any_next_range () const
378 {
379 if (m_num_printed < m_per_thread_summary.m_event_ranges.length () - 1)
380 return m_per_thread_summary.m_event_ranges[m_num_printed + 1];
381 else
382 return nullptr;
383 }
384
385 void print_swimlane_for_event_range (diagnostic_context *dc,
386 pretty_printer *pp,
387 event_range *range)
388 {
389 const char *const line_color = "path";
390 const char *start_line_color
391 = colorize_start (pp_show_color (pp), line_color);
392 const char *end_line_color = colorize_stop (pp_show_color (pp));
393
394 write_indent (pp, m_cur_indent);
395 if (const event_range *prev_range = get_any_prev_range ())
396 {
397 if (range->m_stack_depth > prev_range->m_stack_depth)
398 {
399 /* Show pushed stack frame(s). */
400 const char *push_prefix = "+--> ";
401 pp_string (pp, start_line_color);
402 pp_string (pp, push_prefix);
403 pp_string (pp, end_line_color);
404 m_cur_indent += strlen (push_prefix);
405 }
406 }
407 if (range->m_fndecl)
408 {
409 print_fndecl (pp, range->m_fndecl, true);
410 pp_string (pp, ": ");
411 }
412 if (range->m_start_idx == range->m_end_idx)
413 pp_printf (pp, "event %i",
414 range->m_start_idx + 1);
415 else
416 pp_printf (pp, "events %i-%i",
417 range->m_start_idx + 1, range->m_end_idx + 1);
418 if (m_show_depths)
419 pp_printf (pp, " (depth %i)", range->m_stack_depth);
420 pp_newline (pp);
421
422 /* Print a run of events. */
423 {
424 write_indent (pp, m_cur_indent + per_frame_indent);
425 pp_string (pp, start_line_color);
426 pp_string (pp, "|");
427 pp_string (pp, end_line_color);
428 pp_newline (pp);
429
430 char *saved_prefix = pp_take_prefix (pp);
431 char *prefix;
432 {
433 pretty_printer tmp_pp;
434 write_indent (&tmp_pp, m_cur_indent + per_frame_indent);
435 pp_string (&tmp_pp, start_line_color);
436 pp_string (&tmp_pp, "|");
437 pp_string (&tmp_pp, end_line_color);
438 prefix = xstrdup (pp_formatted_text (&tmp_pp));
439 }
440 pp_set_prefix (pp, prefix);
441 pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
442 range->print (dc, pp);
443 pp_set_prefix (pp, saved_prefix);
444
445 write_indent (pp, m_cur_indent + per_frame_indent);
446 pp_string (pp, start_line_color);
447 pp_string (pp, "|");
448 pp_string (pp, end_line_color);
449 pp_newline (pp);
450 }
451
452 if (const event_range *next_range = get_any_next_range ())
453 {
454 if (range->m_stack_depth > next_range->m_stack_depth)
455 {
456 if (m_vbar_column_for_depth.get (next_range->m_stack_depth))
457 {
458 /* Show returning from stack frame(s), by printing
459 something like:
460 " |\n"
461 " <------------ +\n"
462 " |\n". */
463 int vbar_for_next_frame
464 = *m_vbar_column_for_depth.get (next_range->m_stack_depth);
465
466 int indent_for_next_frame
467 = vbar_for_next_frame - per_frame_indent;
468 write_indent (pp, vbar_for_next_frame);
469 pp_string (pp, start_line_color);
470 pp_character (pp, '<');
471 for (int i = indent_for_next_frame + per_frame_indent;
472 i < m_cur_indent + per_frame_indent - 1; i++)
473 pp_character (pp, '-');
474 pp_character (pp, '+');
475 pp_string (pp, end_line_color);
476 pp_newline (pp);
477 m_cur_indent = indent_for_next_frame;
478
479 write_indent (pp, vbar_for_next_frame);
480 pp_string (pp, start_line_color);
481 pp_character (pp, '|');
482 pp_string (pp, end_line_color);
483 pp_newline (pp);
484 }
485 else
486 {
487 /* Handle disjoint paths (e.g. a callback at some later
488 time). */
489 m_cur_indent = base_indent;
490 }
491 }
492 else if (range->m_stack_depth < next_range->m_stack_depth)
493 {
494 /* Prepare to show pushed stack frame. */
495 gcc_assert (range->m_stack_depth != EMPTY);
496 gcc_assert (range->m_stack_depth != DELETED);
497 m_vbar_column_for_depth.put (range->m_stack_depth,
498 m_cur_indent + per_frame_indent);
499 m_cur_indent += per_frame_indent;
500 }
501 }
502
503 m_num_printed++;
504 }
505
506 int get_cur_indent () const { return m_cur_indent; }
507
508private:
509 const per_thread_summary &m_per_thread_summary;
510 bool m_show_depths;
511
512 /* Print the ranges. */
513 int m_cur_indent;
514
515 /* Keep track of column numbers of existing '|' characters for
516 stack depths we've already printed. */
517 static const int EMPTY = -1;
518 static const int DELETED = -2;
519 typedef int_hash <int, EMPTY, DELETED> vbar_hash;
520 hash_map <vbar_hash, int> m_vbar_column_for_depth;
521
522 /* How many event ranges within this swimlane have we printed.
523 This is the index of the next event_range to print. */
524 unsigned m_num_printed;
525};
526
f8cc59ef 527/* Print path_summary PS to DC, giving an overview of the interprocedural
4bc1899b
DM
528 calls and returns.
529
530 Print the event descriptions in a nested form, printing the event
531 descriptions within calls to diagnostic_show_locus, using labels to
532 show the events:
533
534 'foo' (events 1-2)
535 | NN |
536 | |
537 +--> 'bar' (events 3-4)
538 | NN |
539 | |
540 +--> 'baz' (events 5-6)
541 | NN |
542 | |
543 <------------ +
544 |
545 'foo' (events 7-8)
546 | NN |
547 | |
548 +--> 'bar' (events 9-10)
549 | NN |
550 | |
551 +--> 'baz' (events 11-12)
552 | NN |
553 | |
554
555 If SHOW_DEPTHS is true, append " (depth N)" to the header of each run
556 of events.
557
558 For events with UNKNOWN_LOCATION, print a summary of each the event. */
559
3a1e9f3e 560static void
f8cc59ef
DM
561print_path_summary_as_text (const path_summary *ps, diagnostic_context *dc,
562 bool show_depths)
4bc1899b
DM
563{
564 pretty_printer *pp = dc->printer;
565
3a1e9f3e
DM
566 std::vector<thread_event_printer> thread_event_printers;
567 for (auto t : ps->m_per_thread_summary)
568 thread_event_printers.push_back (thread_event_printer (*t, show_depths));
4bc1899b 569
4bc1899b
DM
570 unsigned i;
571 event_range *range;
f8cc59ef 572 FOR_EACH_VEC_ELT (ps->m_ranges, i, range)
4bc1899b 573 {
3a1e9f3e
DM
574 const int swimlane_idx
575 = range->m_per_thread_summary.get_swimlane_index ();
576 if (ps->multithreaded_p ())
577 if (i == 0 || ps->m_ranges[i - 1]->m_thread_id != range->m_thread_id)
578 {
579 if (i > 0)
580 pp_newline (pp);
581 pp_printf (pp, "Thread: %qs",
582 range->m_per_thread_summary.get_name ());
583 pp_newline (pp);
584 }
585 thread_event_printer &tep = thread_event_printers[swimlane_idx];
586 tep.print_swimlane_for_event_range (dc, pp, range);
4bc1899b
DM
587 }
588}
589
590} /* end of anonymous namespace for path-printing code. */
591
592/* Print PATH to CONTEXT, according to CONTEXT's path_format. */
593
594void
595default_tree_diagnostic_path_printer (diagnostic_context *context,
596 const diagnostic_path *path)
597{
598 gcc_assert (path);
599
600 const unsigned num_events = path->num_events ();
601
8200cd97 602 switch (context->get_path_format ())
4bc1899b
DM
603 {
604 case DPF_NONE:
605 /* Do nothing. */
606 return;
607
608 case DPF_SEPARATE_EVENTS:
609 {
610 /* A note per event. */
611 for (unsigned i = 0; i < num_events; i++)
612 {
613 const diagnostic_event &event = path->get_event (i);
614 label_text event_text (event.get_desc (false));
f858fe7a 615 gcc_assert (event_text.get ());
4bc1899b 616 diagnostic_event_id_t event_id (i);
8200cd97 617 if (context->show_path_depths_p ())
63c07319
DM
618 {
619 int stack_depth = event.get_stack_depth ();
620 tree fndecl = event.get_fndecl ();
621 /* -fdiagnostics-path-format=separate-events doesn't print
622 fndecl information, so with -fdiagnostics-show-path-depths
623 print the fndecls too, if any. */
624 if (fndecl)
625 inform (event.get_location (),
626 "%@ %s (fndecl %qD, depth %i)",
f858fe7a 627 &event_id, event_text.get (),
63c07319
DM
628 fndecl, stack_depth);
629 else
630 inform (event.get_location (),
631 "%@ %s (depth %i)",
f858fe7a 632 &event_id, event_text.get (),
63c07319
DM
633 stack_depth);
634 }
635 else
636 inform (event.get_location (),
f858fe7a 637 "%@ %s", &event_id, event_text.get ());
4bc1899b
DM
638 }
639 }
640 break;
641
642 case DPF_INLINE_EVENTS:
643 {
644 /* Consolidate related events. */
645 path_summary summary (*path, true);
646 char *saved_prefix = pp_take_prefix (context->printer);
647 pp_set_prefix (context->printer, NULL);
f8cc59ef 648 print_path_summary_as_text (&summary, context,
8200cd97 649 context->show_path_depths_p ());
4bc1899b
DM
650 pp_flush (context->printer);
651 pp_set_prefix (context->printer, saved_prefix);
652 }
3a1e9f3e 653 break;
4bc1899b
DM
654 }
655}
656
657/* This has to be here, rather than diagnostic-format-json.cc,
658 since diagnostic-format-json.o is within OBJS-libcommon and thus
659 doesn't have access to trees (for m_fndecl). */
660
661json::value *
004bb936 662default_tree_make_json_for_path (diagnostic_context *context,
4bc1899b
DM
663 const diagnostic_path *path)
664{
665 json::array *path_array = new json::array ();
666 for (unsigned i = 0; i < path->num_events (); i++)
667 {
668 const diagnostic_event &event = path->get_event (i);
669
670 json::object *event_obj = new json::object ();
671 if (event.get_location ())
672 event_obj->set ("location",
004bb936
LH
673 json_from_expanded_location (context,
674 event.get_location ()));
4bc1899b 675 label_text event_text (event.get_desc (false));
070944fd 676 event_obj->set_string ("description", event_text.get ());
4bc1899b
DM
677 if (tree fndecl = event.get_fndecl ())
678 {
679 const char *function
680 = identifier_to_locale (lang_hooks.decl_printable_name (fndecl, 2));
070944fd 681 event_obj->set_string ("function", function);
4bc1899b 682 }
070944fd 683 event_obj->set_integer ("depth", event.get_stack_depth ());
4bc1899b
DM
684 path_array->append (event_obj);
685 }
686 return path_array;
687}
688
689#if CHECKING_P
690
ca23341b
MS
691/* Disable warnings about missing quoting in GCC diagnostics for the print
692 calls in the tests below. */
693#if __GNUC__ >= 10
694# pragma GCC diagnostic push
695# pragma GCC diagnostic ignored "-Wformat-diag"
696#endif
697
4bc1899b
DM
698namespace selftest {
699
700/* A subclass of simple_diagnostic_path that adds member functions
701 for adding test events. */
702
703class test_diagnostic_path : public simple_diagnostic_path
704{
705 public:
706 test_diagnostic_path (pretty_printer *event_pp)
707 : simple_diagnostic_path (event_pp)
708 {
709 }
710
711 void add_entry (tree fndecl, int stack_depth)
712 {
713 add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
714 "entering %qE", fndecl);
715 }
716
717 void add_return (tree fndecl, int stack_depth)
718 {
719 add_event (UNKNOWN_LOCATION, fndecl, stack_depth,
720 "returning to %qE", fndecl);
721 }
722
723 void add_call (tree caller, int caller_stack_depth, tree callee)
724 {
725 add_event (UNKNOWN_LOCATION, caller, caller_stack_depth,
726 "calling %qE", callee);
727 add_entry (callee, caller_stack_depth + 1);
728 }
729};
730
731/* Verify that empty paths are handled gracefully. */
732
733static void
734test_empty_path (pretty_printer *event_pp)
735{
736 test_diagnostic_path path (event_pp);
737 ASSERT_FALSE (path.interprocedural_p ());
738
739 path_summary summary (path, false);
740 ASSERT_EQ (summary.get_num_ranges (), 0);
741
742 test_diagnostic_context dc;
f8cc59ef 743 print_path_summary_as_text (&summary, &dc, true);
4bc1899b
DM
744 ASSERT_STREQ ("",
745 pp_formatted_text (dc.printer));
746}
747
748/* Verify that print_path_summary works on a purely intraprocedural path. */
749
750static void
751test_intraprocedural_path (pretty_printer *event_pp)
752{
753 tree fntype_void_void
754 = build_function_type_array (void_type_node, 0, NULL);
755 tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
756
757 test_diagnostic_path path (event_pp);
758 path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "first %qs", "free");
759 path.add_event (UNKNOWN_LOCATION, fndecl_foo, 0, "double %qs", "free");
760
761 ASSERT_FALSE (path.interprocedural_p ());
762
763 path_summary summary (path, false);
764 ASSERT_EQ (summary.get_num_ranges (), 1);
765
766 test_diagnostic_context dc;
f8cc59ef 767 print_path_summary_as_text (&summary, &dc, true);
4bc1899b
DM
768 ASSERT_STREQ (" `foo': events 1-2 (depth 0)\n"
769 " |\n"
770 " | (1): first `free'\n"
771 " | (2): double `free'\n"
772 " |\n",
773 pp_formatted_text (dc.printer));
774}
775
776/* Verify that print_path_summary works on an interprocedural path. */
777
778static void
779test_interprocedural_path_1 (pretty_printer *event_pp)
780{
781 /* Build fndecls. The types aren't quite right, but that
782 doesn't matter for the purposes of this test. */
783 tree fntype_void_void
784 = build_function_type_array (void_type_node, 0, NULL);
785 tree fndecl_test = build_fn_decl ("test", fntype_void_void);
786 tree fndecl_make_boxed_int
787 = build_fn_decl ("make_boxed_int", fntype_void_void);
788 tree fndecl_wrapped_malloc
789 = build_fn_decl ("wrapped_malloc", fntype_void_void);
790 tree fndecl_free_boxed_int
791 = build_fn_decl ("free_boxed_int", fntype_void_void);
792 tree fndecl_wrapped_free
793 = build_fn_decl ("wrapped_free", fntype_void_void);
794
795 test_diagnostic_path path (event_pp);
796 path.add_entry (fndecl_test, 0);
797 path.add_call (fndecl_test, 0, fndecl_make_boxed_int);
798 path.add_call (fndecl_make_boxed_int, 1, fndecl_wrapped_malloc);
799 path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_malloc, 2, "calling malloc");
800 path.add_return (fndecl_test, 0);
801 path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
802 path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
803 path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
804 path.add_return (fndecl_test, 0);
805 path.add_call (fndecl_test, 0, fndecl_free_boxed_int);
806 path.add_call (fndecl_free_boxed_int, 1, fndecl_wrapped_free);
807 path.add_event (UNKNOWN_LOCATION, fndecl_wrapped_free, 2, "calling free");
808 ASSERT_EQ (path.num_events (), 18);
809
810 ASSERT_TRUE (path.interprocedural_p ());
811
812 path_summary summary (path, false);
813 ASSERT_EQ (summary.get_num_ranges (), 9);
814
815 test_diagnostic_context dc;
f8cc59ef 816 print_path_summary_as_text (&summary, &dc, true);
4bc1899b
DM
817 ASSERT_STREQ
818 (" `test': events 1-2 (depth 0)\n"
819 " |\n"
820 " | (1): entering `test'\n"
821 " | (2): calling `make_boxed_int'\n"
822 " |\n"
823 " +--> `make_boxed_int': events 3-4 (depth 1)\n"
824 " |\n"
825 " | (3): entering `make_boxed_int'\n"
826 " | (4): calling `wrapped_malloc'\n"
827 " |\n"
828 " +--> `wrapped_malloc': events 5-6 (depth 2)\n"
829 " |\n"
830 " | (5): entering `wrapped_malloc'\n"
831 " | (6): calling malloc\n"
832 " |\n"
833 " <-------------+\n"
834 " |\n"
835 " `test': events 7-8 (depth 0)\n"
836 " |\n"
837 " | (7): returning to `test'\n"
838 " | (8): calling `free_boxed_int'\n"
839 " |\n"
840 " +--> `free_boxed_int': events 9-10 (depth 1)\n"
841 " |\n"
842 " | (9): entering `free_boxed_int'\n"
843 " | (10): calling `wrapped_free'\n"
844 " |\n"
845 " +--> `wrapped_free': events 11-12 (depth 2)\n"
846 " |\n"
847 " | (11): entering `wrapped_free'\n"
848 " | (12): calling free\n"
849 " |\n"
850 " <-------------+\n"
851 " |\n"
852 " `test': events 13-14 (depth 0)\n"
853 " |\n"
854 " | (13): returning to `test'\n"
855 " | (14): calling `free_boxed_int'\n"
856 " |\n"
857 " +--> `free_boxed_int': events 15-16 (depth 1)\n"
858 " |\n"
859 " | (15): entering `free_boxed_int'\n"
860 " | (16): calling `wrapped_free'\n"
861 " |\n"
862 " +--> `wrapped_free': events 17-18 (depth 2)\n"
863 " |\n"
864 " | (17): entering `wrapped_free'\n"
865 " | (18): calling free\n"
866 " |\n",
867 pp_formatted_text (dc.printer));
868}
869
870/* Example where we pop the stack to an intermediate frame, rather than the
871 initial one. */
872
873static void
874test_interprocedural_path_2 (pretty_printer *event_pp)
875{
876 /* Build fndecls. The types aren't quite right, but that
877 doesn't matter for the purposes of this test. */
878 tree fntype_void_void
879 = build_function_type_array (void_type_node, 0, NULL);
880 tree fndecl_foo = build_fn_decl ("foo", fntype_void_void);
881 tree fndecl_bar = build_fn_decl ("bar", fntype_void_void);
882 tree fndecl_baz = build_fn_decl ("baz", fntype_void_void);
883
884 test_diagnostic_path path (event_pp);
885 path.add_entry (fndecl_foo, 0);
886 path.add_call (fndecl_foo, 0, fndecl_bar);
887 path.add_call (fndecl_bar, 1, fndecl_baz);
888 path.add_return (fndecl_bar, 1);
889 path.add_call (fndecl_bar, 1, fndecl_baz);
890 ASSERT_EQ (path.num_events (), 8);
891
892 ASSERT_TRUE (path.interprocedural_p ());
893
894 path_summary summary (path, false);
895 ASSERT_EQ (summary.get_num_ranges (), 5);
896
897 test_diagnostic_context dc;
f8cc59ef 898 print_path_summary_as_text (&summary, &dc, true);
4bc1899b
DM
899 ASSERT_STREQ
900 (" `foo': events 1-2 (depth 0)\n"
901 " |\n"
902 " | (1): entering `foo'\n"
903 " | (2): calling `bar'\n"
904 " |\n"
905 " +--> `bar': events 3-4 (depth 1)\n"
906 " |\n"
907 " | (3): entering `bar'\n"
908 " | (4): calling `baz'\n"
909 " |\n"
910 " +--> `baz': event 5 (depth 2)\n"
911 " |\n"
912 " | (5): entering `baz'\n"
913 " |\n"
914 " <------+\n"
915 " |\n"
916 " `bar': events 6-7 (depth 1)\n"
917 " |\n"
918 " | (6): returning to `bar'\n"
919 " | (7): calling `baz'\n"
920 " |\n"
921 " +--> `baz': event 8 (depth 2)\n"
922 " |\n"
923 " | (8): entering `baz'\n"
924 " |\n",
925 pp_formatted_text (dc.printer));
926}
927
928/* Verify that print_path_summary is sane in the face of a recursive
929 diagnostic_path. */
930
931static void
932test_recursion (pretty_printer *event_pp)
933{
934 tree fntype_void_void
935 = build_function_type_array (void_type_node, 0, NULL);
936 tree fndecl_factorial = build_fn_decl ("factorial", fntype_void_void);
937
938 test_diagnostic_path path (event_pp);
939 path.add_entry (fndecl_factorial, 0);
940 for (int depth = 0; depth < 3; depth++)
941 path.add_call (fndecl_factorial, depth, fndecl_factorial);
942 ASSERT_EQ (path.num_events (), 7);
943
944 ASSERT_TRUE (path.interprocedural_p ());
945
946 path_summary summary (path, false);
947 ASSERT_EQ (summary.get_num_ranges (), 4);
948
949 test_diagnostic_context dc;
f8cc59ef 950 print_path_summary_as_text (&summary, &dc, true);
4bc1899b
DM
951 ASSERT_STREQ
952 (" `factorial': events 1-2 (depth 0)\n"
953 " |\n"
954 " | (1): entering `factorial'\n"
955 " | (2): calling `factorial'\n"
956 " |\n"
957 " +--> `factorial': events 3-4 (depth 1)\n"
958 " |\n"
959 " | (3): entering `factorial'\n"
960 " | (4): calling `factorial'\n"
961 " |\n"
962 " +--> `factorial': events 5-6 (depth 2)\n"
963 " |\n"
964 " | (5): entering `factorial'\n"
965 " | (6): calling `factorial'\n"
966 " |\n"
967 " +--> `factorial': event 7 (depth 3)\n"
968 " |\n"
969 " | (7): entering `factorial'\n"
970 " |\n",
971 pp_formatted_text (dc.printer));
972}
973
974/* Run all of the selftests within this file. */
975
976void
977tree_diagnostic_path_cc_tests ()
978{
979 auto_fix_quotes fix_quotes;
980 pretty_printer *event_pp = global_dc->printer->clone ();
981 pp_show_color (event_pp) = 0;
982 test_empty_path (event_pp);
983 test_intraprocedural_path (event_pp);
984 test_interprocedural_path_1 (event_pp);
985 test_interprocedural_path_2 (event_pp);
986 test_recursion (event_pp);
987 delete event_pp;
988}
989
990} // namespace selftest
991
ca23341b
MS
992#if __GNUC__ >= 10
993# pragma GCC diagnostic pop
994#endif
995
4bc1899b 996#endif /* #if CHECKING_P */