From 5dcdf676dd0a8ebf2937341363afd46eb291738e Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 13 Nov 2019 11:59:37 -0500
Subject: [PATCH 54/78] FIXME: WIP on macros in diagnostic_path-printing (needs
 splitting)

---
 .../gcc.dg/analyzer/malloc-macro-inline-events.c   |  45 +++++++++
 .../gcc.dg/analyzer/malloc-macro-separate-events.c |  15 +++
 gcc/testsuite/gcc.dg/analyzer/malloc-macro.h       |   2 +
 gcc/tree-diagnostic-path.cc                        | 102 ++++++++++++---------
 gcc/tree-diagnostic.c                              |  10 +-
 gcc/tree-diagnostic.h                              |   3 +
 6 files changed, 127 insertions(+), 50 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/malloc-macro.h

diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
new file mode 100644
index 0000000..0d5831c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-inline-events.c
@@ -0,0 +1,45 @@
+/* Test path-printing in the face of macros.  */
+
+/* { dg-options "--analyzer -fdiagnostics-show-line-numbers -fdiagnostics-nn-line-numbers -fdiagnostics-path-format=inline-events -fdiagnostics-show-caret" } */
+
+#include "malloc-macro.h"
+
+/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */
+
+int test (void *ptr)
+{
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+
+  /* Erase the spans indicating the header file
+     (to avoid embedding path assumptions).  */
+  /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
+  /* { dg-regexp "\[^|\]+/malloc-macro.h:\[0-9\]+:\[0-9\]+:" } */
+
+  /* { dg-begin-multiline-output "" }
+   NN | #define WRAPPED_FREE(PTR) free(PTR)
+      |                           ^~~~~~~~~
+   NN |   WRAPPED_FREE (ptr);
+      |   ^~~~~~~~~~~~
+  'test': event 1
+     |
+     |
+     |   NN | #define WRAPPED_FREE(PTR) free(PTR)
+     |      |                           ^~~~~~~~~
+     |      |                           |
+     |      |                           (1) first 'free' here
+     |   NN |   WRAPPED_FREE (ptr);
+     |      |   ^~~~~~~~~~~~
+     |
+  'test': event 2
+     |
+     |
+     |   NN | #define WRAPPED_FREE(PTR) free(PTR)
+     |      |                           ^~~~~~~~~
+     |      |                           |
+     |      |                           (2) second 'free' here; first 'free' was at (1)
+     |   NN |   WRAPPED_FREE (ptr);
+     |      |   ^~~~~~~~~~~~
+     |
+     { dg-end-multiline-output "" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c
new file mode 100644
index 0000000..9c7e933
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro-separate-events.c
@@ -0,0 +1,15 @@
+/* Test path-printing in the face of macros.  */
+
+/* { dg-options "--analyzer -fdiagnostics-path-format=separate-events" } */
+
+#include "malloc-macro.h"
+
+/* { dg-warning "double-'free' of 'ptr'" "" { target *-*-* } 2 } */
+/* { dg-message "first 'free' here" "" { target *-*-* } 2 } */
+/* { dg-message "second 'free' here" "" { target *-*-* } 2 } */
+
+int test (void *ptr)
+{
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+  WRAPPED_FREE (ptr); /* { dg-message "in expansion of macro 'WRAPPED_FREE'" } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h b/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h
new file mode 100644
index 0000000..8c05b40
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-macro.h
@@ -0,0 +1,2 @@
+#include <stdlib.h>
+#define WRAPPED_FREE(PTR) free(PTR)
diff --git a/gcc/tree-diagnostic-path.cc b/gcc/tree-diagnostic-path.cc
index 3af8e0a..09c46f3 100644
--- a/gcc/tree-diagnostic-path.cc
+++ b/gcc/tree-diagnostic-path.cc
@@ -34,6 +34,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-color.h"
 #include "diagnostic-event-id.h"
 #include "selftest.h"
+#include "selftest-diagnostic.h"
 
 /* Anonymous namespace for path-printing code.  */
 
@@ -145,43 +146,52 @@ class path_summary
       return true;
     }
 
-    /* Print the events in this range to PP, typically as a single
-       call to diagnostic_show_locus.  */
+    /* Print the events in this range to DC, typically as a single
+       call to the printer's diagnostic_show_locus.  */
 
-    void print (pretty_printer *pp)
+    void print (diagnostic_context *dc)
     {
-      if (m_initial_event.get_location () > BUILTINS_LOCATION)
+      location_t initial_loc = m_initial_event.get_location ();
+
+      /* Emit a span indicating the filename (and line/column) if the
+	 line has changed relative to the last call to
+	 diagnostic_show_locus.  */
+      expanded_location exploc
+	= linemap_client_expand_location_to_spelling_point
+	    (initial_loc, LOCATION_ASPECT_CARET);
+      if (exploc.file != LOCATION_FILE (dc->last_location))
+	dc->start_span (dc, exploc);
+
+      /* If we have an UNKNOWN_LOCATION (or BUILTINS_LOCATION) as the
+	 primary location for an event, diagnostic_show_locus won't print
+	 anything.
+
+	 In particular the label for the event won't get printed.
+	 Fail more gracefully in this case by showing the event
+	 index and text, at no particular location.  */
+      if (initial_loc <= BUILTINS_LOCATION)
 	{
-	  /* Call diagnostic_show_locus to show the events using labels.  */
-	  diagnostic_context dc;
-	  memset (&dc, 0, sizeof (dc));
-	  dc.printer = pp;
-	  dc.show_caret = true;
-	  dc.show_labels_p = global_dc->show_labels_p;
-	  dc.show_line_numbers_p = global_dc->show_line_numbers_p;
-	  dc.use_nn_for_line_numbers_p = global_dc->use_nn_for_line_numbers_p;
-	  dc.start_span = global_dc->start_span;
-	  dc.min_margin_width = global_dc->min_margin_width;
-	  dc.caret_chars[0] = '^';
-	  diagnostic_set_caret_max_width (&dc, pp_line_cutoff (pp));
-	  diagnostic_show_locus (&dc, &m_richloc, DK_DIAGNOSTIC_PATH);
-	}
-      else
-	{
-	  /* If we have an UNKNOWN_LOCATION as the primary location
-	     for an event, diagnostic_show_locus won't print anything.
-	     In particular the label for the event won't get printed.
-	     Fail more gracefully in this case by showing the event
-	     index and text, at no particular location.  */
 	  for (unsigned i = m_start_idx; i <= m_end_idx; i++)
 	    {
 	      const diagnostic_event &iter_event = m_path->get_event (i);
 	      diagnostic_event_id_t event_id (i);
 	      label_text event_text (iter_event.get_desc (true));
+	      pretty_printer *pp = dc->printer;
 	      pp_printf (pp, " %@: %s", &event_id, event_text.m_buffer);
 	      pp_newline (pp);
 	      event_text.maybe_free ();
 	    }
+	  return;
+	}
+
+      /* Call diagnostic_show_locus to show the events using labels.  */
+      diagnostic_show_locus (dc, &m_richloc, DK_DIAGNOSTIC_PATH);
+
+      /* If we have a macro expansion, show the expansion to the user.  */
+      if (linemap_location_from_macro_expansion_p (line_table, initial_loc))
+	{
+	  gcc_assert (m_start_idx == m_end_idx);
+	  maybe_unwind_expanded_macro_loc (dc, initial_loc);
 	}
     }
 
@@ -198,7 +208,7 @@ class path_summary
  public:
   path_summary (const diagnostic_path &path, bool check_rich_locations);
 
-  void print (pretty_printer *pp, bool show_depths) const;
+  void print (diagnostic_context *dc, bool show_depths) const;
 
   unsigned get_num_ranges () const { return m_ranges.length (); }
 
@@ -252,7 +262,7 @@ print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
     pp_string (pp, n);
 }
 
-/* Print this path_summary to PP, giving an overview of the interprocedural
+/* Print this path_summary to DC, giving an overview of the interprocedural
    calls and returns.
 
    Print the event descriptions in a nested form, printing the event
@@ -286,8 +296,10 @@ print_fndecl (pretty_printer *pp, tree fndecl, bool quoted)
    For events with UNKNOWN_LOCATION, print a summary of each the event.  */
 
 void
-path_summary::print (pretty_printer *pp, bool show_depths) const
+path_summary::print (diagnostic_context *dc, bool show_depths) const
 {
+  pretty_printer *pp = dc->printer;
+
   auto_vec<int> name_lengths;
 
   /* Populate name_lengths.  */
@@ -375,7 +387,7 @@ path_summary::print (pretty_printer *pp, bool show_depths) const
 	}
 	pp_set_prefix (pp, prefix);
 	pp_prefixing_rule (pp) = DIAGNOSTICS_SHOW_PREFIX_EVERY_LINE;
-	range->print (pp);
+	range->print (dc);
 	pp_set_prefix (pp, saved_prefix);
 
 	write_indent (pp, cur_indent + midpoint);
@@ -478,7 +490,7 @@ default_tree_diagnostic_path_printer (diagnostic_context *context,
 	path_summary summary (*path, true);
 	char *saved_prefix = pp_take_prefix (context->printer);
 	pp_set_prefix (context->printer, NULL);
-	summary.print (context->printer, context->show_path_depths);
+	summary.print (context, context->show_path_depths);
 	pp_flush (context->printer);
 	pp_set_prefix (context->printer, saved_prefix);
       }
@@ -562,10 +574,10 @@ test_empty_path (pretty_printer *event_pp)
   path_summary summary (path, false);
   ASSERT_EQ (summary.get_num_ranges (), 0);
 
-  pretty_printer pp;
-  summary.print (&pp, true);
+  test_diagnostic_context dc;
+  summary.print (&dc, true);
   ASSERT_STREQ ("",
-		pp_formatted_text (&pp));
+		pp_formatted_text (dc.printer));
 }
 
 /* Verify that print_path_summary works on a purely intraprocedural path.  */
@@ -586,14 +598,14 @@ test_intraprocedural_path (pretty_printer *event_pp)
   path_summary summary (path, false);
   ASSERT_EQ (summary.get_num_ranges (), 1);
 
-  pretty_printer pp;
-  summary.print (&pp, true);
+  test_diagnostic_context dc;
+  summary.print (&dc, true);
   ASSERT_STREQ ("  `foo': events 1-2 (depth 0)\n"
 		"    |\n"
 		"    | (1): first `free'\n"
 		"    | (2): double `free'\n"
 		"    |\n",
-		pp_formatted_text (&pp));
+		pp_formatted_text (dc.printer));
 }
 
 /* Verify that print_path_summary works on an interprocedural path.  */
@@ -635,8 +647,8 @@ test_interprocedural_path_1 (pretty_printer *event_pp)
   path_summary summary (path, false);
   ASSERT_EQ (summary.get_num_ranges (), 9);
 
-  pretty_printer pp;
-  summary.print (&pp, true);
+  test_diagnostic_context dc;
+  summary.print (&dc, true);
   ASSERT_STREQ
     ("  `test': events 1-2 (depth 0)\n"
      "     |\n"
@@ -687,7 +699,7 @@ test_interprocedural_path_1 (pretty_printer *event_pp)
      "                              | (17): entering `wrapped_free'\n"
      "                              | (18): calling free\n"
      "                              |\n",
-     pp_formatted_text (&pp));
+     pp_formatted_text (dc.printer));
 }
 
 /* Example where we pop the stack to an intermediate frame, rather than the
@@ -717,8 +729,8 @@ test_interprocedural_path_2 (pretty_printer *event_pp)
   path_summary summary (path, false);
   ASSERT_EQ (summary.get_num_ranges (), 5);
 
-  pretty_printer pp;
-  summary.print (&pp, true);
+  test_diagnostic_context dc;
+  summary.print (&dc, true);
   ASSERT_STREQ
     ("  `foo': events 1-2 (depth 0)\n"
      "    |\n"
@@ -745,7 +757,7 @@ test_interprocedural_path_2 (pretty_printer *event_pp)
      "                  |\n"
      "                  | (8): entering `baz'\n"
      "                  |\n",
-     pp_formatted_text (&pp));
+     pp_formatted_text (dc.printer));
 }
 
 /* Verify that print_path_summary is sane in the face of a recursive
@@ -769,8 +781,8 @@ test_recursion (pretty_printer *event_pp)
   path_summary summary (path, false);
   ASSERT_EQ (summary.get_num_ranges (), 4);
 
-  pretty_printer pp;
-  summary.print (&pp, true);
+  test_diagnostic_context dc;
+  summary.print (&dc, true);
   ASSERT_STREQ
     ("  `factorial': events 1-2 (depth 0)\n"
      "       |\n"
@@ -791,7 +803,7 @@ test_recursion (pretty_printer *event_pp)
      "                                     |\n"
      "                                     | (7): entering `factorial'\n"
      "                                     |\n",
-     pp_formatted_text (&pp));
+     pp_formatted_text (dc.printer));
 }
 
 /* Run all of the selftests within this file.  */
diff --git a/gcc/tree-diagnostic.c b/gcc/tree-diagnostic.c
index 0cc6d84..d5f2f9e 100644
--- a/gcc/tree-diagnostic.c
+++ b/gcc/tree-diagnostic.c
@@ -96,9 +96,8 @@ struct loc_map_pair
    unwound macro expansion trace.  That's the part generated by this
    function.  */
 
-static void
+void
 maybe_unwind_expanded_macro_loc (diagnostic_context *context,
-                                 const diagnostic_info *diagnostic,
                                  location_t where)
 {
   const struct line_map *map;
@@ -106,6 +105,8 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
   unsigned ix;
   loc_map_pair loc, *iter;
 
+  const location_t original_loc = where;
+
   map = linemap_lookup (line_table, where);
   if (!linemap_macro_expansion_map_p (map))
     return;
@@ -142,7 +143,7 @@ maybe_unwind_expanded_macro_loc (diagnostic_context *context,
      first macro which expansion triggered this trace was expanded
      inside a system header.  */
   int saved_location_line =
-    expand_location_to_spelling_point (diagnostic_location (diagnostic)).line;
+    expand_location_to_spelling_point (original_loc).line;
 
   if (!LINEMAP_SYSP (ord_map))
     FOR_EACH_VEC_ELT (loc_vec, ix, iter)
@@ -238,8 +239,7 @@ void
 virt_loc_aware_diagnostic_finalizer (diagnostic_context *context,
 				     diagnostic_info *diagnostic)
 {
-  maybe_unwind_expanded_macro_loc (context, diagnostic,
-				   diagnostic_location (diagnostic));
+  maybe_unwind_expanded_macro_loc (context, diagnostic_location (diagnostic));
 }
 
 /* Default tree printer.   Handles declarations only.  */
diff --git a/gcc/tree-diagnostic.h b/gcc/tree-diagnostic.h
index ba933ca..d97a973 100644
--- a/gcc/tree-diagnostic.h
+++ b/gcc/tree-diagnostic.h
@@ -62,4 +62,7 @@ extern void default_tree_diagnostic_path_printer (diagnostic_context *,
 extern json::value *default_tree_make_json_for_path (diagnostic_context *,
 						     const diagnostic_path *);
 
+extern void maybe_unwind_expanded_macro_loc (diagnostic_context *context,
+					     location_t where);
+
 #endif /* ! GCC_TREE_DIAGNOSTIC_H */
-- 
1.8.5.3

