From 25f0137d431faee2a46c7ea7012c2ddeee47cf6f Mon Sep 17 00:00:00 2001
From: Daniele Varrazzo <daniele.varrazzo@gmail.com>
Date: Fri, 24 Feb 2012 12:53:42 +0000
Subject: [PATCH] cpychecker: implement PyWeakref_GetObject

---
 libcpychecker/refcounts.py                         |   15 ++++++
 .../refcounts/PyWeakref_GetObject/correct/input.c  |   50 ++++++++++++++++++++
 .../PyWeakref_GetObject/correct/script.py          |   22 +++++++++
 .../PyWeakref_GetObject/correct/stdout.txt         |   40 ++++++++++++++++
 .../PyWeakref_GetObject/incorrect/input.c          |   46 ++++++++++++++++++
 .../PyWeakref_GetObject/incorrect/metadata.ini     |    3 +
 .../PyWeakref_GetObject/incorrect/script.py        |   22 +++++++++
 .../PyWeakref_GetObject/incorrect/stderr.txt       |   12 +++++
 .../PyWeakref_GetObject/incorrect/stdout.txt       |   40 ++++++++++++++++
 9 files changed, 250 insertions(+), 0 deletions(-)
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/correct/script.py
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/correct/stdout.txt
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/metadata.ini
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/script.py
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stderr.txt
 create mode 100644 tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stdout.txt

diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 967417d..bf02cc3 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -3263,6 +3263,21 @@ class CPython(Facet):
                                                           'PyUnicode_Type')
         return [t_success, t_failure]
 
+    ########################################################################
+    # PyWeakref_*
+    ########################################################################
+
+    def impl_PyWeakref_GetObject(self, stmt, v_op):
+        fnmeta = FnMeta(name='PyWeakref_GetObject',
+                        docurl='http://docs.python.org/c-api/weakref.html#PyWeakref_GetObject',
+                        defined_in='Objects/weakrefobject.c')
+        self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op,
+                   why=invokes_Py_TYPE_via_macro(fnmeta,
+                                                 'PyWeakref_Check'))
+        s_success = self.mkstate_borrowed_ref(stmt,
+                                              fnmeta)
+        return [Transition(self.state, s_success, None)]
+
 
     ########################################################################
     # (end of Python API implementations)
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c
new file mode 100644
index 0000000..a6a8522
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c
@@ -0,0 +1,50 @@
+/*
+   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/>.
+*/
+
+#include <Python.h>
+
+/*
+  Test of correct reference-handling in a call to PyWeakref_GetObject
+*/
+
+extern PyObject *some_ref;
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+    PyObject *item;
+    item = PyWeakref_GetObject(some_ref);
+    if (item) {
+        /*
+          If it succeeds, it only returns a borrowed ref, so we must incref
+          it:
+        */
+        Py_INCREF(item);
+        return item;
+    }
+    return NULL;
+}
+
+/*
+  PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/script.py b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+#   Copyright 2011 David Malcolm <dmalcolm@redhat.com>
+#   Copyright 2011 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 libcpychecker import main
+main(verify_refcounting=True,
+     dump_traces=True,
+     show_traces=False)
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/stdout.txt b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/stdout.txt
new file mode 100644
index 0000000..25c9089
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/stdout.txt
@@ -0,0 +1,40 @@
+Trace 0:
+  Transitions:
+    'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:26 as non-NULL'
+    'taking True path'
+    'returning'
+  Return value:
+    repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=32), region=RegionOnHeap('borrowed reference returned by PyWeakref_GetObject()', gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=32)))
+    str(): (struct PyObject *)&RegionOnHeap('borrowed reference returned by PyWeakref_GetObject()', gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=32)) from tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:32
+    r->ob_refcnt: refs: 1 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=32), region=Region('PyTypeObject for borrowed reference returned by PyWeakref_GetObject()'))
+  Region("region-for-arg-gcc.ParmDecl('self')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('self')")
+    str(): Region("region-for-arg-gcc.ParmDecl('self')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')"))
+  Region("region-for-arg-gcc.ParmDecl('args')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('args')")
+    str(): Region("region-for-arg-gcc.ParmDecl('args')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+  Exception:
+    (struct PyObject *)0 from tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:30
+
+Trace 1:
+  Transitions:
+    'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:26 as NULL'
+  error: NullPtrArgument()
+  error: possibly calling PyWeakref_GetObject with NULL as argument 1 (some_ref.0) at tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:32
+  Region("region-for-arg-gcc.ParmDecl('self')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('self')")
+    str(): Region("region-for-arg-gcc.ParmDecl('self')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')"))
+  Region("region-for-arg-gcc.ParmDecl('args')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('args')")
+    str(): Region("region-for-arg-gcc.ParmDecl('args')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+  Exception:
+    (struct PyObject *)0 from tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:30
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c
new file mode 100644
index 0000000..3195f1b
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c
@@ -0,0 +1,46 @@
+/*
+   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/>.
+*/
+
+#include <Python.h>
+
+/*
+  Test of incorrect reference-handling in a call to PyWeakref_GetObject
+*/
+
+extern PyObject *some_ref;
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+    PyObject *item;
+    item = PyWeakref_GetObject(some_ref);
+    if (item) {
+        /* BUG: this is only a borrowed ref; we ought to INCREF it: */
+        return item;
+    }
+    return NULL;
+}
+
+/*
+  PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/metadata.ini b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/metadata.ini
@@ -0,0 +1,3 @@
+[ExpectedBehavior]
+# We expect only compilation *warnings*, so we expect a 0 exit code
+exitcode = 0
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/script.py b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+#   Copyright 2011 David Malcolm <dmalcolm@redhat.com>
+#   Copyright 2011 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 libcpychecker import main
+main(verify_refcounting=True,
+     dump_traces=True,
+     show_traces=False)
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stderr.txt
new file mode 100644
index 0000000..4e3dda9
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stderr.txt
@@ -0,0 +1,12 @@
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c: In function 'test':
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:38:nn: warning: ob_refcnt of return value is 1 too low [enabled by default]
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:38:nn: note: was expecting final ob_refcnt to be N + 1 (for some unknown N)
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:38:nn: note: due to object being referenced by: return value
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:38:nn: note: but final ob_refcnt is N + 0
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:32:nn: note: borrowed reference returned by PyWeakref_GetObject() allocated at:     item = PyWeakref_GetObject(some_ref);
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:32:nn: note: when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:26 as non-NULL at:     item = PyWeakref_GetObject(some_ref);
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:32:nn: note: ob_refcnt is now refs: 0 + N where N >= 1
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:33:nn: note: taking True path at:     if (item) {
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:35:nn: note: reaching:         return item;
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:38:nn: note: returning
+tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:30:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stdout.txt
new file mode 100644
index 0000000..b442afb
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stdout.txt
@@ -0,0 +1,40 @@
+Trace 0:
+  Transitions:
+    'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:26 as non-NULL'
+    'taking True path'
+    'returning'
+  Return value:
+    repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=32), region=RegionOnHeap('borrowed reference returned by PyWeakref_GetObject()', gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=32)))
+    str(): (struct PyObject *)&RegionOnHeap('borrowed reference returned by PyWeakref_GetObject()', gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=32)) from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:32
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=32), region=Region('PyTypeObject for borrowed reference returned by PyWeakref_GetObject()'))
+  Region("region-for-arg-gcc.ParmDecl('self')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('self')")
+    str(): Region("region-for-arg-gcc.ParmDecl('self')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')"))
+  Region("region-for-arg-gcc.ParmDecl('args')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('args')")
+    str(): Region("region-for-arg-gcc.ParmDecl('args')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+  Exception:
+    (struct PyObject *)0 from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:30
+
+Trace 1:
+  Transitions:
+    'when treating unknown struct PyObject * from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:26 as NULL'
+  error: NullPtrArgument()
+  error: possibly calling PyWeakref_GetObject with NULL as argument 1 (some_ref.0) at tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:32
+  Region("region-for-arg-gcc.ParmDecl('self')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('self')")
+    str(): Region("region-for-arg-gcc.ParmDecl('self')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('self')"))
+  Region("region-for-arg-gcc.ParmDecl('args')"):
+    repr(): Region("region-for-arg-gcc.ParmDecl('args')")
+    str(): Region("region-for-arg-gcc.ParmDecl('args')")
+    r->ob_refcnt: refs: 0 + N where N >= 1
+    r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+  Exception:
+    (struct PyObject *)0 from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:30
-- 
1.7.5.4

