From 958f702149f5e26ec5c1c9f1ff89bd3199d1a1ca Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 12 Mar 2019 12:35:43 -0400
Subject: [PATCH 093/161] FIXME: start adding pattern test

---
 gcc/analyzer/Make-lang.in                          |   1 +
 gcc/analyzer/engine.cc                             |  12 +-
 .../hardcoded-state-machine-pattern-test.cc        | 131 +++++++++++++++
 gcc/analyzer/hardcoded-state-machine.cc            |   1 +
 gcc/analyzer/hardcoded-state-machine.h             |  36 +++++
 .../TODO-analyzer/patterns/conditional/input.c     |  30 ----
 .../conditional/input.c.cpychecker-log.txt         |   1 -
 ...ough_uninitialized_pointer-refcount-errors.html | 175 ---------------------
 .../TODO-analyzer/patterns/conditional/script.py   |  35 -----
 .../TODO-analyzer/patterns/conditional/stderr.txt  |   2 -
 .../TODO-analyzer/patterns/conditional/stdout.txt  |   0
 gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c     |  14 ++
 12 files changed, 194 insertions(+), 244 deletions(-)
 create mode 100644 gcc/analyzer/hardcoded-state-machine-pattern-test.cc
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.cpychecker-log.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.test_read_through_uninitialized_pointer-refcount-errors.html
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/script.py
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/stderr.txt
 delete mode 100644 gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/stdout.txt
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c

diff --git a/gcc/analyzer/Make-lang.in b/gcc/analyzer/Make-lang.in
index db632ac..a838bb3 100644
--- a/gcc/analyzer/Make-lang.in
+++ b/gcc/analyzer/Make-lang.in
@@ -63,6 +63,7 @@ ANALYZER_PLUGIN_SO_OBJS = \
 	analyzer/hardcoded-state-machine.o \
 	analyzer/hardcoded-state-machine-malloc.o \
 	analyzer/hardcoded-state-machine-file.o \
+	analyzer/hardcoded-state-machine-pattern-test.o \
 	analyzer/hardcoded-state-machine-sensitive.o \
 	analyzer/hardcoded-state-machine-taint.o \
 	analyzer/supergraph.o \
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index f2c84d5..2413d23 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -1141,7 +1141,8 @@ public:
     gcc_assert (d); // take ownership
 
     var_state_t vs_from (var, state);
-    if (!const_cast <sm_instance *> (m_old_sm_inst)->m_active_vars.m_set.contains (vs_from))
+    if (!start_start_p (state) &&
+	!const_cast <sm_instance *> (m_old_sm_inst)->m_active_vars.m_set.contains (vs_from))
       {
 	if (m_engine->get_logger ())
 	  m_engine->log ("not in state %s; no error",
@@ -2896,6 +2897,15 @@ run_checkers ()
       run_one_checker (sg, sm, the_logger.get_logger ());
     }
 
+    /* We only attempt to run the pattern tests if it might have been manually
+       enabled (for DejaGnu purposes).  */
+    if (flag_analyzer_checker)
+      {
+	auto_client_timevar tv ("pattern_test_state_machine");
+	pattern_test_state_machine sm (the_logger.get_logger ());
+	run_one_checker (sg, sm, the_logger.get_logger ());
+      }
+
     /* end of lifetime of the_logger (so that dump file is closed after the
        various dtors run).  */
   }
diff --git a/gcc/analyzer/hardcoded-state-machine-pattern-test.cc b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
new file mode 100644
index 0000000..7f1460c
--- /dev/null
+++ b/gcc/analyzer/hardcoded-state-machine-pattern-test.cc
@@ -0,0 +1,131 @@
+/* FIXME.  */
+#include "config.h"
+#include "gcc-plugin.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "tm.h"
+#include "toplev.h"
+#include "hash-table.h"
+#include "vec.h"
+#include "ggc.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "intl.h"
+#include "context.h"
+#include "diagnostic.h"
+#include "bitmap.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-pretty-print.h"
+#include "tree-pretty-print.h"
+#include "analyzer.h"
+#include "graphviz.h"
+#include "cgraph.h"
+#include "diagnostic-path.h"
+#include "gcc-rich-location.h"
+#include <string>
+#include "supergraph.h"
+#include "hardcoded-state-machine.h"
+
+static const bool DEBUG = false;
+//static const bool DEBUG = true;
+
+////////////////////////////////////////////////////////////////////////////
+
+class pattern_match : public pending_diagnostic
+{
+public:
+  pattern_match (tree arg, const char *msg) : m_arg (arg), m_msg (msg) {}
+
+  void emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    // TODO:
+    warning_at (rich_loc, 0, "pattern match on %qE: %s", m_arg, m_msg);
+  }
+
+private:
+  tree m_arg;
+  const char *m_msg;
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+pattern_test_state_machine::pattern_test_state_machine (logger *logger)
+: state_machine ("pattern-test", logger)
+{
+  // TODO: parse all of this from a file:
+  m_start = add_state ("start");
+}
+
+void
+pattern_test_state_machine::on_stmt (sm_context *sm_ctxt,
+				     const supernode *node,
+				     const gimple *stmt) const
+{
+  //tree fndecl = node->m_fun->decl;
+
+  // FIXME: do all of this from data
+  if (const gcall *call = dyn_cast <const gcall *> (stmt))
+    {
+    }
+}
+
+/* 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);
+	    }
+	}
+    }
+}
+
+void
+pattern_test_state_machine::on_leak (sm_context *sm_ctxt,
+				     const supernode *node,
+				     const gimple *stmt,
+				     tree var,
+				     state_machine::state_t state) const
+{
+  /* Empty.  */
+}
diff --git a/gcc/analyzer/hardcoded-state-machine.cc b/gcc/analyzer/hardcoded-state-machine.cc
index 69cedbe..fcfb6d7 100644
--- a/gcc/analyzer/hardcoded-state-machine.cc
+++ b/gcc/analyzer/hardcoded-state-machine.cc
@@ -117,4 +117,5 @@ make_checkers (auto_delete_vec <state_machine> &out, logger *logger)
   out.safe_push (new fileptr_state_machine (logger));
   out.safe_push (new taint_state_machine (logger));
   out.safe_push (new sensitive_state_machine (logger));
+  // we don't add pattern_test_state_machine by default
 }
diff --git a/gcc/analyzer/hardcoded-state-machine.h b/gcc/analyzer/hardcoded-state-machine.h
index 1cbbb52..0d3992b 100644
--- a/gcc/analyzer/hardcoded-state-machine.h
+++ b/gcc/analyzer/hardcoded-state-machine.h
@@ -51,6 +51,14 @@ private:
   auto_vec<const char *> m_state_names;
 };
 
+/* FIXME.  */
+
+static inline bool
+start_start_p (state_machine::state_t state)
+{
+  return state == 0;
+}
+
 ////////////////////////////////////////////////////////////////////////////
 
 /* FIXME.  */
@@ -253,3 +261,31 @@ private:
   state_t m_sensitive;
   state_t m_stop;
 };
+
+////////////////////////////////////////////////////////////////////////////
+
+/* A particular state machine, for prototyping purposes:
+   a pattern-matching test, for DejaGnu tests.  */
+
+class pattern_test_state_machine : public state_machine
+{
+public:
+  pattern_test_state_machine (logger *logger);
+
+  void on_stmt (sm_context *sm_ctxt,
+		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_leak (sm_context *sm_ctxt,
+		const supernode *node,
+		const gimple *stmt,
+		tree var,
+		state_machine::state_t state) const FINAL OVERRIDE;
+
+private:
+  state_t m_start;
+};
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c
deleted file mode 100644
index a6f25ed..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
-   Copyright 2012 David Malcolm <dmalcolm@redhat.com>
-   Copyright 2012 Red Hat, Inc.
-
-   This is free software: you can redistribute it and/or modify it
-   under the terms of the GNU General Public License as published by
-   the Free Software Foundation, either version 3 of the License, or
-   (at your option) any later version.
-
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with this program.  If not, see
-   <http://www.gnu.org/licenses/>.
-*/
-
-extern void foo(void *);
-extern void bar(void *);
-
-void test1(void *ptr)
-{
-  if (ptr) {
-    foo(ptr);
-  } else {
-    bar(ptr);
-  }
-}
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.cpychecker-log.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.cpychecker-log.txt
deleted file mode 100644
index 7499d84..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.cpychecker-log.txt
+++ /dev/null
@@ -1 +0,0 @@
-gcc.Function('test')
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.test_read_through_uninitialized_pointer-refcount-errors.html b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.test_read_through_uninitialized_pointer-refcount-errors.html
deleted file mode 100644
index b92b8e0..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/input.c.test_read_through_uninitialized_pointer-refcount-errors.html
+++ /dev/null
@@ -1,175 +0,0 @@
-<html>
-  <head>
-    <title>test_read_through_uninitialized_pointer</title>
-    <style type="text/css">
-.unreached-line {
-    #margin:1em;
-    #border:0.1em dotted #00aa00;
-
-    opacity:1.0;
-    filter:alpha(opacity=0);
-    #color:black;
-}
-
-.reached-line {
-    #margin:1em;
-    #border:0.1em dotted #00aa00;
-    border: 0.1em solid blue;
-    font-weight: bold;
-
-    opacity:1.0;
-    filter:alpha(opacity=0);
-    #color:black;
-}
-
-.reached-lineno {
-    border: 0.1em solid blue;
-    font-weight: bold;
-}
-
-pre {
-    #line-height:200%
-}
-
-._jsPlumb_connector { z-index: -2; }
-._jsPlumb_endpoint { z-index: -1; }
-
-.transition {
-    #border: 0.1em dotted #ddffdd;
-    #padding: 1em;
-    border: 0.1em solid #ccc;
-    -moz-box-shadow: 2px 2px 2px #ccc;
-    -webkit-box-shadow: 2px 2px 2px #ccc;
-    box-shadow: 2px 2px 2px #ccc;
-    margin-left: 5em;
-    font-family: proportional;
-    font-style: italic;
-    font-size: 90%;
-}
-
-.error {
-    #border: 0.1em dotted #ddffdd;
-    #padding: 1em;
-    border: 0.1em solid #cc0000;
-    -moz-box-shadow: 2px 2px 2px #cc0000;
-    -webkit-box-shadow: 2px 2px 2px #cc0000;
-    box-shadow: 2px 2px 2px #cc0000;
-    margin-left: 5em;
-    color: red;
-    font-family: proportional;
-    font-weight: bold;
-    font-style: italic;
-    font-size: 90%;
-}
-
-.note {
-    #border: 0.1em dotted #ddffdd;
-    #padding: 1em;
-    border: 0.1em solid #ccc;
-    -moz-box-shadow: 2px 2px 2px #ccc;
-    -webkit-box-shadow: 2px 2px 2px #ccc;
-    box-shadow: 2px 2px 2px #ccc;
-    margin-left: 5em;
-    font-family: proportional;
-    font-weight: bold;
-    font-style: italic;
-    font-size: 90%;
-}
-
-.hll { background-color: #ffffcc }
-.source_c { color: #408080; font-style: italic } /* Comment */
-.source_err { border: 1px solid #FF0000 } /* Error */
-.source_k { color: #008000; font-weight: bold } /* Keyword */
-.source_o { color: #666666 } /* Operator */
-.source_cm { color: #408080; font-style: italic } /* Comment.Multiline */
-.source_cp { color: #BC7A00 } /* Comment.Preproc */
-.source_c1 { color: #408080; font-style: italic } /* Comment.Single */
-.source_cs { color: #408080; font-style: italic } /* Comment.Special */
-.source_gd { color: #A00000 } /* Generic.Deleted */
-.source_ge { font-style: italic } /* Generic.Emph */
-.source_gr { color: #FF0000 } /* Generic.Error */
-.source_gh { color: #000080; font-weight: bold } /* Generic.Heading */
-.source_gi { color: #00A000 } /* Generic.Inserted */
-.source_go { color: #808080 } /* Generic.Output */
-.source_gp { color: #000080; font-weight: bold } /* Generic.Prompt */
-.source_gs { font-weight: bold } /* Generic.Strong */
-.source_gu { color: #800080; font-weight: bold } /* Generic.Subheading */
-.source_gt { color: #0040D0 } /* Generic.Traceback */
-.source_kc { color: #008000; font-weight: bold } /* Keyword.Constant */
-.source_kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
-.source_kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
-.source_kp { color: #008000 } /* Keyword.Pseudo */
-.source_kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
-.source_kt { color: #B00040 } /* Keyword.Type */
-.source_m { color: #666666 } /* Literal.Number */
-.source_s { color: #BA2121 } /* Literal.String */
-.source_na { color: #7D9029 } /* Name.Attribute */
-.source_nb { color: #008000 } /* Name.Builtin */
-.source_nc { color: #0000FF; font-weight: bold } /* Name.Class */
-.source_no { color: #880000 } /* Name.Constant */
-.source_nd { color: #AA22FF } /* Name.Decorator */
-.source_ni { color: #999999; font-weight: bold } /* Name.Entity */
-.source_ne { color: #D2413A; font-weight: bold } /* Name.Exception */
-.source_nf { color: #0000FF } /* Name.Function */
-.source_nl { color: #A0A000 } /* Name.Label */
-.source_nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
-.source_nt { color: #008000; font-weight: bold } /* Name.Tag */
-.source_nv { color: #19177C } /* Name.Variable */
-.source_ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
-.source_w { color: #bbbbbb } /* Text.Whitespace */
-.source_mf { color: #666666 } /* Literal.Number.Float */
-.source_mh { color: #666666 } /* Literal.Number.Hex */
-.source_mi { color: #666666 } /* Literal.Number.Integer */
-.source_mo { color: #666666 } /* Literal.Number.Oct */
-.source_sb { color: #BA2121 } /* Literal.String.Backtick */
-.source_sc { color: #BA2121 } /* Literal.String.Char */
-.source_sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
-.source_s2 { color: #BA2121 } /* Literal.String.Double */
-.source_se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
-.source_sh { color: #BA2121 } /* Literal.String.Heredoc */
-.source_si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
-.source_sx { color: #008000 } /* Literal.String.Other */
-.source_sr { color: #BB6688 } /* Literal.String.Regex */
-.source_s1 { color: #BA2121 } /* Literal.String.Single */
-.source_ss { color: #19177C } /* Literal.String.Symbol */
-.source_bp { color: #008000 } /* Name.Builtin.Pseudo */
-.source_vc { color: #19177C } /* Name.Variable.Class */
-.source_vg { color: #19177C } /* Name.Variable.Global */
-.source_vi { color: #19177C } /* Name.Variable.Instance */
-.source_il { color: #666666 } /* Literal.Number.Integer.Long */    </style>
-  </head>
-  <body>
-<script type="text/javascript" src="http://explorercanvas.googlecode.com/svn/trunk/excanvas.js"></script>
-<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.0/jquery.min.js"></script>
-<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.13/jquery-ui.min.js"></script>
-<script type="text/javascript" src="http://jsplumb.googlecode.com/files/jquery.jsPlumb-1.2.6-all-min.js"></script>
-<div id="report-0">
-<table>
-  <tr><td>File:</td> <td><b>tests/cpychecker/absinterp/read-through-uninitialized-ptr/input.c</b></td></tr>
-  <tr><td>Function:</td> <td><b>test_read_through_uninitialized_pointer</b></td></tr>
-  <tr><td>Error:</td> <td><b>dereferencing uninitialized pointer (coord_ptr->x) at tests/cpychecker/absinterp/read-through-uninitialized-ptr/input.c:35</b></td></tr>
-</table>
-<div class="highlight"><pre><span class="lineno">30</span> <span id="trace0-line30" class="unreached-line"><span class="source_kt">int</span> <span class="source_nf">test_read_through_uninitialized_pointer</span><span class="source_p">(</span><span class="source_kt">void</span><span class="source_p">)</span></span>
-<span class="lineno">31</span> <span id="trace0-line31" class="unreached-line"><span class="source_p">{</span></span>
-<span class="lineno">32</span> <span id="trace0-line32" class="unreached-line">    <span class="source_k">struct</span> <span class="source_n">coord</span> <span class="source_o">*</span><span class="source_n">coord_ptr</span><span class="source_p">;</span></span>
-<span class="lineno">33</span> <span id="trace0-line33" class="unreached-line"></span>
-<span class="lineno">34</span> <span id="trace0-line34" class="unreached-line">    <span class="source_k">return</span> <span class="source_n">coord_ptr</span><span class="source_o">-&gt;</span><span class="source_n">x</span><span class="source_p">;</span></span>
-<span class="reached-lineno">35</span> <span id="trace0-line35" class="reached-line"><span class="source_p">}</span></span>
-<span class="error">dereferencing uninitialized pointer (coord_ptr->x) at tests/cpychecker/absinterp/read-through-uninitialized-ptr/input.c:35</span>
-<span class="lineno">36</span> <span id="trace0-line36" class="unreached-line"></pre></div></span>
-</pre></div>
-
-<script type="text/javascript">
-  jsPlumb.bind("ready", function() {
-    /* Set up defaults: */
-    jsPlumb.Defaults.Connector = [ "Bezier", 1 ];
-    jsPlumb.Defaults.Connector = [ "Straight" ];
-    jsPlumb.Defaults.PaintStyle = { strokeStyle:"blue", lineWidth:1 };
-    jsPlumb.Defaults.EndpointStyle = { radius:2, fillStyle:"red" };
-    jsPlumb.Defaults.Anchors =  [ "BottomCenter", "TopCenter" ];
-
-    /* Add lines: */
-  })
-</script>
-<hr/></div>
-  </body>
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/script.py b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/script.py
deleted file mode 100644
index 696d51a..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/script.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# -*- coding: utf-8 -*-
-#   Copyright 2012 David Malcolm <dmalcolm@redhat.com>
-#   Copyright 2012 Red Hat, Inc.
-#
-#   This is free software: you can redistribute it and/or modify it
-#   under the terms of the GNU General Public License as published by
-#   the Free Software Foundation, either version 3 of the License, or
-#   (at your option) any later version.
-#
-#   This program is distributed in the hope that it will be useful, but
-#   WITHOUT ANY WARRANTY; without even the implied warranty of
-#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-#   General Public License for more details.
-#
-#   You should have received a copy of the GNU General Public License
-#   along with this program.  If not, see
-#   <http://www.gnu.org/licenses/>.
-
-from sm import main
-from sm.parser import parse_string
-
-SCRIPT = '''
-sm checked_against_null {
-  stateful decl any_pointer ptr;
-
-  ptr.*:
-    { ptr != 0 } => {{ error('%s was checked against NULL' % ptr) }};
-}
-'''
-
-# TODO: this currently emits the warning for both the true and the false path
-# from the conditional, rather than just once.
-
-checker = parse_string(SCRIPT)
-main([checker])
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/stderr.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/stderr.txt
deleted file mode 100644
index 546bf95..0000000
--- a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/stderr.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-tests/sm/patterns/conditional/input.c: In function 'test1':
-tests/sm/patterns/conditional/input.c:25:nn: error: ptr was checked against NULL
diff --git a/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/stdout.txt b/gcc/testsuite/gcc.dg/TODO-analyzer/patterns/conditional/stdout.txt
deleted file mode 100644
index e69de29..0000000
diff --git a/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
new file mode 100644
index 0000000..c5e6f1d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/pattern-test-1.c
@@ -0,0 +1,14 @@
+/* { dg-options "--analyzer -fanalyzer-checker=pattern-test" } */
+
+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);
+  }
+}
-- 
1.8.5.3

