From 1a1b48c3d7731dacb649f257aa36a47191a64db7 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 30 Jul 2019 10:32:34 -0400
Subject: [PATCH 041/124] FIXME: eliminate on_edge vfunc in favor of
 on_condition

---
 gcc/analyzer/impl-4.cc                         | 27 +++------
 gcc/analyzer/region-model.cc                   |  8 +--
 gcc/analyzer/region-model.h                    | 11 +++-
 gcc/analyzer/sm-file.cc                        | 74 ++++++++---------------
 gcc/analyzer/sm-malloc.cc                      | 53 ++--------------
 gcc/analyzer/sm-pattern-test.cc                | 73 ++++++----------------
 gcc/analyzer/sm-sensitive.cc                   | 36 ++++-------
 gcc/analyzer/sm-taint.cc                       | 84 ++++++++++----------------
 gcc/analyzer/sm.cc                             | 31 ++++++++++
 gcc/analyzer/sm.h                              |  6 +-
 gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c | 18 +++++-
 gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c | 29 +++++++++
 12 files changed, 193 insertions(+), 257 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c

diff --git a/gcc/analyzer/impl-4.cc b/gcc/analyzer/impl-4.cc
index cad86b7..eba413c 100644
--- a/gcc/analyzer/impl-4.cc
+++ b/gcc/analyzer/impl-4.cc
@@ -2500,6 +2500,8 @@ program_point::get_stmt () const
 {
   if (m_kind == PK_BEFORE_STMT)
     return m_supernode->m_stmts[m_stmt_idx];
+  else if (m_kind == PK_AFTER_SUPERNODE)
+    return m_supernode->get_last_stmt ();
   else
     return NULL;
 }
@@ -3474,7 +3476,9 @@ impl_region_model_context::on_inherited_svalue (svalue_id parent_sid,
     }
 }
 
-/* FIXME.  */
+/* Implementation of region_model_context::on_condition vfunc.
+   Notify all state machines about the condition, which could lead to
+   state transitions.  */
 
 void
 impl_region_model_context::on_condition (tree lhs, enum tree_code op, tree rhs)
@@ -3703,14 +3707,16 @@ program_state::on_edge (exploded_graph &eg,
     }
 
   const program_point &point = enode.get_point ();
-  const program_state &old_state = enode.get_state ();
   const gimple *last_stmt = point.m_supernode->get_last_stmt ();
 
   /* For conditionals and switch statements, add the
      relevant conditions (for the specific edge) to new_state;
      skip edges for which the resulting constraints
      are impossible.
-     This also updates frame information for call/return superedges.  */
+     This also updates frame information for call/return superedges.
+     Adding the relevant conditions for the edge could also trigger
+     sm-state transitions (e.g. transitions due to ptrs becoming known
+     to be NULL or non-NULL) */
 
   impl_region_model_context ctxt (eg, &enode, this, NULL);
   if (!m_region_model->maybe_update_for_edge (*succ,
@@ -3722,21 +3728,6 @@ program_state::on_edge (exploded_graph &eg,
       return false;
     }
 
-  /* Pass the edge to the various state machines to see if any transitions
-     or warnings occur (e.g. transitions due to ptrs becoming known to be NULL
-     or non-NULL).  */
-  int i;
-  sm_state_map *old_smap;
-  FOR_EACH_VEC_ELT (old_state.m_checker_states, i, old_smap)
-    {
-      const state_machine &sm = eg.get_ext_state ().get_sm (i);
-      impl_sm_context ctxt (eg, sm, &enode,
-			    this,
-			    old_state.m_checker_states[i],
-			    m_checker_states[i]);
-      sm.on_edge (&ctxt, last_stmt, succ);
-    }
-
   return true;
 }
 
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 1071e62..239a112 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -3747,10 +3747,10 @@ region_model::add_constraint (tree lhs, enum tree_code op, tree rhs,
 	}
     }
 
-  /* Notify the context, if any.  This is used to notify program_state
-     FIXME... and thus to set sm-state for e.g. unchecked->checked when
-     synthesizing constraints as above.
-     FIXME: could also use it for edges: but would we need a return value?  */
+  /* Notify the context, if any.  This exists so that the state machines
+     in a program_state can be notified about the condition, and so can
+     set sm-state for e.g. unchecked->checked, both for cfg-edges, and
+     when synthesizing constraints as above.  */
   if (ctxt)
     ctxt->on_condition (lhs, op, rhs);
 
diff --git a/gcc/analyzer/region-model.h b/gcc/analyzer/region-model.h
index d6de239..04522ef 100644
--- a/gcc/analyzer/region-model.h
+++ b/gcc/analyzer/region-model.h
@@ -1267,7 +1267,12 @@ class region_model_context
   virtual void on_inherited_svalue (svalue_id parent_sid,
 				    svalue_id child_sid) = 0;
 
-  /* FIXME.  */
+  /* Hook for clients to be notified when the condition
+     "LHS OP RHS" is added to the region model.
+     This exists so that state machines can detect tests on edges,
+     and use them to trigger sm-state transitions (e.g. transitions due
+     to ptrs becoming known to be NULL or non-NULL, rather than just
+     "unchecked") */
   virtual void on_condition (tree lhs, enum tree_code op, tree rhs) = 0;
 };
 
@@ -1430,7 +1435,9 @@ public:
 
   unsigned get_num_diagnostics () const { return m_diagnostics.length (); }
 
-  void on_condition (tree lhs, enum tree_code op, tree rhs) FINAL OVERRIDE
+  void on_condition (tree lhs ATTRIBUTE_UNUSED,
+		     enum tree_code op ATTRIBUTE_UNUSED,
+		     tree rhs ATTRIBUTE_UNUSED) FINAL OVERRIDE
   {
   }
 
diff --git a/gcc/analyzer/sm-file.cc b/gcc/analyzer/sm-file.cc
index cba2c95..ee3f9b0 100644
--- a/gcc/analyzer/sm-file.cc
+++ b/gcc/analyzer/sm-file.cc
@@ -72,10 +72,6 @@ public:
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
-  void on_edge (sm_context *sm_ctxt,
-		const gimple *last_stmt,
-		const superedge *edge) const FINAL OVERRIDE;
-
   void on_condition (sm_context *sm_ctxt,
 		     const supernode *node,
 		     const gimple *stmt,
@@ -288,50 +284,6 @@ fileptr_state_machine::on_stmt (sm_context *sm_ctxt,
 /* FIXME.  */
 
 void
-fileptr_state_machine::on_edge (sm_context *sm_ctxt,
-				const gimple *last_stmt,
-				const superedge *edge) const
-{
-  if (last_stmt == NULL)
-    return;
-
-  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    {
-      tree arg;
-      enum tree_code op;
-      if (is_comparison_against_zero (cond_stmt, &arg, &op))
-	{
-	  // FIXME: has to be a FILE *, specifically
-	  if (TREE_CODE (TREE_TYPE (arg)) != POINTER_TYPE)
-	    return;
-
-	  ::edge cfg_edge = edge->get_any_cfg_edge ();
-	  gcc_assert (cfg_edge != NULL);
-	  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
-	  
-	  if (op == NE_EXPR)
-	    {
-	      log ("got 'ARG != 0' match");
-	      sm_ctxt->on_transition (edge->m_src, last_stmt, arg, m_unchecked,
-				      (cfg_edge->flags & EDGE_TRUE_VALUE
-				       ? m_nonnull : m_null));
-	    }
-	  else if (op == EQ_EXPR)
-	    {
-	      log ("got 'ARG == 0' match");
-	      sm_ctxt->on_transition (edge->m_src, last_stmt, arg, m_unchecked,
-				      (cfg_edge->flags & EDGE_TRUE_VALUE
-				       ? m_null : m_nonnull));
-	    }
-	}
-    }
-
-  // TODO: something similar for switch statements?
-}
-
-/* FIXME.  */
-
-void
 fileptr_state_machine::on_condition (sm_context *sm_ctxt,
 				     const supernode *node,
 				     const gimple *stmt,
@@ -340,6 +292,32 @@ fileptr_state_machine::on_condition (sm_context *sm_ctxt,
 				     tree rhs) const
 {
   // TODO
+  // TODO
+  if (!zerop (rhs))
+    return;
+
+#if 1
+  // FIXME: has to be a FILE *, specifically
+  if (TREE_CODE (TREE_TYPE (lhs)) != POINTER_TYPE)
+    return;
+
+  // FIXME: has to be a FILE *, specifically
+  if (TREE_CODE (TREE_TYPE (rhs)) != POINTER_TYPE)
+    return;
+#endif
+
+  if (op == NE_EXPR)
+    {
+      log ("got 'ARG != 0' match");
+      sm_ctxt->on_transition (node, stmt,
+			      lhs, m_unchecked, m_nonnull);
+    }
+  else if (op == EQ_EXPR)
+    {
+      log ("got 'ARG == 0' match");
+      sm_ctxt->on_transition (node, stmt,
+			      lhs, m_unchecked, m_null);
+    }
 }
 
 void
diff --git a/gcc/analyzer/sm-malloc.cc b/gcc/analyzer/sm-malloc.cc
index 9558b1c..f901891 100644
--- a/gcc/analyzer/sm-malloc.cc
+++ b/gcc/analyzer/sm-malloc.cc
@@ -73,10 +73,6 @@ public:
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
-  void on_edge (sm_context *sm_ctxt,
-		const gimple *last_stmt,
-		const superedge *edge) const FINAL OVERRIDE;
-
   void on_condition (sm_context *sm_ctxt,
 		     const supernode *node,
 		     const gimple *stmt,
@@ -763,49 +759,6 @@ malloc_state_machine::on_stmt (sm_context *sm_ctxt,
 /* FIXME.  */
 
 void
-malloc_state_machine::on_edge (sm_context *sm_ctxt,
-			       const gimple *last_stmt,
-			       const superedge *edge) const
-{
-  if (last_stmt == NULL)
-    return;
-
-  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    {
-      tree arg;
-      enum tree_code op;
-      if (is_comparison_against_zero (cond_stmt, &arg, &op))
-	{
-	  if (!any_pointer_p (arg))
-	    return;
-
-	  ::edge cfg_edge = edge->get_any_cfg_edge ();
-	  gcc_assert (cfg_edge != NULL);
-	  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
-	  
-	  if (op == NE_EXPR)
-	    {
-	      log ("got 'ARG != 0' match");
-	      sm_ctxt->on_transition (edge->m_src, last_stmt, arg, m_unchecked,
-				      (cfg_edge->flags & EDGE_TRUE_VALUE
-				       ? m_nonnull : m_null));
-	    }
-	  else if (op == EQ_EXPR)
-	    {
-	      log ("got 'ARG == 0' match");
-	      sm_ctxt->on_transition (edge->m_src, last_stmt, arg, m_unchecked,
-				      (cfg_edge->flags & EDGE_TRUE_VALUE
-				       ? m_null : m_nonnull));
-	    }
-	}
-    }
-
-  // TODO: something similar for switch statements?
-}
-
-/* FIXME.  */
-
-void
 malloc_state_machine::on_condition (sm_context *sm_ctxt,
 				    const supernode *node,
 				    const gimple *stmt,
@@ -813,10 +766,14 @@ malloc_state_machine::on_condition (sm_context *sm_ctxt,
 				    enum tree_code op,
 				    tree rhs) const
 {
-  // TODO
   if (!zerop (rhs))
     return;
 
+  if (!any_pointer_p (lhs))
+    return;
+  if (!any_pointer_p (rhs))
+    return;
+
   if (op == NE_EXPR)
     {
       log ("got 'ARG != 0' match");
diff --git a/gcc/analyzer/sm-pattern-test.cc b/gcc/analyzer/sm-pattern-test.cc
index 8aae9ac..cb99662 100644
--- a/gcc/analyzer/sm-pattern-test.cc
+++ b/gcc/analyzer/sm-pattern-test.cc
@@ -74,10 +74,6 @@ public:
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
-  void on_edge (sm_context *sm_ctxt,
-		const gimple *last_stmt,
-		const superedge *edge) const FINAL OVERRIDE;
-
   void on_condition (sm_context *sm_ctxt,
 		     const supernode *node,
 		     const gimple *stmt,
@@ -101,26 +97,28 @@ private:
 class pattern_match : public pending_diagnostic_subclass<pattern_match>
 {
 public:
-  pattern_match (tree arg, const char *msg) : m_arg (arg), m_msg (msg) {}
+  pattern_match (tree lhs, enum tree_code op, tree rhs)
+  : m_lhs (lhs), m_op (op), m_rhs (rhs) {}
 
   const char *get_kind () const FINAL OVERRIDE { return "pattern_match"; }
 
   bool operator== (const pattern_match &other) const
   {
-    return (m_arg == other.m_arg
-	    // FIXME: should this be string equality?
-	    && m_msg == other.m_msg);
+    return (m_lhs == other.m_lhs
+	    && m_op == other.m_op
+	    && m_rhs == other.m_rhs);
   }
 
   void emit (rich_location *rich_loc) FINAL OVERRIDE
   {
-    // TODO:
-    warning_at (rich_loc, 0, "pattern match on %qE: %s", m_arg, m_msg);
+    warning_at (rich_loc, 0, "pattern match on %<%E %s %E%>",
+		m_lhs, op_symbol_code (m_op), m_rhs);
   }
 
 private:
-  tree m_arg;
-  const char *m_msg;
+  tree m_lhs;
+  enum tree_code m_op;
+  tree m_rhs;
 };
 
 ////////////////////////////////////////////////////////////////////////////
@@ -151,48 +149,6 @@ pattern_test_state_machine::on_stmt (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
 /* FIXME.  */
 
 void
-pattern_test_state_machine::on_edge (sm_context *sm_ctxt,
-				     const gimple *last_stmt,
-				     const superedge *edge) const
-{
-  if (last_stmt == NULL)
-    return;
-
-  const supernode *node = edge->m_src;
-
-  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    {
-      tree arg;
-      enum tree_code op;
-      if (is_comparison_against_zero (cond_stmt, &arg, &op))
-	{
-	  ::edge cfg_edge = edge->get_any_cfg_edge ();
-	  gcc_assert (cfg_edge != NULL);
-	  gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
-
-	  bool is_true = cfg_edge->flags & EDGE_TRUE_VALUE;
-
-	  if (op == NE_EXPR)
-	    {
-	      pending_diagnostic *diag
-		= new pattern_match (arg,
-				     is_true ? "!= 0" : "== 0");
-	      sm_ctxt->warn_for_state (node, last_stmt, arg, m_start, diag);
-	    }
-	  else if (op == EQ_EXPR)
-	    {
-	      pending_diagnostic *diag
-		= new pattern_match (arg,
-				     is_true ? "== 0" : "!= 0");
-	      sm_ctxt->warn_for_state (node, last_stmt, arg, m_start, diag);
-	    }
-	}
-    }
-}
-
-/* FIXME.  */
-
-void
 pattern_test_state_machine::on_condition (sm_context *sm_ctxt,
 					  const supernode *node,
 					  const gimple *stmt,
@@ -200,7 +156,14 @@ pattern_test_state_machine::on_condition (sm_context *sm_ctxt,
 					  enum tree_code op,
 					  tree rhs) const
 {
-  // TODO
+  if (stmt == NULL)
+    return;
+
+  if (!CONSTANT_CLASS_P (rhs))
+    return;
+
+  pending_diagnostic *diag = new pattern_match (lhs, op, rhs);
+  sm_ctxt->warn_for_state (node, stmt, lhs, m_start, diag);
 }
 
 void
diff --git a/gcc/analyzer/sm-sensitive.cc b/gcc/analyzer/sm-sensitive.cc
index dbfc126..51a6a69 100644
--- a/gcc/analyzer/sm-sensitive.cc
+++ b/gcc/analyzer/sm-sensitive.cc
@@ -73,14 +73,12 @@ public:
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
-  void on_edge (sm_context *sm_ctxt,
-		const gimple *last_stmt,
-		const superedge *edge) const FINAL OVERRIDE;
-
-void on_condition (sm_context *sm_ctxt,
-		   const supernode *node,
-		   const gimple *stmt,
-		   tree lhs, enum tree_code op, tree rhs) const FINAL OVERRIDE;
+  void on_condition (sm_context *sm_ctxt,
+		     const supernode *node,
+		     const gimple *stmt,
+		     tree lhs,
+		     enum tree_code op,
+		     tree rhs) const FINAL OVERRIDE;
 
   void on_leak (sm_context *sm_ctxt,
 		const supernode *node,
@@ -191,22 +189,12 @@ sensitive_state_machine::on_stmt (sm_context *sm_ctxt,
 /* FIXME.  */
 
 void
-sensitive_state_machine::on_edge (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
-				  const gimple *last_stmt ATTRIBUTE_UNUSED,
-				  const superedge *edge ATTRIBUTE_UNUSED) const
-{
-  /* Empty.  */
-}
-
-/* FIXME.  */
-
-void
-sensitive_state_machine::on_condition (sm_context *sm_ctxt,
-				       const supernode *node,
-				       const gimple *stmt,
-				       tree lhs,
-				       enum tree_code op,
-				       tree rhs) const
+sensitive_state_machine::on_condition (sm_context *sm_ctxt ATTRIBUTE_UNUSED,
+				       const supernode *node ATTRIBUTE_UNUSED,
+				       const gimple *stmt ATTRIBUTE_UNUSED,
+				       tree lhs ATTRIBUTE_UNUSED,
+				       enum tree_code op ATTRIBUTE_UNUSED,
+				       tree rhs ATTRIBUTE_UNUSED) const
 {
   /* Empty.  */
 }
diff --git a/gcc/analyzer/sm-taint.cc b/gcc/analyzer/sm-taint.cc
index e8c15b9..1cd1c4d 100644
--- a/gcc/analyzer/sm-taint.cc
+++ b/gcc/analyzer/sm-taint.cc
@@ -74,10 +74,6 @@ public:
 		const supernode *node,
 		const gimple *stmt) const FINAL OVERRIDE;
 
-  void on_edge (sm_context *sm_ctxt,
-		const gimple *last_stmt,
-		const superedge *edge) const FINAL OVERRIDE;
-
   void on_condition (sm_context *sm_ctxt,
 		     const supernode *node,
 		     const gimple *stmt,
@@ -235,62 +231,44 @@ taint_state_machine::on_stmt (sm_context *sm_ctxt,
 /* FIXME.  */
 
 void
-taint_state_machine::on_edge (sm_context *sm_ctxt,
-			       const gimple *last_stmt,
-			       const superedge *edge) const
-{
-  if (last_stmt == NULL)
-    return;
-
-  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    {
-      enum tree_code op = gimple_cond_code (cond_stmt);
-      tree lhs = gimple_cond_lhs (cond_stmt);
-      //tree rhs = gimple_cond_rhs (cond_stmt);
-      // TODO: make it symmetric?
-
-      switch (op)
-	{
-	  //case NE_EXPR:
-	  //case EQ_EXPR:
-	case GE_EXPR:
-	case GT_EXPR:
-	  {
-	    sm_ctxt->on_transition (edge->m_src, last_stmt, lhs, m_tainted,
-				    m_has_lb);
-	    sm_ctxt->on_transition (edge->m_src, last_stmt, lhs, m_has_ub,
-				    m_stop);
-	  }
-	  break;
-	case LE_EXPR:
-	case LT_EXPR:
-	  {
-	    sm_ctxt->on_transition (edge->m_src, last_stmt, lhs, m_tainted,
-				    m_has_ub);
-	    sm_ctxt->on_transition (edge->m_src, last_stmt, lhs, m_has_lb,
-				    m_stop);
-	  }
-	  break;
-
-	default:
-	  break;
-	}
-    }
-
-  // TODO: something similar for switch statements?
-}
-
-/* FIXME.  */
-
-void
 taint_state_machine::on_condition (sm_context *sm_ctxt,
 				   const supernode *node,
 				   const gimple *stmt,
 				   tree lhs,
 				   enum tree_code op,
-				   tree rhs) const
+				   tree rhs ATTRIBUTE_UNUSED) const
 {
+  if (stmt == NULL)
+    return;
+
+  // TODO: this doesn't use the RHS; should we make it symmetric?
+
   // TODO
+  switch (op)
+    {
+      //case NE_EXPR:
+      //case EQ_EXPR:
+    case GE_EXPR:
+    case GT_EXPR:
+      {
+	sm_ctxt->on_transition (node, stmt, lhs, m_tainted,
+				m_has_lb);
+	sm_ctxt->on_transition (node, stmt, lhs, m_has_ub,
+				m_stop);
+      }
+      break;
+    case LE_EXPR:
+    case LT_EXPR:
+      {
+	sm_ctxt->on_transition (node, stmt, lhs, m_tainted,
+				m_has_ub);
+	sm_ctxt->on_transition (node, stmt, lhs, m_has_lb,
+				m_stop);
+      }
+      break;
+    default:
+      break;
+    }
 }
 
 void
diff --git a/gcc/analyzer/sm.cc b/gcc/analyzer/sm.cc
index 8efc598..a6912e7 100644
--- a/gcc/analyzer/sm.cc
+++ b/gcc/analyzer/sm.cc
@@ -127,6 +127,37 @@ state_machine::get_state_name (state_t s) const
   return m_state_names[s];
 }
 
+/* FIXME: get rid of this; on_condition should handle it from now on
+   (it's only used by impl-2 and imp-3, I think).  */
+
+void
+state_machine::on_edge (sm_context *sm_ctxt,
+			const gimple *last_stmt,
+			const superedge *edge) const
+{
+  if (last_stmt == NULL)
+    return;
+
+  if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
+    {
+      ::edge cfg_edge = edge->get_any_cfg_edge ();
+      gcc_assert (cfg_edge != NULL);
+      gcc_assert (cfg_edge->flags & (EDGE_TRUE_VALUE | EDGE_FALSE_VALUE));
+
+      tree lhs = gimple_cond_lhs (cond_stmt);
+      enum tree_code op = gimple_cond_code (cond_stmt);
+      tree rhs = gimple_cond_rhs (cond_stmt);
+
+      if (cfg_edge->flags & EDGE_FALSE_VALUE)
+	op = invert_tree_comparison (op, false /* honor_nans */);
+      on_condition (sm_ctxt, edge->m_src, last_stmt,
+		    lhs, op, rhs);
+    }
+
+  // TODO: something similar for switch statements?
+
+}
+
 void
 state_machine::validate (state_t s) const
 {
diff --git a/gcc/analyzer/sm.h b/gcc/analyzer/sm.h
index 1f64356..020d88e 100644
--- a/gcc/analyzer/sm.h
+++ b/gcc/analyzer/sm.h
@@ -71,9 +71,9 @@ public:
 			const supernode *node,
 			const gimple *stmt) const = 0;
 
-  virtual void on_edge (sm_context *sm_ctxt,
-			const gimple *last_stmt,
-			const superedge *edge) const = 0;
+  void on_edge (sm_context *sm_ctxt,
+		const gimple *last_stmt,
+		const superedge *edge) const;
 
   virtual void on_condition (sm_context *sm_ctxt,
 			     const supernode *node,
diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
index b68e605..8a1ca58 100644
--- a/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
+++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
@@ -1,14 +1,28 @@
 /* { dg-additional-options "-fanalyzer-checker=pattern-test" } */
 
+#include <stdlib.h>
+
 extern void foo(void *);
 extern void bar(void *);
 
 void test1(void *ptr)
 {
-  if (ptr) { /* { dg-warning "pattern match on 'ptr': != 0" } */
-  /* { dg-warning "pattern match on 'ptr': == 0" "" { target *-*-* } .-1 } */
+  if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */
+  /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */
     foo(ptr);
   } else {
     bar(ptr);
   }
 }
+
+void test_2 (void *p, void *q)
+{
+  if (p == NULL || q == NULL) /* { dg-line cond_2 }  */
+    return;
+  foo(p);
+
+  /* { dg-warning "pattern match on 'p == 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'q == 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */
+}
diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
new file mode 100644
index 0000000..3924900
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-2.c
@@ -0,0 +1,29 @@
+/* { dg-additional-options "-fanalyzer-checker=pattern-test -O2" } */
+// TODO: run this at every optimization level
+
+#include <stdlib.h>
+
+extern void foo(void *);
+extern void bar(void *);
+
+void test1(void *ptr)
+{
+  if (ptr) { /* { dg-warning "pattern match on 'ptr != 0'" } */
+  /* { dg-warning "pattern match on 'ptr == 0'" "" { target *-*-* } .-1 } */
+    foo(ptr);
+  } else {
+    bar(ptr);
+  }
+}
+
+void test_2 (void *p, void *q)
+{
+  if (p == NULL || q == NULL) /* { dg-line cond_2 }  */
+    return;
+  foo(p);
+
+  /* { dg-warning "pattern match on '<unknown> == 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on '<unknown> != 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'p != 0'" "" { target *-*-* } cond_2 } */
+  /* { dg-warning "pattern match on 'q != 0'" "" { target *-*-* } cond_2 } */
+}
-- 
1.8.5.3

