commit ef9cdb8bb9af87e6b4b310ffd152b0dbc280ceba
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 28 15:14:02 2012 -0500
cpychecker: improved handling of unknown functions that return a PyObject * subclass
In 90d237c02bb8fe6bd7b40aa6c2420652c87ce906 unknown functions that return
a PyObject* are assumed to return either a new reference, or return NULL
setting an exception.
Generalize this for functions returning PyObject subclasses.
Fixes a false warning for psycopg:
psycopg/connection_type.c: In function ‘psyco_conn_tpc_begin’:
psycopg/connection_type.c:228:1: warning: returning (PyObject*)NULL without setting
an exception
libcpychecker/absinterp.py | 10 ++-
.../refcounts/unrecognized_function4/input.c | 51 +++++++++++++
.../refcounts/unrecognized_function4/script.py | 22 ++++++
.../refcounts/unrecognized_function4/stdout.txt | 79 ++++++++++++++++++++
4 files changed, 158 insertions(+), 4 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index a7295d7..d7ecb89 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -211,8 +211,9 @@ class AbstractValue(object):
check_isinstance(stmt, gcc.GimpleCall)
returntype = stmt.fn.type.dereference.type
- if str(returntype) == 'struct PyObject *':
- log('Invocation of function pointer returning PyObject *')
+ from libcpychecker.refcounts import type_is_pyobjptr_subclass
+ if type_is_pyobjptr_subclass(returntype):
+ log('Invocation of function pointer returning PyObject * (or
subclass)')
# Assume that all such functions either:
# - return a new reference, or
# - return NULL and set an exception (e.g. MemoryError)
@@ -2212,8 +2213,9 @@ class State(object):
raise NotImplementedError('not yet implemented: %s' %
fnname)
# Unknown function returning (PyObject*):
- if str(stmt.fn.operand.type.type) == 'struct PyObject *':
- log('Invocation of unknown function returning PyObject *: %r' %
fnname)
+ from libcpychecker.refcounts import type_is_pyobjptr_subclass
+ if type_is_pyobjptr_subclass(stmt.fn.operand.type.type):
+ log('Invocation of unknown function returning PyObject * (or
subclass): %r' % fnname)
fnmeta = FnMeta(name=fnname)
diff --git a/tests/cpychecker/refcounts/unrecognized_function4/input.c
b/tests/cpychecker/refcounts/unrecognized_function4/input.c
new file mode 100644
index 0000000..e691582
--- /dev/null
+++ b/tests/cpychecker/refcounts/unrecognized_function4/input.c
@@ -0,0 +1,51 @@
+/*
+ Copyright 2012 David Malcolm <dmalcolm(a)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>
+
+/*
+ Ensure that the checker can cope with calls to a function that it doesn't
+ recognize that returns a PyObject subclass:
+*/
+typedef struct FooObject {
+ PyObject_HEAD
+ int i;
+} FooObject;
+
+extern FooObject *make_foo(int i);
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ FooObject *f = make_foo(42);
+ if (NULL == f) {
+ return NULL; /* we assume an exception was set by make_foo() */
+ }
+ Py_DECREF(f);
+
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/unrecognized_function4/script.py
b/tests/cpychecker/refcounts/unrecognized_function4/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/unrecognized_function4/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm(a)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/unrecognized_function4/stdout.txt
b/tests/cpychecker/refcounts/unrecognized_function4/stdout.txt
new file mode 100644
index 0000000..c6479c7
--- /dev/null
+++ b/tests/cpychecker/refcounts/unrecognized_function4/stdout.txt
@@ -0,0 +1,79 @@
+Trace 0:
+ Transitions:
+ 'when make_foo() succeeds'
+ 'taking False path'
+ 'when taking True path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c',
line=42), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')))
+ str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))
from tests/cpychecker/refcounts/unrecognized_function4/input.c:42
+ r->ob_refcnt: refs: 1 + N where N >= 1
+ r->ob_type: None
+ 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/unrecognized_function4/input.c',
line=34), 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/unrecognized_function4/input.c',
line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ new ref from (unknown) make_foo allocated at
tests/cpychecker/refcounts/unrecognized_function4/input.c:36:
+ repr(): RegionOnHeap('new ref from (unknown) make_foo',
gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c',
line=36))
+ str(): new ref from (unknown) make_foo allocated at
tests/cpychecker/refcounts/unrecognized_function4/input.c:36
+ r->ob_refcnt: refs: 0 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c',
line=36), region=Region('PyTypeObject for new ref from (unknown) make_foo'))
+ Exception:
+ (struct PyObject *)0 from
tests/cpychecker/refcounts/unrecognized_function4/input.c:35
+
+Trace 1:
+ Transitions:
+ 'when make_foo() succeeds'
+ 'taking False path'
+ 'when taking False path'
+ 'calling tp_dealloc on new ref from (unknown) make_foo allocated at
tests/cpychecker/refcounts/unrecognized_function4/input.c:36'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c',
line=42), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')))
+ str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))
from tests/cpychecker/refcounts/unrecognized_function4/input.c:42
+ r->ob_refcnt: refs: 1 + N where N >= 1
+ r->ob_type: None
+ 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/unrecognized_function4/input.c',
line=34), 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/unrecognized_function4/input.c',
line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ new ref from (unknown) make_foo allocated at
tests/cpychecker/refcounts/unrecognized_function4/input.c:36:
+ repr(): RegionOnHeap('new ref from (unknown) make_foo',
gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c',
line=36))
+ str(): new ref from (unknown) make_foo allocated at
tests/cpychecker/refcounts/unrecognized_function4/input.c:36
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)0 from
tests/cpychecker/refcounts/unrecognized_function4/input.c:35
+
+Trace 2:
+ Transitions:
+ 'when make_foo() fails'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *',
loc=gcc.Location(file='tests/cpychecker/refcounts/unrecognized_function4/input.c',
line=38), value=0)
+ str(): (struct PyObject *)0 from
tests/cpychecker/refcounts/unrecognized_function4/input.c:38
+ 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/unrecognized_function4/input.c',
line=34), 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/unrecognized_function4/input.c',
line=34), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError'))
from tests/cpychecker/refcounts/unrecognized_function4/input.c:36