[gcc-python-plugin] cpychecker: implement multiplication of ConcreteValue pairs
by David Hugh Malcolm
commit 7b52fd8f379c360ebc63a10a13f31aac86231db2
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 19:35:28 2011 -0400
cpychecker: implement multiplication of ConcreteValue pairs
libcpychecker/refcounts.py | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index b850b6a..d8de452 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1087,6 +1087,8 @@ class MyState(State):
log('b: %r', b)
if isinstance(a, UnknownValue) or isinstance(b, UnknownValue):
return UnknownValue(stmt.lhs.type, stmt.loc)
+ if isinstance(a, ConcreteValue) and isinstance(b, ConcreteValue):
+ return ConcreteValue(stmt.lhs.type, stmt.loc, a.value * b.value)
raise NotImplementedError("Don't know how to cope with multiplication of\n %r\nand\n %rat %s"
% (a, b, stmt.loc))
elif stmt.exprcode == gcc.ComponentRef:
12 years, 8 months
[gcc-python-plugin] cpychecker: implement PyErr_Format
by David Hugh Malcolm
commit 07a1d9684dbe5ac17fa5fad4239bd3cab49dcd69
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 19:34:39 2011 -0400
cpychecker: implement PyErr_Format
libcpychecker/refcounts.py | 15 ++++++
.../refcounts/PyErr_Format/correct/input.c | 49 ++++++++++++++++++++
.../refcounts/PyErr_Format/correct/script.py | 22 +++++++++
.../refcounts/PyErr_Format/correct/stdout.txt | 19 ++++++++
4 files changed, 105 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 3c7454a..b850b6a 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -591,6 +591,21 @@ class MyState(State):
########################################################################
# PyErr_*
########################################################################
+ def impl_PyErr_Format(self, stmt):
+ # Declared in pyerrors.h:
+ # PyAPI_FUNC(void) PyErr_SetString(PyObject *, const char *);
+ # Defined in Python/errors.c
+ #
+ args = self.eval_stmt_args(stmt)
+ v_exc = args[0]
+ v_fmt = args[1]
+ # It always returns NULL:
+ t_next = self.mktrans_assignment(stmt.lhs,
+ make_null_pyobject_ptr(stmt),
+ 'PyErr_Format()')
+ t_next.dest.exception_rvalue = v_exc
+ return [t_next]
+
def impl_PyErr_Print(self, stmt):
# Declared in pythonrun.h:
# PyAPI_FUNC(void) PyErr_Print(void);
diff --git a/tests/cpychecker/refcounts/PyErr_Format/correct/input.c b/tests/cpychecker/refcounts/PyErr_Format/correct/input.c
new file mode 100644
index 0000000..2251a7a
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyErr_Format/correct/input.c
@@ -0,0 +1,49 @@
+/*
+ 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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of correct usage of PyErr_Format
+*/
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ /*
+ It's more idiomatic to do a "return PyErr_Format".
+ However, this approach exercises this case, and verifies that the
+ checker doesn't erroneously think that it returns an object:
+ */
+ PyErr_Format(PyExc_RuntimeError,
+ "This test method always fails");
+ return NULL;
+}
+static PyMethodDef test_methods[] = {
+ {"test_method", test, METH_VARARGS, NULL},
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyErr_Format/correct/script.py b/tests/cpychecker/refcounts/PyErr_Format/correct/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyErr_Format/correct/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/PyErr_Format/correct/stdout.txt b/tests/cpychecker/refcounts/PyErr_Format/correct/stdout.txt
new file mode 100644
index 0000000..dade485
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyErr_Format/correct/stdout.txt
@@ -0,0 +1,19 @@
+Trace 0:
+ Transitions:
+ 'PyErr_Format()'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyErr_Format/correct/input.c', line=36), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyErr_Format/correct/input.c:36
+ 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/PyErr_Format/correct/input.c', line=27), 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/PyErr_Format/correct/input.c', line=27), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ unknown struct PyObject * from /usr/include/python2.7/pyerrors.h:140
12 years, 8 months
[gcc-python-plugin] cpychecker: support more array/pointer operations
by David Hugh Malcolm
commit 4aa76b74a198e972ddef7f67fefc4d725d874bca
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 19:18:03 2011 -0400
cpychecker: support more array/pointer operations
libcpychecker/absinterp.py | 2 +-
libcpychecker/refcounts.py | 16 +++++++-
tests/cpychecker/absinterp/arrays3/input.c | 46 +++++++++++++++++++++++++
tests/cpychecker/absinterp/arrays3/script.py | 22 ++++++++++++
tests/cpychecker/absinterp/arrays3/stdout.txt | 17 +++++++++
tests/cpychecker/absinterp/arrays4/input.c | 46 +++++++++++++++++++++++++
tests/cpychecker/absinterp/arrays4/script.py | 22 ++++++++++++
tests/cpychecker/absinterp/arrays4/stdout.txt | 8 ++++
8 files changed, 176 insertions(+), 3 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 69a8537..9607997 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -609,7 +609,7 @@ class State:
def _array_region(self, parent, index):
# Used by element_region, and pointer_add_region
check_isinstance(parent, Region)
- check_isinstance(index, (int, long))
+ check_isinstance(index, (int, long, UnknownValue))
if index in parent.fields:
log('reusing')
return parent.fields[index]
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 1982fb1..3c7454a 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1065,6 +1065,15 @@ class MyState(State):
return RefcountValue(a.relvalue - b.value, a.min_external)
raise NotImplementedError("Don't know how to cope with subtraction of\n %r\nand\n %rat %s"
% (a, b, stmt.loc))
+ elif stmt.exprcode == gcc.MultExpr:
+ a = self.eval_rvalue(rhs[0], stmt.loc)
+ b = self.eval_rvalue(rhs[1], stmt.loc)
+ log('a: %r', a)
+ log('b: %r', b)
+ if isinstance(a, UnknownValue) or isinstance(b, UnknownValue):
+ return UnknownValue(stmt.lhs.type, stmt.loc)
+ raise NotImplementedError("Don't know how to cope with multiplication of\n %r\nand\n %rat %s"
+ % (a, b, stmt.loc))
elif stmt.exprcode == gcc.ComponentRef:
return self.eval_rvalue(rhs[0], stmt.loc)
elif stmt.exprcode == gcc.VarDecl:
@@ -1082,8 +1091,11 @@ class MyState(State):
elif stmt.exprcode == gcc.MemRef:
return self.eval_rvalue(rhs[0], stmt.loc)
elif stmt.exprcode == gcc.PointerPlusExpr:
- region = self.pointer_plus_region(stmt)
- return PointerToRegion(stmt.lhs.type, stmt.loc, region)
+ try:
+ region = self.pointer_plus_region(stmt)
+ return PointerToRegion(stmt.lhs.type, stmt.loc, region)
+ except NotImplementedError:
+ return UnknownValue(stmt.lhs.type, stmt.loc)
elif stmt.exprcode in (gcc.EqExpr, gcc.NeExpr, gcc.LtExpr,
gcc.LeExpr, gcc.GeExpr, gcc.GtExpr):
# Comparisons
diff --git a/tests/cpychecker/absinterp/arrays3/input.c b/tests/cpychecker/absinterp/arrays3/input.c
new file mode 100644
index 0000000..7e113b3
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays3/input.c
@@ -0,0 +1,46 @@
+/*
+ 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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of handling a pointer like an array
+*/
+
+typedef struct foo
+{
+ const char *fieldA;
+ const char *fieldB;
+} Foo;
+
+extern Foo *foo_array;
+
+static const char*
+test(int i)
+{
+ return foo_array[i].fieldA;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/absinterp/arrays3/script.py b/tests/cpychecker/absinterp/arrays3/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays3/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)
diff --git a/tests/cpychecker/absinterp/arrays3/stdout.txt b/tests/cpychecker/absinterp/arrays3/stdout.txt
new file mode 100644
index 0000000..0ca5af1
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays3/stdout.txt
@@ -0,0 +1,17 @@
+Trace 0:
+ Transitions:
+ "treating unknown struct Foo * from tests/cpychecker/absinterp/arrays3/input.c:37 as (struct Foo *)&Region('heap-region-0')"
+ 'returning'
+ Return value:
+ repr(): UnknownValue(gcctype=<gcc.PointerType object at 0xdeadbeef>, loc=gcc.Location(file='tests/cpychecker/absinterp/arrays3/input.c', line=37))
+ str(): unknown const char * from tests/cpychecker/absinterp/arrays3/input.c:37
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/arrays3/input.c:36
+
+Trace 1:
+ Transitions:
+ 'treating unknown struct Foo * from tests/cpychecker/absinterp/arrays3/input.c:37 as (struct Foo *)0'
+ error: NullPtrDereference()
+ error: possibly dereferencing NULL (D.12021->fieldA) at tests/cpychecker/absinterp/arrays3/input.c:37
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/arrays3/input.c:36
diff --git a/tests/cpychecker/absinterp/arrays4/input.c b/tests/cpychecker/absinterp/arrays4/input.c
new file mode 100644
index 0000000..68170ee
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays4/input.c
@@ -0,0 +1,46 @@
+/*
+ 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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of handling an array with an unknown index
+*/
+
+typedef struct foo
+{
+ const char *fieldA;
+ const char *fieldB;
+} Foo;
+
+extern Foo foo_array[10];
+
+static const char*
+test(int i)
+{
+ return foo_array[i].fieldA;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/absinterp/arrays4/script.py b/tests/cpychecker/absinterp/arrays4/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays4/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)
diff --git a/tests/cpychecker/absinterp/arrays4/stdout.txt b/tests/cpychecker/absinterp/arrays4/stdout.txt
new file mode 100644
index 0000000..a469fd1
--- /dev/null
+++ b/tests/cpychecker/absinterp/arrays4/stdout.txt
@@ -0,0 +1,8 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): UnknownValue(gcctype=<gcc.PointerType object at 0xdeadbeef>, loc=gcc.Location(file='tests/cpychecker/absinterp/arrays4/input.c', line=37))
+ str(): unknown const char * from tests/cpychecker/absinterp/arrays4/input.c:37
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/absinterp/arrays4/input.c:36
12 years, 8 months
[gcc-python-plugin] cpychecker: simplification of State.get_field_region
by David Hugh Malcolm
commit 305046565054708c0a4b0c6c34c7e5b30ebaf801
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 19:13:36 2011 -0400
cpychecker: simplification of State.get_field_region
libcpychecker/absinterp.py | 43 +++++++++++++++----------------------------
1 files changed, 15 insertions(+), 28 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 8bfdc07..69a8537 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -627,34 +627,21 @@ class State:
#cr.debug()
log('target: %r %s ', cr.target, cr.target)
log('field: %r', cr.field)
- #fr = FIXME #self.make_field_region(target, field)
- if 1: # fr not in self.region_for_var:
- if 1: # cr.target not in self.region_for_var:
- log('foo')
- if isinstance(cr.target, gcc.MemRef):
- ptr = self.eval_rvalue(cr.target.operand, loc) # FIXME
- log('ptr: %r', ptr)
- self.raise_any_null_ptr_deref(cr, ptr)
- if isinstance(ptr, UnknownValue):
- # It could be NULL; it could be non-NULL
- # Split the analysis
- # Non-NULL pointer:
- log('splitting %s into non-NULL/NULL pointers', cr)
- self.raise_split_value(ptr)
- check_isinstance(ptr, PointerToRegion)
- return self.make_field_region(ptr.region, cr.field.name)
- elif isinstance(cr.target, gcc.VarDecl):
- log('bar')
- vr = self.var_region(cr.target)
- log('%s', vr)
- return self.make_field_region(vr, cr.field.name)
- elif isinstance(cr.target, gcc.ComponentRef):
- # nested field:
- vr = self.get_field_region(cr.target, loc)
- log('%s', vr)
- return self.make_field_region(vr, cr.field.name)
- log('cr: %r %s', cr, cr)
- return self.region_for_var[cr]
+ if isinstance(cr.target, gcc.MemRef):
+ ptr = self.eval_rvalue(cr.target.operand, loc) # FIXME
+ log('ptr: %r', ptr)
+ self.raise_any_null_ptr_deref(cr, ptr)
+ if isinstance(ptr, UnknownValue):
+ # It could be NULL; it could be non-NULL
+ # Split the analysis
+ # Non-NULL pointer:
+ log('splitting %s into non-NULL/NULL pointers', cr)
+ self.raise_split_value(ptr)
+ check_isinstance(ptr, PointerToRegion)
+ return self.make_field_region(ptr.region, cr.field.name)
+
+ target_region = self.eval_lvalue(cr.target, loc)
+ return self.make_field_region(target_region, cr.field.name)
def string_constant_region(self, expr, loc):
log('string_constant_region: %s', expr)
12 years, 8 months
[gcc-python-plugin] cpychecker: refinements to handling of NULL pointers
by David Hugh Malcolm
commit 5b98cec10143271d6365951f5d3037f04c123159
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 19:04:27 2011 -0400
cpychecker: refinements to handling of NULL pointers
Previously, any time we dereferenced an unknown pointer value, we considered
the path in which it was NULL, and warned about a possible NULL pointer
dereference.
This leads to large numbers of false positives when running on real-world
code, so for now the default is not to report on those errors, and assume that
such code is correct: that the unknown pointer value was not NULL.
This is a boolean, so that it can potentially be a command-line option within
the invocation harness.
Note that for the cases where we know that the pointer can _definitely_ be NULL,
an error will still be reported (e.g. when considering the various possible
return values for a function known to be able to return NULL).
libcpychecker/__init__.py | 7 +++++--
libcpychecker/refcounts.py | 13 ++++++++++++-
tests/cpychecker/absinterp/custom-strdup/script.py | 3 ++-
.../read-through-global-ptr-unchecked/script.py | 3 ++-
.../write-through-arg-unchecked/script.py | 3 ++-
.../write-through-global-ptr-unchecked/script.py | 3 ++-
6 files changed, 25 insertions(+), 7 deletions(-)
---
diff --git a/libcpychecker/__init__.py b/libcpychecker/__init__.py
index 65afa31..3d431b4 100644
--- a/libcpychecker/__init__.py
+++ b/libcpychecker/__init__.py
@@ -27,11 +27,13 @@ class CpyChecker(gcc.GimplePass):
def __init__(self,
dump_traces=False,
show_traces=False,
- verify_refcounting=False):
+ verify_refcounting=False,
+ show_possible_null_derefs=False):
gcc.GimplePass.__init__(self, 'cpychecker')
self.dump_traces = dump_traces
self.show_traces = show_traces
self.verify_refcounting = verify_refcounting
+ self.show_possible_null_derefs = show_possible_null_derefs
def execute(self, fun):
if fun:
@@ -40,7 +42,8 @@ class CpyChecker(gcc.GimplePass):
# The refcount code is too buggy for now to be on by default:
if self.verify_refcounting:
- check_refcounts(fun, self.dump_traces, self.show_traces)
+ check_refcounts(fun, self.dump_traces, self.show_traces,
+ self.show_possible_null_derefs)
def is_a_method_callback(decl):
methods = get_all_PyMethodDef_methods()
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index ec9fe61..1982fb1 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1388,7 +1388,8 @@ class ExceptionStateAnnotator(Annotator):
return result
-def check_refcounts(fun, dump_traces=False, show_traces=False):
+def check_refcounts(fun, dump_traces=False, show_traces=False,
+ show_possible_null_derefs=False):
"""
The top-level function of the refcount checker, checking the refcounting
behavior of a function
@@ -1445,6 +1446,16 @@ def check_refcounts(fun, dump_traces=False, show_traces=False):
# This trace bails early with a fatal error; it probably doesn't
# have a return value
log('trace.err: %s %r', trace.err, trace.err)
+
+ # Unless explicitly enabled, don't report on NULL pointer
+ # dereferences that are only possible, not definite: it may be
+ # that there are invariants that we know nothing about that mean
+ # that they can't happen:
+ if isinstance(trace.err, NullPtrDereference):
+ if not trace.err.isdefinite:
+ if not show_possible_null_derefs:
+ continue
+
err = rep.make_error(fun, trace.err.loc, str(trace.err))
err.add_trace(trace)
# FIXME: in our example this ought to mention where the values came from
diff --git a/tests/cpychecker/absinterp/custom-strdup/script.py b/tests/cpychecker/absinterp/custom-strdup/script.py
index 6bb62fd..7683fd6 100644
--- a/tests/cpychecker/absinterp/custom-strdup/script.py
+++ b/tests/cpychecker/absinterp/custom-strdup/script.py
@@ -19,4 +19,5 @@
from libcpychecker import main
main(verify_refcounting=True,
- dump_traces=True)
+ dump_traces=True,
+ show_possible_null_derefs=True)
diff --git a/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/script.py b/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/script.py
index 6bb62fd..7683fd6 100644
--- a/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/script.py
+++ b/tests/cpychecker/absinterp/read-through-global-ptr-unchecked/script.py
@@ -19,4 +19,5 @@
from libcpychecker import main
main(verify_refcounting=True,
- dump_traces=True)
+ dump_traces=True,
+ show_possible_null_derefs=True)
diff --git a/tests/cpychecker/absinterp/write-through-arg-unchecked/script.py b/tests/cpychecker/absinterp/write-through-arg-unchecked/script.py
index fdd5ba3..e57921b 100644
--- a/tests/cpychecker/absinterp/write-through-arg-unchecked/script.py
+++ b/tests/cpychecker/absinterp/write-through-arg-unchecked/script.py
@@ -19,4 +19,5 @@
from libcpychecker import main
main(verify_refcounting=True,
dump_traces=True,
- show_traces=False)
+ show_traces=False,
+ show_possible_null_derefs=True)
diff --git a/tests/cpychecker/absinterp/write-through-global-ptr-unchecked/script.py b/tests/cpychecker/absinterp/write-through-global-ptr-unchecked/script.py
index 6bb62fd..7683fd6 100644
--- a/tests/cpychecker/absinterp/write-through-global-ptr-unchecked/script.py
+++ b/tests/cpychecker/absinterp/write-through-global-ptr-unchecked/script.py
@@ -19,4 +19,5 @@
from libcpychecker import main
main(verify_refcounting=True,
- dump_traces=True)
+ dump_traces=True,
+ show_possible_null_derefs=True)
12 years, 8 months
[gcc-python-plugin] cpychecker: alphabetize the various API implementations
by David Hugh Malcolm
commit 4ddba5e47545169d2fae3f01e33f7b7d5e437f2b
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 15:12:47 2011 -0400
cpychecker: alphabetize the various API implementations
libcpychecker/refcounts.py | 171 ++++++++++++++++++++++++++------------------
1 files changed, 100 insertions(+), 71 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index b649ab5..ec9fe61 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -545,7 +545,30 @@ class MyState(State):
UnknownValue(returntype, stmt.loc),
desc)]
- # Specific Python API function implementations:
+ # Specific Python API function implementations
+ # (keep this list alphabetized, discounting case and underscores)
+
+ ########################################################################
+ # PyArg_*
+ ########################################################################
+ def impl_PyArg_ParseTuple(self, stmt):
+ # Decl:
+ # PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);
+ # Also:
+ # #define PyArg_ParseTuple _PyArg_ParseTuple_SizeT
+
+ s_success = self.mkstate_concrete_return_of(stmt, 1)
+
+ s_failure = self.mkstate_concrete_return_of(stmt, 0)
+ # Various errors are possible, but a TypeError is always possible
+ # e.g. for the case of the wrong number of arguments:
+ s_failure.set_exception('PyExc_TypeError')
+
+ return self.make_transitions_for_fncall(stmt, s_success, s_failure)
+
+ ########################################################################
+ # PyDict_*
+ ########################################################################
def impl_PyDict_GetItemString(self, stmt):
# Declared in dictobject.h:
# PyAPI_FUNC(PyObject *) PyDict_GetItemString(PyObject *dp, const char *key);
@@ -565,6 +588,9 @@ class MyState(State):
t_notfound]
#t_memoryexc]
+ ########################################################################
+ # PyErr_*
+ ########################################################################
def impl_PyErr_Print(self, stmt):
# Declared in pythonrun.h:
# PyAPI_FUNC(void) PyErr_Print(void);
@@ -595,47 +621,29 @@ class MyState(State):
t_next.dest.exception_rvalue = v_exc
return [t_next]
- def impl_PyList_New(self, stmt):
+ ########################################################################
+ # Py_InitModule*
+ ########################################################################
+ def impl_Py_InitModule4_64(self, stmt):
# Decl:
- # PyObject* PyList_New(Py_ssize_t len)
- # Returns a new reference, or raises MemoryError
- lenarg = self.eval_rvalue(stmt.args[0], stmt.loc)
- check_isinstance(lenarg, AbstractValue)
- newobj, success, failure = self.impl_object_ctor(stmt,
- 'PyListObject', 'PyList_Type')
- # Set ob_size:
- ob_size = success.dest.make_field_region(newobj, 'ob_size')
- success.dest.value_for_region[ob_size] = lenarg
-
- # "Allocate" ob_item, and set it up so that all of the array is
- # treated as NULL:
- ob_item_region = success.dest.make_heap_region(
- 'ob_item array for PyListObject',
- stmt)
- success.dest.value_for_region[ob_item_region] = \
- ConcreteValue(get_PyObjectPtr(),
- stmt.loc, 0)
-
- ob_item = success.dest.make_field_region(newobj, 'ob_item')
- success.dest.value_for_region[ob_item] = PointerToRegion(get_PyObjectPtr().pointer,
- stmt.loc,
- ob_item_region)
-
- return [success, failure]
-
- def impl_PyLong_FromLong(self, stmt):
- newobj, success, failure = self.impl_object_ctor(stmt,
- 'PyLongObject', 'PyLong_Type')
- return [success, failure]
-
- def impl_PyLong_FromString(self, stmt):
- # Declared in longobject.h as:
- # PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
- # Defined in longobject.c
- newobj, success, failure = self.impl_object_ctor(stmt,
- 'PyLongObject', 'PyLong_Type')
- return [success, failure]
+ # PyAPI_FUNC(PyObject *) Py_InitModule4(const char *name, PyMethodDef *methods,
+ # const char *doc, PyObject *self,
+ # int apiver);
+ # Returns a borrowed reference
+ #
+ # FIXME:
+ # On 64-bit:
+ # #define Py_InitModule4 Py_InitModule4_64
+ # with tracerefs:
+ # #define Py_InitModule4 Py_InitModule4TraceRefs_64
+ # #define Py_InitModule4 Py_InitModule4TraceRefs
+ s_success = self.mkstate_borrowed_ref(stmt, 'output from Py_InitModule4')
+ s_failure = self.mkstate_exception(stmt, 'Py_InitModule4')
+ return self.make_transitions_for_fncall(stmt, s_success, s_failure)
+ ########################################################################
+ # PyList_*
+ ########################################################################
def impl_PyList_Append(self, stmt):
# Declared in listobject.h as:
# PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);
@@ -666,6 +674,34 @@ class MyState(State):
return self.make_transitions_for_fncall(stmt, s_success, s_failure)
+ def impl_PyList_New(self, stmt):
+ # Decl:
+ # PyObject* PyList_New(Py_ssize_t len)
+ # Returns a new reference, or raises MemoryError
+ lenarg = self.eval_rvalue(stmt.args[0], stmt.loc)
+ check_isinstance(lenarg, AbstractValue)
+ newobj, success, failure = self.impl_object_ctor(stmt,
+ 'PyListObject', 'PyList_Type')
+ # Set ob_size:
+ ob_size = success.dest.make_field_region(newobj, 'ob_size')
+ success.dest.value_for_region[ob_size] = lenarg
+
+ # "Allocate" ob_item, and set it up so that all of the array is
+ # treated as NULL:
+ ob_item_region = success.dest.make_heap_region(
+ 'ob_item array for PyListObject',
+ stmt)
+ success.dest.value_for_region[ob_item_region] = \
+ ConcreteValue(get_PyObjectPtr(),
+ stmt.loc, 0)
+
+ ob_item = success.dest.make_field_region(newobj, 'ob_item')
+ success.dest.value_for_region[ob_item] = PointerToRegion(get_PyObjectPtr().pointer,
+ stmt.loc,
+ ob_item_region)
+
+ return [success, failure]
+
def impl_PyList_SetItem(self, stmt):
# Decl:
# int PyList_SetItem(PyObject *list, Py_ssize_t index, PyObject *item)
@@ -706,38 +742,25 @@ class MyState(State):
return result
- def impl_PyArg_ParseTuple(self, stmt):
- # Decl:
- # PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);
- # Also:
- # #define PyArg_ParseTuple _PyArg_ParseTuple_SizeT
-
- s_success = self.mkstate_concrete_return_of(stmt, 1)
-
- s_failure = self.mkstate_concrete_return_of(stmt, 0)
- # Various errors are possible, but a TypeError is always possible
- # e.g. for the case of the wrong number of arguments:
- s_failure.set_exception('PyExc_TypeError')
-
- return self.make_transitions_for_fncall(stmt, s_success, s_failure)
+ ########################################################################
+ # PyLong_*
+ ########################################################################
+ def impl_PyLong_FromLong(self, stmt):
+ newobj, success, failure = self.impl_object_ctor(stmt,
+ 'PyLongObject', 'PyLong_Type')
+ return [success, failure]
- def impl_Py_InitModule4_64(self, stmt):
- # Decl:
- # PyAPI_FUNC(PyObject *) Py_InitModule4(const char *name, PyMethodDef *methods,
- # const char *doc, PyObject *self,
- # int apiver);
- # Returns a borrowed reference
- #
- # FIXME:
- # On 64-bit:
- # #define Py_InitModule4 Py_InitModule4_64
- # with tracerefs:
- # #define Py_InitModule4 Py_InitModule4TraceRefs_64
- # #define Py_InitModule4 Py_InitModule4TraceRefs
- s_success = self.mkstate_borrowed_ref(stmt, 'output from Py_InitModule4')
- s_failure = self.mkstate_exception(stmt, 'Py_InitModule4')
- return self.make_transitions_for_fncall(stmt, s_success, s_failure)
+ def impl_PyLong_FromString(self, stmt):
+ # Declared in longobject.h as:
+ # PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
+ # Defined in longobject.c
+ newobj, success, failure = self.impl_object_ctor(stmt,
+ 'PyLongObject', 'PyLong_Type')
+ return [success, failure]
+ ########################################################################
+ # PyObject_*
+ ########################################################################
def impl__PyObject_New(self, stmt):
# Declaration in objimpl.h:
# PyAPI_FUNC(PyObject *) _PyObject_New(PyTypeObject *);
@@ -778,7 +801,9 @@ class MyState(State):
'PyStringObject', 'PyString_Type')
return [t_success, t_failure]
-
+ ########################################################################
+ # PyString_*
+ ########################################################################
def impl_PyString_FromString(self, stmt):
# Declared in stringobject.h as:
# PyAPI_FUNC(PyObject *) PyString_FromString(const char *);
@@ -786,6 +811,10 @@ class MyState(State):
'PyStringObject', 'PyString_Type')
return [t_success, t_failure]
+ ########################################################################
+ # (end of Python API implementations)
+ ########################################################################
+
def _get_transitions_for_GimpleCall(self, stmt):
log('stmt.lhs: %s %r', stmt.lhs, stmt.lhs)
log('stmt.fn: %s %r', stmt.fn, stmt.fn)
12 years, 8 months
[gcc-python-plugin] cpychecker: implement PyLong_FromString, PyObject_Str, PyString_FromString
by David Hugh Malcolm
commit 582af89b8546da8bd5993050ea3ed27d0b4b7a94
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 15:04:07 2011 -0400
cpychecker: implement PyLong_FromString, PyObject_Str, PyString_FromString
libcpychecker/refcounts.py | 25 +++++++++++++++++++++++++
1 files changed, 25 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 70f81ad..b649ab5 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -628,6 +628,14 @@ class MyState(State):
'PyLongObject', 'PyLong_Type')
return [success, failure]
+ def impl_PyLong_FromString(self, stmt):
+ # Declared in longobject.h as:
+ # PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
+ # Defined in longobject.c
+ newobj, success, failure = self.impl_object_ctor(stmt,
+ 'PyLongObject', 'PyLong_Type')
+ return [success, failure]
+
def impl_PyList_Append(self, stmt):
# Declared in listobject.h as:
# PyAPI_FUNC(int) PyList_Append(PyObject *, PyObject *);
@@ -761,6 +769,23 @@ class MyState(State):
t_failure.dest.set_exception('PyExc_MemoryError')
return [t_success, t_failure]
+ def impl_PyObject_Str(self, stmt):
+ # Declared in object.h as:
+ # PyAPI_FUNC(PyObject *) PyObject_Str(PyObject *);
+ # also with:
+ # #define PyObject_Bytes PyObject_Str
+ newobj, t_success, t_failure = self.impl_object_ctor(stmt,
+ 'PyStringObject', 'PyString_Type')
+ return [t_success, t_failure]
+
+
+ def impl_PyString_FromString(self, stmt):
+ # Declared in stringobject.h as:
+ # PyAPI_FUNC(PyObject *) PyString_FromString(const char *);
+ newobj, t_success, t_failure = self.impl_object_ctor(stmt,
+ 'PyStringObject', 'PyString_Type')
+ return [t_success, t_failure]
+
def _get_transitions_for_GimpleCall(self, stmt):
log('stmt.lhs: %s %r', stmt.lhs, stmt.lhs)
log('stmt.fn: %s %r', stmt.fn, stmt.fn)
12 years, 8 months
[gcc-python-plugin] run the selftests in alphabetical order
by David Hugh Malcolm
commit 7fe1e94f760c7e352e7fcb53eedcc209b20098ce
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 15:02:00 2011 -0400
run the selftests in alphabetical order
run-test-suite.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
---
diff --git a/run-test-suite.py b/run-test-suite.py
index 04a4a43..8ec682b 100644
--- a/run-test-suite.py
+++ b/run-test-suite.py
@@ -249,7 +249,7 @@ if six.MAXSIZE == 0x7fffffff:
num_passes = 0
failed_tests = []
-for testdir in testdirs:
+for testdir in sorted(testdirs):
try:
sys.stdout.write('%s: ' % testdir)
run_test(testdir)
12 years, 8 months
[gcc-python-plugin] cpychecker: implement PyErr_Print/PrintEx
by David Hugh Malcolm
commit 5b4d2632ca5709051620125ab2bb43f9cd1565aa
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 14:57:59 2011 -0400
cpychecker: implement PyErr_Print/PrintEx
libcpychecker/refcounts.py | 20 +++++++++
tests/cpychecker/refcounts/PyErr_PrintEx/input.c | 46 ++++++++++++++++++++
tests/cpychecker/refcounts/PyErr_PrintEx/script.py | 22 +++++++++
.../cpychecker/refcounts/PyErr_PrintEx/stderr.txt | 8 +++
.../cpychecker/refcounts/PyErr_PrintEx/stdout.txt | 10 ++++
5 files changed, 106 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index d76b50a..70f81ad 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -565,6 +565,26 @@ class MyState(State):
t_notfound]
#t_memoryexc]
+ def impl_PyErr_Print(self, stmt):
+ # Declared in pythonrun.h:
+ # PyAPI_FUNC(void) PyErr_Print(void);
+ # Defined in pythonrun.c
+
+ t_next = self.mktrans_nop(stmt, 'PyErr_Print')
+ # Clear the error indicator:
+ t_next.dest.exception_rvalue = make_null_pyobject_ptr(stmt)
+ return [t_next]
+
+ def impl_PyErr_PrintEx(self, stmt):
+ # Declared in pythonrun.h:
+ # PyAPI_FUNC(void) PyErr_PrintEx(int);
+ # Defined in pythonrun.c
+
+ t_next = self.mktrans_nop(stmt, 'PyErr_PrintEx')
+ # Clear the error indicator:
+ t_next.dest.exception_rvalue = make_null_pyobject_ptr(stmt)
+ return [t_next]
+
def impl_PyErr_SetString(self, stmt):
# Declared in pyerrors.h:
# PyAPI_FUNC(void) PyErr_SetString(PyObject *, const char *);
diff --git a/tests/cpychecker/refcounts/PyErr_PrintEx/input.c b/tests/cpychecker/refcounts/PyErr_PrintEx/input.c
new file mode 100644
index 0000000..90717ce
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyErr_PrintEx/input.c
@@ -0,0 +1,46 @@
+/*
+ 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/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of invocation of PyErr_PrintEx
+*/
+
+PyObject*
+test(void)
+{
+ /* Set the exception: */
+ PyErr_SetString(PyExc_RuntimeError,
+ "This function always fails");
+
+ /* This ought to clear it: */
+ PyErr_PrintEx(1);
+
+ /* ...and this will be an error: the exception has been cleared: */
+ return NULL;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyErr_PrintEx/script.py b/tests/cpychecker/refcounts/PyErr_PrintEx/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyErr_PrintEx/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/PyErr_PrintEx/stderr.txt b/tests/cpychecker/refcounts/PyErr_PrintEx/stderr.txt
new file mode 100644
index 0000000..c64ded4
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyErr_PrintEx/stderr.txt
@@ -0,0 +1,8 @@
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c: In function 'test':
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c:37:5: error: returning (PyObject*)NULL without setting an exception
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c:30:20: note: when calling PyErr_SetString() at: PyErr_SetString(PyExc_RuntimeError,
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c:30:20: note: thread-local exception state now has value: unknown struct PyObject * from /usr/include/python2.7/pyerrors.h:140
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c:34:18: note: when calling PyErr_PrintEx() at: PyErr_PrintEx(1);
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c:34:18: note: thread-local exception state now has value: (struct PyObject *)0 from tests/cpychecker/refcounts/PyErr_PrintEx/input.c:34
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c:37:5: note: when returning at: return NULL;
+tests/cpychecker/refcounts/PyErr_PrintEx/input.c:28:1: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyErr_PrintEx/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyErr_PrintEx/stdout.txt b/tests/cpychecker/refcounts/PyErr_PrintEx/stdout.txt
new file mode 100644
index 0000000..3e0d972
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyErr_PrintEx/stdout.txt
@@ -0,0 +1,10 @@
+Trace 0:
+ Transitions:
+ 'calling PyErr_SetString()'
+ 'calling PyErr_PrintEx()'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyErr_PrintEx/input.c', line=37), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyErr_PrintEx/input.c:37
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyErr_PrintEx/input.c:34
12 years, 8 months
[gcc-python-plugin] cpychecker: display history of thread-local exception state when reporting on exception-handling err
by David Hugh Malcolm
commit 3952a2201c7c8c52beba98cf990f98964d4b0c2a
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Aug 30 14:55:43 2011 -0400
cpychecker: display history of thread-local exception state when reporting on exception-handling errors
libcpychecker/refcounts.py | 32 +++++++++++++++++++++++++++-----
1 files changed, 27 insertions(+), 5 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 9ad71ea..d76b50a 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1231,10 +1231,8 @@ class DebugAnnotator(Annotator):
% (region, dest_value))))
# Show exception information:
- if transition.dest.exception_rvalue != transition.src.exception_rvalue:
- result.append(Note(loc,
- ('thread-local exception state now has value: %s'
- % transition.dest.exception_rvalue)))
+ esa = ExceptionStateAnnotator()
+ result += esa.get_notes(transition)
return result
@@ -1292,6 +1290,30 @@ class RefcountAnnotator(Annotator):
('all refs: %s' % dest_refs)))
return result
+class ExceptionStateAnnotator(Annotator):
+ """
+ Annotate a trace with information on changes to the thread-local exception
+ state
+ """
+ def get_notes(self, transition):
+ """
+ Add a note to every transition that affects thread-local exception
+ state
+ """
+ loc = transition.src.get_gcc_loc_or_none()
+ if loc is None:
+ # (we can't add a note without a valid location)
+ return []
+
+ result = []
+
+ if transition.dest.exception_rvalue != transition.src.exception_rvalue:
+ result.append(Note(loc,
+ ('thread-local exception state now has value: %s'
+ % transition.dest.exception_rvalue)))
+
+ return result
+
def check_refcounts(fun, dump_traces=False, show_traces=False):
"""
The top-level function of the refcount checker, checking the refcounting
@@ -1484,7 +1506,7 @@ def check_refcounts(fun, dump_traces=False, show_traces=False):
err = rep.make_error(fun,
endstate.get_gcc_loc(fun),
'returning (PyObject*)NULL without setting an exception')
- err.add_trace(trace)
+ err.add_trace(trace, ExceptionStateAnnotator())
# (all traces analysed)
12 years, 8 months