From 61798f9e67b535b927215baaec33d8bec7d249cb Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 26 Jul 2019 15:57:31 -0400
Subject: [PATCH 30/33] FIXME: split out edge_event into superedge_event and
 subclasses

---
 gcc/analyzer/impl-4.cc | 557 +++++++++++++++++++++++++++----------------------
 1 file changed, 311 insertions(+), 246 deletions(-)

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index 6874d99..da303f5 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -1490,7 +1490,8 @@ public:
 
 ////////////////////////////////////////////////////////////////////////////
 
-/* FIXME.  */
+/* An enum for discriminating between the concrete subclasses of
+   checker4_event.  */
 
 enum event_kind
 {
@@ -1498,7 +1499,9 @@ enum event_kind
   EK_STMT,
   EK_FUNCTION_ENTRY,
   EK_STATE_CHANGE,
-  EK_EDGE,
+  EK_CFG_EDGE,
+  EK_CALL_EDGE,
+  EK_RETURN_EDGE,
   EK_WARNING
 };
 
@@ -1517,14 +1520,18 @@ event_kind_to_string (enum event_kind ek)
       return "EK_FUNCTION_ENTRY";
     case EK_STATE_CHANGE:
       return "EK_STATE_CHANGE";
-    case EK_EDGE:
-      return "EK_EDGE";
+    case EK_CFG_EDGE:
+      return "EK_CFG_EDGE";
+    case EK_CALL_EDGE:
+      return "EK_CALL_EDGE";
+    case EK_RETURN_EDGE:
+      return "EK_RETURN_EDGE";
     case EK_WARNING:
       return "EK_WARNING";
     }
 }
 
-/* FIXME.  */
+/* Subclass of diagnostic_event for use in analyzer diagnostic_paths.  */
 
 class checker4_event : public diagnostic_event
 {
@@ -1583,6 +1590,8 @@ public:
   event_id_t m_emission_id; // only set once all pruning has occurred
 };
 
+/* A purely textual event, for use in debugging path creation and filtering.  */
+
 class debug_event : public checker4_event
 {
 public:
@@ -1611,6 +1620,9 @@ private:
   char *m_desc;
 };
 
+/* An event subclass for the execution of a gimple statement, for use
+   at high verbosity levels when debugging paths.  */
+
 class statement_event : public checker4_event
 {
 public:
@@ -1640,6 +1652,8 @@ public:
   program_state m_dst_state;
 };
 
+/* An event subclass describing the entry to a function.  */
+
 class function_entry_event : public checker4_event
 {
 public:
@@ -1661,6 +1675,8 @@ public:
   bool is_function_entry_p () const FINAL OVERRIDE { return true; }
 };
 
+/* Subclass of checker4_event describing a state change.  */
+
 class state_change_event : public checker4_event
 {
 public:
@@ -1760,12 +1776,15 @@ public:
   program_state m_dst_state;
 };
 
-class edge_event : public checker4_event
+/* Subclass of checker4_event; parent class for subclasses that relate to
+   a superedge.  */
+
+class superedge_event : public checker4_event
 {
 public:
-  edge_event (const exploded_edge &eedge,
-	      location_t loc, tree fndecl, int depth)
-  : checker4_event (EK_EDGE, loc, fndecl, depth),
+  superedge_event (enum event_kind kind, const exploded_edge &eedge,
+		   location_t loc, tree fndecl, int depth)
+  : checker4_event (kind, loc, fndecl, depth),
     m_eedge (eedge), m_sedge (eedge.m_sedge),
     m_var (NULL_TREE), m_critical_state (0)
   {
@@ -1781,105 +1800,105 @@ public:
     m_critical_state = state;
   }
 
-  label_text get_desc (bool can_colorize) const FINAL OVERRIDE
+  const callgraph_superedge& get_callgraph_superedge () const
+  {
+    gcc_assert (m_sedge->m_kind != SUPEREDGE_CFG_EDGE);
+    return *m_sedge->dyn_cast_callgraph_superedge ();
+  }
+
+  bool should_filter_p (int verbosity) const
   {
     switch (m_sedge->m_kind)
       {
-      default:
-	gcc_unreachable ();
       case SUPEREDGE_CFG_EDGE:
 	{
-	  bool user_facing = !flag_analyzer_verbose_edges;
-	  char *edge_desc = m_sedge->get_description (user_facing);
-	  if (user_facing)
-	    {
-	      if (edge_desc && strlen (edge_desc) > 0)
-		{
-		  label_text result
-		    = label_text::take (print (can_colorize,
-					       "following %qs branch",
-					       edge_desc));
-		  free (edge_desc);
-		  return result;
-		}
-	      else
-		{
-		  free (edge_desc);
-		  return label_text::borrow ("");
-		}
-	    }
-	  else
+	  if (verbosity < 2)
+	    return true;
+
+	  if (verbosity == 2)
 	    {
-	      if (strlen (edge_desc) > 0)
-		{
-		  label_text result
-		    = label_text::take (print (can_colorize,
-					       "taking %qs edge SN:%i -> SN:%i",
-					       edge_desc,
-					       m_sedge->m_src->m_index,
-					       m_sedge->m_dest->m_index));
-		  free (edge_desc);
-		  return result;
-		}
-	      else
-		{
-		  free (edge_desc);
-		  return label_text::take (print (can_colorize,
-						  "taking edge SN:%i -> SN:%i",
-						  m_sedge->m_src->m_index,
-						  m_sedge->m_dest->m_index));
-		}
+	      /* Filter events with empty descriptions.  This ought to filter
+		 FALLTHRU, but retain true/false/switch edges.  */
+	      label_text desc = get_desc (false);
+	      gcc_assert (desc.m_buffer);
+	      if (desc.m_buffer[0] == '\0')
+		return true;
+	      desc.maybe_free ();
 	    }
 	}
-      case SUPEREDGE_CALL:
-	{
-	  if (m_critical_state && m_pending_diagnostic)
-	    {
-	      gcc_assert (m_var);
-	      label_text custom_desc
-		= m_pending_diagnostic->describe_call_with_state
-		    (evdesc::call_with_state (m_sedge->m_src->m_fun->decl,
-					      m_sedge->m_dest->m_fun->decl,
-					      m_var,
-					      m_critical_state));
-	      if (custom_desc.m_buffer)
-		return custom_desc;
-	    }
+	break;
 
-	  return label_text::take (print (can_colorize,
-					  "calling %qE from %qE",
-					  m_sedge->m_dest->m_fun->decl,
-					  m_sedge->m_src->m_fun->decl));
-	}
+	default:
+	  break;
+      }
+    return false;
+  }
 
-      case SUPEREDGE_RETURN:
-	{
-	  /*  For greatest precision-of-wording, if this is returning the
-	      state involved in the pending diagnostic, give the pending
-	      diagnostic a chance to describe this return (in terms of
-	      itself).  */
-	  if (m_critical_state && m_pending_diagnostic)
-	    {
-	      label_text custom_desc
-		= m_pending_diagnostic->describe_return_of_state
-		    (evdesc::return_of_state (m_sedge->m_dest->m_fun->decl,
-					      m_sedge->m_src->m_fun->decl,
-					      m_critical_state));
-	      if (custom_desc.m_buffer)
-		return custom_desc;
-	    }
-	  return label_text::take (print (can_colorize,
-					  "returning to %qE from %qE",
-					  m_sedge->m_dest->m_fun->decl,
-					  m_sedge->m_src->m_fun->decl));
-	}
+  const exploded_edge &m_eedge;
+  const superedge *m_sedge;
+  tree m_var;
+  state_machine::state_t m_critical_state;
+};
 
-      case SUPEREDGE_INTRAPROCEDURAL_CALL:
-	return label_text::take (// TODO: possibly a higher-level description?
-				 print (can_colorize,
-					"taking edge SN:%i -> SN:%i",
-					m_sedge->m_src->m_index,
-					m_sedge->m_dest->m_index));
+/* An event subclass for when a CFG edge is followed.  */
+
+class cfg_edge_event : public superedge_event
+{
+public:
+  cfg_edge_event (const exploded_edge &eedge,
+		  location_t loc, tree fndecl, int depth)
+  : superedge_event (EK_CFG_EDGE, eedge, loc, fndecl, depth)
+  {
+    gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CFG_EDGE);
+  }
+
+  checker4_event *clone () const FINAL OVERRIDE
+  {
+    return new cfg_edge_event (m_eedge, m_loc, m_fndecl, m_depth);
+  }
+
+  label_text get_desc (bool can_colorize) const FINAL OVERRIDE
+  {
+    bool user_facing = !flag_analyzer_verbose_edges;
+    char *edge_desc = m_sedge->get_description (user_facing);
+    if (user_facing)
+      {
+	if (edge_desc && strlen (edge_desc) > 0)
+	  {
+	    label_text result
+	      = label_text::take (print (can_colorize,
+					 "following %qs branch",
+					 edge_desc));
+	    free (edge_desc);
+	    return result;
+	  }
+	else
+	  {
+	    free (edge_desc);
+	    return label_text::borrow ("");
+	  }
+      }
+    else
+      {
+	if (strlen (edge_desc) > 0)
+	  {
+	    label_text result
+	      = label_text::take (print (can_colorize,
+					 "taking %qs edge SN:%i -> SN:%i",
+					 edge_desc,
+					 m_sedge->m_src->m_index,
+					 m_sedge->m_dest->m_index));
+	    free (edge_desc);
+	    return result;
+	  }
+	else
+	  {
+	    free (edge_desc);
+	    return label_text::take (print (can_colorize,
+					    "taking edge SN:%i -> SN:%i",
+					    m_sedge->m_src->m_index,
+					    m_sedge->m_dest->m_index));
+	  }
       }
   }
 
@@ -1888,62 +1907,100 @@ public:
     gcc_assert (m_sedge->m_kind == SUPEREDGE_CFG_EDGE);
     return *m_sedge->dyn_cast_cfg_superedge ();
   }
+};
 
-  const callgraph_superedge& get_callgraph_superedge () const
+/* An event subclass for an interprocedural call.  */
+
+class call_event : public superedge_event
+{
+public:
+  call_event (const exploded_edge &eedge,
+	      location_t loc, tree fndecl, int depth)
+  : superedge_event (EK_CALL_EDGE, eedge, loc, fndecl, depth)
   {
-    gcc_assert (m_sedge->m_kind != SUPEREDGE_CFG_EDGE);
-    return *m_sedge->dyn_cast_callgraph_superedge ();
+    gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_CALL);
   }
 
   checker4_event *clone () const FINAL OVERRIDE
   {
-    return new edge_event (m_eedge, m_loc, m_fndecl, m_depth);
+    return new call_event (m_eedge, m_loc, m_fndecl, m_depth);
+  }
+
+  label_text get_desc (bool can_colorize) const FINAL OVERRIDE
+  {
+    if (m_critical_state && m_pending_diagnostic)
+      {
+	gcc_assert (m_var);
+	label_text custom_desc
+	  = m_pending_diagnostic->describe_call_with_state
+	  (evdesc::call_with_state (m_sedge->m_src->m_fun->decl,
+				    m_sedge->m_dest->m_fun->decl,
+				    m_var,
+				    m_critical_state));
+	if (custom_desc.m_buffer)
+	  return custom_desc;
+      }
+
+    return label_text::take (print (can_colorize,
+				    "calling %qE from %qE",
+				    m_sedge->m_dest->m_fun->decl,
+				    m_sedge->m_src->m_fun->decl));
   }
 
   bool is_call_p () const FINAL OVERRIDE
   {
     return m_sedge->m_kind == SUPEREDGE_CALL;
   }
-  bool is_return_p () const FINAL OVERRIDE
+};
+
+/* An event subclass for an interprocedural return.  */
+
+class return_event : public superedge_event
+{
+public:
+  return_event (const exploded_edge &eedge,
+		location_t loc, tree fndecl, int depth)
+  : superedge_event (EK_RETURN_EDGE, eedge, loc, fndecl, depth)
   {
-    return m_sedge->m_kind == SUPEREDGE_RETURN;
+    gcc_assert (eedge.m_sedge->m_kind == SUPEREDGE_RETURN);
   }
 
-  bool should_filter_p (int verbosity) const
+  checker4_event *clone () const FINAL OVERRIDE
   {
-    switch (m_sedge->m_kind)
-      {
-      case SUPEREDGE_CFG_EDGE:
-	{
-	  if (verbosity < 2)
-	    return true;
-
-	  if (verbosity == 2)
-	    {
-	      /* Filter events with empty descriptions.  This ought to filter
-		 FALLTHRU, but retain true/false/switch edges.  */
-	      label_text desc = get_desc (false);
-	      gcc_assert (desc.m_buffer);
-	      if (desc.m_buffer[0] == '\0')
-		return true;
-	      desc.maybe_free ();
-	    }
-	}
-	break;
+    return new return_event (m_eedge, m_loc, m_fndecl, m_depth);
+  }
 
-	default:
-	  break;
+  label_text get_desc (bool can_colorize) const FINAL OVERRIDE
+  {
+    /*  For greatest precision-of-wording, if this is returning the
+	state involved in the pending diagnostic, give the pending
+	diagnostic a chance to describe this return (in terms of
+	itself).  */
+    if (m_critical_state && m_pending_diagnostic)
+      {
+	label_text custom_desc
+	  = m_pending_diagnostic->describe_return_of_state
+	  (evdesc::return_of_state (m_sedge->m_dest->m_fun->decl,
+				    m_sedge->m_src->m_fun->decl,
+				    m_critical_state));
+	if (custom_desc.m_buffer)
+	  return custom_desc;
       }
-    return false;
+    return label_text::take (print (can_colorize,
+				    "returning to %qE from %qE",
+				    m_sedge->m_dest->m_fun->decl,
+				    m_sedge->m_src->m_fun->decl));
   }
 
-  const exploded_edge &m_eedge;
-  const superedge *m_sedge;
-  tree m_var;
-  state_machine::state_t m_critical_state;
+  bool is_return_p () const FINAL OVERRIDE
+  {
+    return m_sedge->m_kind == SUPEREDGE_RETURN;
+  }
 };
 
-/* FIXME: a repeat of the warning message at the end of the path.  */
+/* Subclass of checker4_event for use at the end of a path: a repeat of
+   the warning message at the end of the path, at the point where the
+   problem occurs.  */
 
 class warning_event : public checker4_event
 {
@@ -5030,17 +5087,26 @@ diagnostic_manager::add_events_for_superedge (const exploded_edge &eedge,
   const program_point &dst_point = dst_node->get_point ();
   const int src_stack_depth = src_point.get_stack_depth ();
   const int dst_stack_depth = dst_point.get_stack_depth ();
+  const gimple *last_stmt = src_point.m_supernode->get_last_stmt ();
 
   switch (eedge.m_sedge->m_kind)
     {
     case SUPEREDGE_CFG_EDGE:
+      {
+	emission_path->add_event
+	  (new cfg_edge_event (eedge,
+			       (last_stmt
+				? last_stmt->location
+				: UNKNOWN_LOCATION),
+			       src_point.m_supernode->m_fun->decl,
+			       src_stack_depth));
+      }
+      break;
+
     case SUPEREDGE_CALL:
-    case SUPEREDGE_INTRAPROCEDURAL_CALL:
       {
-	const gimple *last_stmt
-	  = src_point.m_supernode->get_last_stmt ();
 	emission_path->add_event
-	  (new edge_event (eedge,
+	  (new call_event (eedge,
 			   (last_stmt
 			    ? last_stmt->location
 			    : UNKNOWN_LOCATION),
@@ -5048,6 +5114,11 @@ diagnostic_manager::add_events_for_superedge (const exploded_edge &eedge,
 			   src_stack_depth));
       }
       break;
+
+    case SUPEREDGE_INTRAPROCEDURAL_CALL:
+      gcc_unreachable ();
+      break;
+
     case SUPEREDGE_RETURN:
       {
 	const return_superedge *return_edge
@@ -5057,12 +5128,12 @@ diagnostic_manager::add_events_for_superedge (const exploded_edge &eedge,
 
 	const gcall *call_stmt = return_edge->get_call_stmt ();
 	emission_path->add_event
-	  (new edge_event (eedge,
-			   (call_stmt
-			    ? call_stmt->location
-			    : UNKNOWN_LOCATION),
-			   dst_point.m_supernode->m_fun->decl,
-			   dst_stack_depth));
+	  (new return_event (eedge,
+			     (call_stmt
+			      ? call_stmt->location
+			      : UNKNOWN_LOCATION),
+			     dst_point.m_supernode->m_fun->decl,
+			     dst_stack_depth));
       }
       break;
     }
@@ -5292,9 +5363,46 @@ diagnostic_manager::prune_path (checker4_path *path,
 	  }
 	  break;
 
-	case EK_EDGE:
+	case EK_CFG_EDGE:
 	  {
-	    edge_event *event = (edge_event *)base_event;
+	    cfg_edge_event *event = (cfg_edge_event *)base_event;
+	    const cfg_superedge& cfg_superedge
+	      = event->get_cfg_superedge ();
+	    const supernode *dest = event->m_sedge->m_dest;
+	    /* Do we have an SSA_NAME defined via a phi node in
+	       the dest CFG node?  */
+	    if (var && TREE_CODE (var) == SSA_NAME)
+	      if (SSA_NAME_DEF_STMT (var)->bb == dest->m_bb)
+		{
+		  if (gphi *phi
+		      = dyn_cast <gphi *> (SSA_NAME_DEF_STMT (var)))
+		    {
+		      /* Update var based on its phi node.  */
+		      tree old_var = var;
+		      var = cfg_superedge.get_phi_arg (phi);
+		      log ("updating from %qE to %qE based on phi node",
+			   old_var, var);
+		      if (get_logger ())
+			{
+			  pretty_printer pp;
+			  pp_gimple_stmt_1 (&pp, phi, 0, (dump_flags_t)0);
+			  log ("  phi: %s", pp_formatted_text (&pp));
+			}
+		    }
+		}
+
+	    /* TODO: is this edge significant to var?
+	       See if var can be in other states in the dest, but not
+	       in other states in the src?
+	       Must have multiple sibling edges.  */
+
+	    if (event->should_filter_p (m_verbosity))
+	      {
+		log ("filtering event %i: CFG edge", idx);
+		path->delete_event (idx);
+	      }
+	  }
+	  break;
 
 #if 0
 	    const program_state &src_state
@@ -5308,114 +5416,71 @@ diagnostic_manager::prune_path (checker4_path *path,
 #endif
 
 #if 0
-	    inform (base_event->m_loc, "considering edge_event %i: %s",
+	    inform (base_event->m_loc, "considering superedge_event %i: %s",
 		    idx, base_event->m_desc);
 #endif
-	    switch (event->m_sedge->m_kind)
-	      {
-	      case SUPEREDGE_CFG_EDGE:
-		{
-		  const cfg_superedge& cfg_superedge
-		    = event->get_cfg_superedge ();
-		  const supernode *dest = event->m_sedge->m_dest;
-		  /* Do we have an SSA_NAME defined via a phi node in
-		     the dest CFG node?  */
-		  if (var && TREE_CODE (var) == SSA_NAME)
-		    if (SSA_NAME_DEF_STMT (var)->bb == dest->m_bb)
-		      {
-			if (gphi *phi
-			    = dyn_cast <gphi *> (SSA_NAME_DEF_STMT (var)))
-			  {
-			    /* Update var based on its phi node.  */
-			    tree old_var = var;
-			    var = cfg_superedge.get_phi_arg (phi);
-			    log ("updating from %qE to %qE based on phi node",
-				 old_var, var);
-			    if (get_logger ())
-			      {
-				pretty_printer pp;
-				pp_gimple_stmt_1 (&pp, phi, 0, (dump_flags_t)0);
-				log ("  phi: %s", pp_formatted_text (&pp));
-			      }
-			  }
-		      }
 
-		  /* TODO: is this edge significant to var?
-		     See if var can be in other states in the dest, but not
-		     in other states in the src?
-		     Must have multiple sibling edges.  */
-
-		  if (event->should_filter_p (m_verbosity))
-		    {
-		      log ("filtering event %i: CFG edge", idx);
-		      path->delete_event (idx);
-		    }
-		}
-		break;
+	case EK_CALL_EDGE:
+	  {
+	    call_event *event = (call_event *)base_event;
+	    const callgraph_superedge& cg_superedge
+	      = event->get_callgraph_superedge ();
+	    callsite_expr expr;
+	    tree caller_var
+	      = cg_superedge.map_expr_from_callee_to_caller (var, &expr);
+	    if (caller_var)
+	      {
+		log ("event %i:"
+		     " switching var of interest"
+		     " from %qE in callee to %qE in caller",
+		     idx, var, caller_var);
+		var = caller_var;
+		if (expr.param_p ())
+		  event->record_critical_state (var, state);
+	      }
+	    //#error
+	    /* potentially use this for precision-of-wording:
+	       identify passing a parm (and which parm idx)
+	       then update the superedge_event to note that it's a
+	       critical state-related edge (with the detail, maybe as an
+	       optional object).
+	       Then update the get_desc to use the detail, passing
+	       it to the pending_diagnostic to get a precise message
+	       e.g. passing possibly-NULL pointer 'ptr' to 'foo' from 'bar' as param 1
+	       or somesuch
+	    */
+	  }
+	  break;
 
-	      case SUPEREDGE_CALL:
-		{
-		  const callgraph_superedge& cg_superedge
-		    = event->get_callgraph_superedge ();
-		  callsite_expr expr;
-		  tree caller_var
-		    = cg_superedge.map_expr_from_callee_to_caller (var, &expr);
-		  if (caller_var)
-		    {
-		      log ("event %i:"
-			   " switching var of interest"
-			   " from %qE in callee to %qE in caller",
-			   idx, var, caller_var);
-		      var = caller_var;
-		      if (expr.param_p ())
-			event->record_critical_state (var, state);
-		    }
-		  //#error
-		  /* potentially use this for precision-of-wording:
-		     identify passing a parm (and which parm idx)
-		     then update the edge_event to note that it's a critical
-		     state-related edge (with the detail, maybe as an optional
-		     object).
-		     Then update the get_desc to use the detail, passing
-		     it to the pending_diagnostic to get a precise message
-		     e.g. passing possibly-NULL pointer 'ptr' to 'foo' from 'bar' as param 1
-		     or somesuch
-		   */
-		}
-		break;
-	      case SUPEREDGE_RETURN:
-		// TODO: potentially update var/state based on return value,
-		// args etc
-		{
-		  const callgraph_superedge& cg_superedge
-		    = event->get_callgraph_superedge ();
-		  callsite_expr expr;
-		  tree callee_var
-		    = cg_superedge.map_expr_from_caller_to_callee (var, &expr);
-		  if (callee_var)
-		    {
-		      if (var)
-			log ("event %i:"
-			     " switching var of interest"
-			     " from %qE in caller to %qE in callee",
-			     idx, var, callee_var);
-		      else
-			// FIXME: how does this happen?
-			log ("event %i:"
-			     " switching var of interest"
-			     " from NULL in caller to %qE in callee",
-			     idx, callee_var);
-		      var = callee_var;
-		      if (expr.return_value_p ())
-			event->record_critical_state (var, state);
-		    }
-		  /* FIXME: similar precision-of-wording notes to above,
-		     but for return value.  */
-		}
-		break;
-	      case SUPEREDGE_INTRAPROCEDURAL_CALL:
-		break;
+	case EK_RETURN_EDGE:
+	  // TODO: potentially update var/state based on return value,
+	  // args etc
+	  {
+	    return_event *event = (return_event *)base_event;
+	    const callgraph_superedge& cg_superedge
+	      = event->get_callgraph_superedge ();
+	    callsite_expr expr;
+	    tree callee_var
+	      = cg_superedge.map_expr_from_caller_to_callee (var, &expr);
+	    if (callee_var)
+	      {
+		if (var)
+		  log ("event %i:"
+		       " switching var of interest"
+		       " from %qE in caller to %qE in callee",
+		       idx, var, callee_var);
+		else
+		  // FIXME: how does this happen?
+		  log ("event %i:"
+		       " switching var of interest"
+		       " from NULL in caller to %qE in callee",
+		       idx, callee_var);
+		var = callee_var;
+		if (expr.return_value_p ())
+		  event->record_critical_state (var, state);
 	      }
+	    /* FIXME: similar precision-of-wording notes to above,
+	       but for return value.  */
 	  }
 	  break;
 
-- 
1.8.5.3

