[gcc-python-plugin] cpychecker: add __attribute__((cpychecker_sets_exception))
by David Hugh Malcolm
commit 9e00c3a6648660c65415b9f2799f97da925da99f
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Feb 28 14:09:44 2012 -0500
cpychecker: add __attribute__((cpychecker_sets_exception))
docs/cpychecker.rst | 41 +++++++++++++---
libcpychecker/absinterp.py | 10 ++++
libcpychecker/attributes.py | 16 ++++++
libcpychecker/refcounts.py | 17 ++++++-
.../sets-exception/correct-marking/input.c | 49 ++++++++++++++++++
.../sets-exception/correct-marking/script.py | 22 ++++++++
.../sets-exception/correct-marking/stdout.txt | 6 ++
.../sets-exception/correct-usage/input.c | 51 +++++++++++++++++++
.../sets-exception/correct-usage/script.py | 22 ++++++++
.../sets-exception/correct-usage/stdout.txt | 18 +++++++
.../sets-exception/incorrect-marking/input.c | 53 ++++++++++++++++++++
.../sets-exception/incorrect-marking/metadata.ini | 3 +
.../sets-exception/incorrect-marking/script.py | 22 ++++++++
.../sets-exception/incorrect-marking/stderr.txt | 4 ++
.../sets-exception/incorrect-marking/stdout.txt | 8 +++
15 files changed, 334 insertions(+), 8 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index 00670b6..ebbfb04 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -301,11 +301,36 @@ behavior is acceptable. Currently this covers functions used as `tp_iternext
<http://docs.python.org/c-api/typeobj.html#tp_iternext>`_ callbacks of a
``PyTypeObject``.
-Many functions within the CPython API return negative values to signify an
-error, setting the exception state, and the checker knows about these.
+If you have a helper function that always sets an exception, you can mark this
+property using a custom GCC attribute:
-If you have a function that behaves in a similar way, you can inform the
-checker of this behavior by marking it with a custom GCC attribute:
+.. code-block:: c
+
+ __attribute__((cpychecker_sets_exception))
+
+which can be used to mark function declarations.
+
+.. code-block:: c
+
+ /* The checker automatically defines this preprocessor name when creating
+ the custom attribute: */
+ #if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_SETS_EXCEPTION \
+ __attribute__((cpychecker_sets_exception))
+ #else
+ #define CPYCHECKER_SETS_EXCEPTION
+ #endif
+
+ extern void raise_error(const char *msg)
+ CPYCHECKER_SETS_EXCEPTION;
+
+Given the above, the checker will know that an exception is set whenever a
+call to `raise_error()` occurs. It will also verify that `raise_error()`
+actually behaves this way when compiling the implementation of `raise_error`.
+
+There is an analogous attribute for the case where a function returns a
+negative value to signify an error, where the exception state is set whenever
+a **negative** value is returned:
.. code-block:: c
@@ -318,19 +343,21 @@ which can be used to mark function declarations.
/* The checker automatically defines this preprocessor name when creating
the custom attribute: */
#if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
- #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION() \
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \
__attribute__((cpychecker_negative_result_sets_exception))
#else
- #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION()
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
#endif
extern int foo(void)
- CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION();
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION;
Given the above, the checker will know that an exception is raised whenever a
call to `foo` returns a negative value. It will also verify that `foo`
actually behaves this way when compiling the implementation of `foo`.
+The checker already knows about many of the functions within the CPython API
+which behave this way.
Format string checking
----------------------
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 8ad1af6..a7295d7 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -2301,6 +2301,16 @@ class State(object):
t_iter.dest.cpython.steal_reference(v_arg, stmt.loc)
# cpython: handle functions that have been marked as setting the
+ # exception state:
+ from libcpychecker.attributes import fnnames_setting_exception
+ if fnname in fnnames_setting_exception:
+ for t_iter in transitions:
+ # Mark the global exception state (with an arbitrary
+ # error):
+ t_iter.dest.cpython.set_exception('PyExc_MemoryError',
+ stmt.loc)
+
+ # cpython: handle functions that have been marked as setting the
# exception state when they return a negative value:
from libcpychecker.attributes import fnnames_setting_exception_on_negative_result
if fnname in fnnames_setting_exception_on_negative_result:
diff --git a/libcpychecker/attributes.py b/libcpychecker/attributes.py
index 216601f..d76020c 100644
--- a/libcpychecker/attributes.py
+++ b/libcpychecker/attributes.py
@@ -21,6 +21,7 @@ from libcpychecker.types import register_type_object
# Recorded attribute data:
fnnames_returning_borrowed_refs = set()
+fnnames_setting_exception = set()
fnnames_setting_exception_on_negative_result = set()
# A dictionary mapping from fnname to set of argument indices:
@@ -81,6 +82,21 @@ def register_our_attributes():
attribute_callback_type_object_for_typedef)
gcc.define_macro('WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE')
+ # Handler for __attribute__((cpychecker_sets_exception))
+ # and #ifdef WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE
+ def attribute_callback_for_sets_exception(*args):
+ if 0:
+ print('attribute_callback_for_sets_exception(%r)' % args)
+ check_isinstance(args[0], gcc.FunctionDecl)
+ fnname = args[0].name
+ fnnames_setting_exception.add(fnname)
+
+ gcc.register_attribute('cpychecker_sets_exception',
+ 0, 0,
+ False, False, False,
+ attribute_callback_for_sets_exception)
+ gcc.define_macro('WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE')
+
# Handler for __attribute__((cpychecker_negative_result_sets_exception))
# and #ifdef WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE
def attribute_callback_for_negative_result_sets_exception(*args):
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 068ec42..7fbd1ea 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -27,7 +27,8 @@ from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc, check_isinstance
from libcpychecker.absinterp import *
from libcpychecker.attributes import fnnames_returning_borrowed_refs, \
- stolen_refs_by_fnname, fnnames_setting_exception_on_negative_result
+ stolen_refs_by_fnname, fnnames_setting_exception, \
+ fnnames_setting_exception_on_negative_result
from libcpychecker.diagnostics import Reporter, Annotator, Note
from libcpychecker.PyArg_ParseTuple import PyArgParseFmt, FormatStringWarning,\
TypeCheckCheckerType, TypeCheckResultType, \
@@ -3668,6 +3669,20 @@ def warn_about_NULL_without_exception(v_return,
w.add_trace(trace, ExceptionStateAnnotator())
# If this is function was marked with our custom
+ # __attribute__((cpychecker_sets_exception))
+ # then verify that this is the case:
+ if fun.decl.name in fnnames_setting_exception:
+ if (isinstance(endstate.cpython.exception_rvalue,
+ ConcreteValue)
+ and endstate.cpython.exception_rvalue.value == 0):
+ w = rep.make_warning(fun,
+ endstate.get_gcc_loc(fun),
+ ('function is marked with'
+ ' __attribute__((cpychecker_sets_exception))'
+ ' but can return without setting an exception'))
+ w.add_trace(trace, ExceptionStateAnnotator())
+
+ # If this is function was marked with our custom
# __attribute__((cpychecker_negative_result_sets_exception))
# then verify that this is the case:
if fun.decl.name in fnnames_setting_exception_on_negative_result:
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/input.c b/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/input.c
new file mode 100644
index 0000000..34264d6
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/input.c
@@ -0,0 +1,49 @@
+/*
+ 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>
+
+/*
+ Test of compiling a function that's been (correctly) marked as setting
+ an exception
+*/
+
+#if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_SETS_EXCEPTION \
+ __attribute__((cpychecker_sets_exception))
+#else
+ #define CPYCHECKER_SETS_EXCEPTION
+ #error (This should have been defined)
+#endif
+
+extern void test(const char *msg)
+ CPYCHECKER_SETS_EXCEPTION;
+
+extern void test(const char *msg)
+{
+ PyErr_SetString(PyExc_RuntimeError, msg);
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/script.py b/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/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/refcounts/attributes/sets-exception/correct-marking/stdout.txt b/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/stdout.txt
new file mode 100644
index 0000000..a0ef682
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/correct-marking/stdout.txt
@@ -0,0 +1,6 @@
+Trace 0:
+ Transitions:
+ 'calling PyErr_SetString()'
+ 'returning'
+ Exception:
+ unknown struct PyObject * from /usr/include/python?.?/pyerrors.h:nn
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/input.c b/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/input.c
new file mode 100644
index 0000000..cc12d0b
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/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>
+
+/*
+ Test of compiling a function that (correctly) uses a function that's been
+ marked as setting an exception
+*/
+
+#if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_SETS_EXCEPTION \
+ __attribute__((cpychecker_sets_exception))
+#else
+ #define CPYCHECKER_SETS_EXCEPTION
+ #error (This should have been defined)
+#endif
+
+extern int foo(void)
+ CPYCHECKER_SETS_EXCEPTION;
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ foo();
+ return NULL;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/script.py b/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/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/refcounts/attributes/sets-exception/correct-usage/stdout.txt b/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/stdout.txt
new file mode 100644
index 0000000..c716909
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/stdout.txt
@@ -0,0 +1,18 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/input.c', line=42), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/input.c:42
+ 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/attributes/sets-exception/correct-usage/input.c', line=39), 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/attributes/sets-exception/correct-usage/input.c', line=39), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/attributes/sets-exception/correct-usage/input.c:41
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c
new file mode 100644
index 0000000..953bab9
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c
@@ -0,0 +1,53 @@
+/*
+ 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>
+
+/*
+ Test of compiling a function that's been (incorrectly) marked as setting
+ an exception
+*/
+
+#if defined(WITH_CPYCHECKER_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_SETS_EXCEPTION \
+ __attribute__((cpychecker_sets_exception))
+#else
+ #define CPYCHECKER_SETS_EXCEPTION
+ #error (This should have been defined)
+#endif
+
+extern int test(void)
+ CPYCHECKER_SETS_EXCEPTION;
+
+extern int test(void)
+{
+ /*
+ Erroneously, doesn't set an exception on this path,
+ despite having been marked as such:
+ */
+ return 0;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/metadata.ini b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/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/attributes/sets-exception/incorrect-marking/script.py b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/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/refcounts/attributes/sets-exception/incorrect-marking/stderr.txt b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/stderr.txt
new file mode 100644
index 0000000..1c5e61f
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/stderr.txt
@@ -0,0 +1,4 @@
+tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c: In function 'test':
+tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c:44:nn: warning: function is marked with __attribute__((cpychecker_sets_exception)) but can return without setting an exception [enabled by default]
+tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c:44:nn: note: returning at: return 0;
+tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c:39:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/stdout.txt b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/stdout.txt
new file mode 100644
index 0000000..0422010
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/stdout.txt
@@ -0,0 +1,8 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c', line=44), value=0)
+ str(): (int)0 from tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c:44
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception/incorrect-marking/input.c:39
12 years, 3 months
[gcc-python-plugin] cpychecker: rewrite CPython.steal_reference to use CPython.change_refcount
by David Hugh Malcolm
commit c455b6e69376d4c815dceb2a68d84f374437b329
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Feb 27 16:28:27 2012 -0500
cpychecker: rewrite CPython.steal_reference to use CPython.change_refcount
Rewrite CPython.steal_reference() to use CPython.change_refcount(), so that
it works in the same way as the other refcount-modifying functions (add_ref
etc)
Fixes a traceback seen when analyzing psycopg:
psycopg/psycopgmodule.c: In function ‘init_psycopg’:
psycopg/psycopgmodule.c:981:23: error: Unhandled Python exception raised calling 'execute' method
...
KeyError: Region('connectionType.ob_refcnt')
libcpychecker/absinterp.py | 2 +-
libcpychecker/refcounts.py | 23 +++++------
.../PyModule_AddObject/incorrect-with-cast/input.c | 42 ++++++++++++++++++++
.../incorrect-with-cast/metadata.ini | 3 +
.../incorrect-with-cast/script.py | 22 ++++++++++
.../incorrect-with-cast/stderr.txt | 8 ++++
.../incorrect-with-cast/stdout.txt | 39 ++++++++++++++++++
7 files changed, 126 insertions(+), 13 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index e086dfa..8ad1af6 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -2298,7 +2298,7 @@ class State(object):
for argindex in stolen_refs_by_fnname[stmt.fn.operand.name]:
v_arg = args[argindex-1]
if isinstance(v_arg, PointerToRegion):
- t_iter.dest.cpython.steal_reference(v_arg.region)
+ t_iter.dest.cpython.steal_reference(v_arg, stmt.loc)
# cpython: handle functions that have been marked as setting the
# exception state when they return a negative value:
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index fa0caa7..068ec42 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -512,18 +512,18 @@ class CPython(Facet):
return self.object_ctor(stmt,
typename, typeobjname)
- def steal_reference(self, region):
- log('steal_reference(%r)', region)
- check_isinstance(region, Region)
- ob_refcnt = self.state.make_field_region(region, 'ob_refcnt')
- value = self.state.value_for_region[ob_refcnt]
- if isinstance(value, RefcountValue):
+ def steal_reference(self, pyobjectptr, loc):
+ def _steal_ref(v_old):
# We have a value known relative to all of the refs owned by the
# rest of the program. Given that the rest of the program is
# stealing a ref, that is increasing by one, hence our value must
# go down by one:
- self.state.value_for_region[ob_refcnt] = RefcountValue(value.relvalue - 1,
- value.min_external + 1)
+ return RefcountValue(v_old.relvalue - 1,
+ v_old.min_external + 1)
+ check_isinstance(pyobjectptr, PointerToRegion)
+ self.change_refcount(pyobjectptr,
+ loc,
+ _steal_ref)
def make_sane_object(self, stmt, name, v_refcount, r_typeobj=None):
"""
@@ -1078,7 +1078,7 @@ class CPython(Facet):
if isinstance(unit, CodeSO):
t_success.dest.cpython.add_external_ref(v_vararg, stmt.loc)
else:
- t_success.dest.cpython.steal_reference(v_vararg.region)
+ t_success.dest.cpython.steal_reference(v_vararg, stmt.loc)
return True
t_success, t_failure = self.make_transitions_for_new_ref_or_fail(stmt, fnmeta)
@@ -1981,8 +1981,7 @@ class CPython(Facet):
# FIXME: update refcounts
# "Steal" a reference to item:
if isinstance(v_item, PointerToRegion):
- check_isinstance(v_item.region, Region)
- s_success.cpython.steal_reference(v_item.region)
+ s_success.cpython.steal_reference(v_item, stmt.loc)
# and discards a
# reference to an item already in the list at the affected position.
@@ -2167,7 +2166,7 @@ class CPython(Facet):
# On success, steals a ref from v_value:
s_success = self.state.mkstate_concrete_return_of(stmt, 0)
- s_success.cpython.steal_reference(v_value.region)
+ s_success.cpython.steal_reference(v_value, stmt.loc)
# Can fail with memory error, overflow error:
s_failure = self.state.mkstate_concrete_return_of(stmt, -1)
diff --git a/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c
new file mode 100644
index 0000000..0e93c7c
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c
@@ -0,0 +1,42 @@
+/*
+ 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>
+
+/*
+ Reproducer for traceback seen when analyzing psycopg
+*/
+
+extern PyTypeObject fooType;
+
+int
+test(PyObject *module)
+{
+ return PyModule_AddObject(module,
+ "foo",
+ (PyObject*)&fooType);
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/metadata.ini b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/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/PyModule_AddObject/incorrect-with-cast/script.py b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/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/PyModule_AddObject/incorrect-with-cast/stderr.txt b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/stderr.txt
new file mode 100644
index 0000000..9b2415f
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/stderr.txt
@@ -0,0 +1,8 @@
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c: In function 'test':
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31:nn: warning: ob_refcnt of fooType is 1 too low [enabled by default]
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31:nn: note: was expecting final ob_refcnt to be N + 0 (for some unknown N)
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31:nn: note: but final ob_refcnt is N + -1
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31:nn: note: when PyModule_AddObject() succeeds at: return PyModule_AddObject(module,
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31:nn: note: ob_refcnt is now refs: -1 + N where N >= 2
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31:nn: note: returning at: return PyModule_AddObject(module,
+tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:30:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/stdout.txt b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/stdout.txt
new file mode 100644
index 0000000..1a98fcf
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/stdout.txt
@@ -0,0 +1,39 @@
+Trace 0:
+ Transitions:
+ 'when PyModule_AddObject() succeeds'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c', line=31), value=0)
+ str(): (int)0 from tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31
+ Region("region-for-arg-gcc.ParmDecl('module')"):
+ repr(): Region("region-for-arg-gcc.ParmDecl('module')")
+ str(): Region("region-for-arg-gcc.ParmDecl('module')")
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('module')"))
+ RegionForGlobal(gcc.VarDecl('fooType')):
+ repr(): RegionForGlobal(gcc.VarDecl('fooType'))
+ str(): RegionForGlobal(gcc.VarDecl('fooType'))
+ r->ob_refcnt: refs: -1 + N where N >= 2
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:30
+
+Trace 1:
+ Transitions:
+ 'when PyModule_AddObject() fails'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c', line=31), value=-1)
+ str(): (int)-1 from tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31
+ Region("region-for-arg-gcc.ParmDecl('module')"):
+ repr(): Region("region-for-arg-gcc.ParmDecl('module')")
+ str(): Region("region-for-arg-gcc.ParmDecl('module')")
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c', line=29), region=Region("region-for-type-of-arg-gcc.ParmDecl('module')"))
+ RegionForGlobal(gcc.VarDecl('fooType')):
+ repr(): RegionForGlobal(gcc.VarDecl('fooType'))
+ str(): RegionForGlobal(gcc.VarDecl('fooType'))
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PyModule_AddObject/incorrect-with-cast/input.c:31
12 years, 3 months
[gcc-python-plugin] cpychecker: add __attribute__((cpychecker_negative_result_sets_exception))
by David Hugh Malcolm
commit 99d7f63b8f2d9884f059a0801dbf86ad37d49344
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Feb 27 14:39:34 2012 -0500
cpychecker: add __attribute__((cpychecker_negative_result_sets_exception))
Add a new custom attribute for marking functions that set an exception when
returning a negative value:
__attribute__((cpychecker_negative_result_sets_exception))
docs/cpychecker.rst | 33 ++++++++++-
libcpychecker/absinterp.py | 55 ++++++++++++++++++
libcpychecker/attributes.py | 16 +++++
libcpychecker/refcounts.py | 23 +++++++-
.../correct-marking/input.c | 54 +++++++++++++++++
.../correct-marking/script.py | 22 +++++++
.../correct-marking/stdout.txt | 34 +++++++++++
.../correct-usage/input.c | 61 ++++++++++++++++++++
.../correct-usage/script.py | 22 +++++++
.../correct-usage/stdout.txt | 43 ++++++++++++++
.../incorrect-marking/input.c | 57 ++++++++++++++++++
.../incorrect-marking/metadata.ini | 3 +
.../incorrect-marking/script.py | 22 +++++++
.../incorrect-marking/stderr.txt | 8 +++
.../incorrect-marking/stdout.txt | 32 ++++++++++
15 files changed, 481 insertions(+), 4 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index 4f46b22..00670b6 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -296,13 +296,42 @@ NULL for which the per-thread exception state has not been set::
input.c: In function 'test':
input.c:32:5: warning: returning (PyObject*)NULL without setting an exception
-(TODO: provide a way to mark a function as setting this state)
-
The checker does not emit the warning for cases where it is known that such
behavior is acceptable. Currently this covers functions used as `tp_iternext
<http://docs.python.org/c-api/typeobj.html#tp_iternext>`_ callbacks of a
``PyTypeObject``.
+Many functions within the CPython API return negative values to signify an
+error, setting the exception state, and the checker knows about these.
+
+If you have a function that behaves in a similar way, you can inform the
+checker of this behavior by marking it with a custom GCC attribute:
+
+.. code-block:: c
+
+ __attribute__((cpychecker_negative_result_sets_exception))
+
+which can be used to mark function declarations.
+
+.. code-block:: c
+
+ /* The checker automatically defines this preprocessor name when creating
+ the custom attribute: */
+ #if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION() \
+ __attribute__((cpychecker_negative_result_sets_exception))
+ #else
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION()
+ #endif
+
+ extern int foo(void)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION();
+
+Given the above, the checker will know that an exception is raised whenever a
+call to `foo` returns a negative value. It will also verify that `foo`
+actually behaves this way when compiling the implementation of `foo`.
+
+
Format string checking
----------------------
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 82e5a86..e086dfa 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -2300,6 +2300,36 @@ class State(object):
if isinstance(v_arg, PointerToRegion):
t_iter.dest.cpython.steal_reference(v_arg.region)
+ # cpython: handle functions that have been marked as setting the
+ # exception state when they return a negative value:
+ from libcpychecker.attributes import fnnames_setting_exception_on_negative_result
+ if fnname in fnnames_setting_exception_on_negative_result:
+
+ def handle_negative_return(t_iter):
+ check_isinstance(t_iter, Transition)
+ check_isinstance(t_iter.src, State)
+ check_isinstance(stmt, gcc.GimpleCall)
+ if stmt.lhs:
+ v_returnval = t_iter.dest.eval_rvalue(stmt.lhs, stmt.loc)
+ # This could raise a SplitValue exception:
+ # the split value affects State instances that are already
+ # within the trace, whereas we're splitting on a new value
+ # that only exists within a new State.
+ # Hence we have to do this within
+ # process_splittable_transitions
+ # so that we can split the new state:
+ eqzero = v_returnval.eval_comparison(
+ 'lt',
+ ConcreteValue.from_int(0))
+ if eqzero is True:
+ # Mark the global exception state (with an arbitrary
+ # error):
+ t_iter.dest.cpython.set_exception('PyExc_MemoryError',
+ stmt.loc)
+
+ transitions = process_splittable_transitions(transitions,
+ handle_negative_return)
+
for t_iter in transitions:
check_isinstance(t_iter, Transition)
for v_arg in args:
@@ -2777,6 +2807,31 @@ def false_edge(bb):
if e.false_value:
return e
+
+def process_splittable_transitions(transitions, callback):
+ """
+ Apply a processing function to each Transition in transitions,
+ handling the case where a SplitValue exception is raised by
+ splitting the destination states.
+
+ Return a new list of Transition instances: which will be the
+ old Transition objects, potentially with additional Transition
+ instances if any have been split
+ """
+ newtransitions = []
+ for t_iter in transitions:
+ try:
+ callback(t_iter)
+ newtransitions.append(t_iter)
+ except SplitValue:
+ err = sys.exc_info()[1]
+ splittransitions = err.split(t_iter.dest)
+ check_isinstance(splittransitions, list)
+ # Recurse:
+ newtransitions += process_splittable_transitions(splittransitions,
+ callback)
+ return newtransitions
+
class Resources:
# Resource tracking for a state
def __init__(self):
diff --git a/libcpychecker/attributes.py b/libcpychecker/attributes.py
index d234438..216601f 100644
--- a/libcpychecker/attributes.py
+++ b/libcpychecker/attributes.py
@@ -21,6 +21,7 @@ from libcpychecker.types import register_type_object
# Recorded attribute data:
fnnames_returning_borrowed_refs = set()
+fnnames_setting_exception_on_negative_result = set()
# A dictionary mapping from fnname to set of argument indices:
stolen_refs_by_fnname = {}
@@ -79,3 +80,18 @@ def register_our_attributes():
False, False, False,
attribute_callback_type_object_for_typedef)
gcc.define_macro('WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE')
+
+ # Handler for __attribute__((cpychecker_negative_result_sets_exception))
+ # and #ifdef WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE
+ def attribute_callback_for_negative_result_sets_exception(*args):
+ if 0:
+ print('attribute_callback_for_negative_result_sets_exception(%r)' % args)
+ check_isinstance(args[0], gcc.FunctionDecl)
+ fnname = args[0].name
+ fnnames_setting_exception_on_negative_result.add(fnname)
+
+ gcc.register_attribute('cpychecker_negative_result_sets_exception',
+ 0, 0,
+ False, False, False,
+ attribute_callback_for_negative_result_sets_exception)
+ gcc.define_macro('WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE')
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 1f6e08e..fa0caa7 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -27,7 +27,7 @@ from gccutils import cfg_to_dot, invoke_dot, get_src_for_loc, check_isinstance
from libcpychecker.absinterp import *
from libcpychecker.attributes import fnnames_returning_borrowed_refs, \
- stolen_refs_by_fnname
+ stolen_refs_by_fnname, fnnames_setting_exception_on_negative_result
from libcpychecker.diagnostics import Reporter, Annotator, Note
from libcpychecker.PyArg_ParseTuple import PyArgParseFmt, FormatStringWarning,\
TypeCheckCheckerType, TypeCheckResultType, \
@@ -3642,7 +3642,9 @@ def check_refcount_for_one_object(r_obj, v_ob_refcnt, v_return,
w.add_note(endstate.get_gcc_loc(fun),
'consider using "Py_RETURN_NONE;"')
-# Detect failure to set exceptions when returning NULL:
+# Detect failure to set exceptions when returning NULL,
+# and verify usage of
+# __attribute__((cpychecker_negative_result_sets_exception))
def warn_about_NULL_without_exception(v_return,
trace, endstate, fun, rep):
if not trace.err:
@@ -3666,6 +3668,23 @@ def warn_about_NULL_without_exception(v_return,
'returning (PyObject*)NULL without setting an exception')
w.add_trace(trace, ExceptionStateAnnotator())
+ # If this is function was marked with our custom
+ # __attribute__((cpychecker_negative_result_sets_exception))
+ # then verify that this is the case:
+ if fun.decl.name in fnnames_setting_exception_on_negative_result:
+ if (isinstance(v_return, ConcreteValue)
+ and v_return.value < 0):
+ if (isinstance(endstate.cpython.exception_rvalue,
+ ConcreteValue)
+ and endstate.cpython.exception_rvalue.value == 0):
+ w = rep.make_warning(fun,
+ endstate.get_gcc_loc(fun),
+ ('function is marked with __attribute__(('
+ 'cpychecker_negative_result_sets_exception))'
+ ' but can return %s without setting an exception'
+ % v_return.value))
+ w.add_trace(trace, ExceptionStateAnnotator())
+
def check_refcounts(fun, dump_traces=False, show_traces=False,
show_possible_null_derefs=False,
show_timings=False):
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c
new file mode 100644
index 0000000..9654bac
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c
@@ -0,0 +1,54 @@
+/*
+ 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>
+
+/*
+ Test of compiling a function that's been (correctly) marked as setting
+ an exception on a negative return value
+*/
+
+#if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION() \
+ __attribute__((cpychecker_negative_result_sets_exception))
+#else
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION()
+ #error (This should have been defined)
+#endif
+
+extern int test(int i)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION();
+
+extern int test(int i)
+{
+ if (i == 42) {
+ return 0; /* success */
+ } else {
+ PyErr_SetString(PyExc_RuntimeError, "an error occurred");
+ return -1; /* failure */
+ }
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/script.py b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/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/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/stdout.txt b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/stdout.txt
new file mode 100644
index 0000000..45c1acb
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/stdout.txt
@@ -0,0 +1,34 @@
+Trace 0:
+ Transitions:
+ 'when considering range: -0x80000000 <= value <= 41'
+ 'taking False path'
+ 'calling PyErr_SetString()'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c', line=44), value=-1)
+ str(): (int)-1 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c:44
+ Exception:
+ unknown struct PyObject * from /usr/include/python?.?/pyerrors.h:nn
+
+Trace 1:
+ Transitions:
+ 'when considering value == (int)42 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c:38'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c', line=41), value=0)
+ str(): (int)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c:41
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c:39
+
+Trace 2:
+ Transitions:
+ 'when considering range: 43 <= value <= 0x7fffffff'
+ 'taking False path'
+ 'calling PyErr_SetString()'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c', line=44), value=-1)
+ str(): (int)-1 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-marking/input.c:44
+ Exception:
+ unknown struct PyObject * from /usr/include/python?.?/pyerrors.h:nn
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c
new file mode 100644
index 0000000..12ea5f9
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c
@@ -0,0 +1,61 @@
+/*
+ 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>
+
+/*
+ Test of compiling a function that (correctly) uses a function that's been
+ marked as setting an exception when it returns a negative value.
+*/
+
+#if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION() \
+ __attribute__((cpychecker_negative_result_sets_exception))
+#else
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION()
+ #error (This should have been defined)
+#endif
+
+extern int foo(void)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION();
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ int i;
+
+ i = foo();
+ if (i < 0) {
+ /*
+ This shouldn't lead to a "returning NULL w/o setting exception"
+ error, given that foo() has been marked as setting an exception
+ on negative:
+ */
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/script.py b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/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/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/stdout.txt b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/stdout.txt
new file mode 100644
index 0000000..1499b52
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/stdout.txt
@@ -0,0 +1,43 @@
+Trace 0:
+ Transitions:
+ 'when considering range: -0x80000000 <= value <= -1'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c', line=50), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c:50
+ 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/attributes/sets-exception-on-nonzero-return/correct-usage/input.c', line=39), 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/attributes/sets-exception-on-nonzero-return/correct-usage/input.c', line=39), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c:43
+
+Trace 1:
+ Transitions:
+ 'when considering range: 0 <= value <= 0x7fffffff'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c', line=52), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')))
+ str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c:52
+ 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/attributes/sets-exception-on-nonzero-return/correct-usage/input.c', line=39), 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/attributes/sets-exception-on-nonzero-return/correct-usage/input.c', line=39), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/correct-usage/input.c:40
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c
new file mode 100644
index 0000000..6b819cd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c
@@ -0,0 +1,57 @@
+/*
+ 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>
+
+/*
+ Test of compiling a function that's been (correctly) marked as setting
+ an exception on a negative return value
+*/
+
+#if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE)
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION() \
+ __attribute__((cpychecker_negative_result_sets_exception))
+#else
+ #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION()
+ #error (This should have been defined)
+#endif
+
+extern int test(int i)
+ CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION();
+
+extern int test(int i)
+{
+ if (i == 42) {
+ return 0; /* success */
+ } else {
+ /*
+ Erroneously, doesn't set an exception on this path,
+ despite having been marked as such:
+ */
+ return -1; /* failure */
+ }
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/metadata.ini b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/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/attributes/sets-exception-on-nonzero-return/incorrect-marking/script.py b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/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/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/stderr.txt b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/stderr.txt
new file mode 100644
index 0000000..fe9bdb0
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/stderr.txt
@@ -0,0 +1,8 @@
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c: In function 'test':
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:49:nn: warning: function is marked with __attribute__((cpychecker_negative_result_sets_exception)) but can return -1 without setting an exception [enabled by default]
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:40:nn: note: when considering range: -0x80000000 <= value <= 41 at: if (i == 42) {
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:40:nn: note: taking False path at: if (i == 42) {
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:47:nn: note: reaching: return -1; /* failure */
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:49:nn: note: returning
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:49:nn: note: found 1 similar trace(s) to this
+tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:39:nn: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/stdout.txt b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/stdout.txt
new file mode 100644
index 0000000..3e2729e
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/stdout.txt
@@ -0,0 +1,32 @@
+Trace 0:
+ Transitions:
+ 'when considering range: -0x80000000 <= value <= 41'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c', line=47), value=-1)
+ str(): (int)-1 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:47
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:39
+
+Trace 1:
+ Transitions:
+ 'when considering value == (int)42 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:38'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c', line=41), value=0)
+ str(): (int)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:41
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:39
+
+Trace 2:
+ Transitions:
+ 'when considering range: 43 <= value <= 0x7fffffff'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='int', loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c', line=47), value=-1)
+ str(): (int)-1 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:47
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/attributes/sets-exception-on-nonzero-return/incorrect-marking/input.c:39
12 years, 3 months
[gcc-python-plugin] cpychecker: implement PySequence_SetItem
by David Hugh Malcolm
commit 2f093468d53f9a6f20242c426d8357a48cacc2d8
Author: Daniele Varrazzo <daniele.varrazzo(a)gmail.com>
Date: Mon Feb 27 11:57:18 2012 -0500
cpychecker: implement PySequence_SetItem
libcpychecker/refcounts.py | 29 ++++
.../refcounts/PySequence_SetItem/correct/input.c | 64 +++++++++
.../refcounts/PySequence_SetItem/correct/script.py | 22 +++
.../PySequence_SetItem/correct/stdout.txt | 146 ++++++++++++++++++++
.../refcounts/PySequence_SetItem/incorrect/input.c | 63 +++++++++
.../PySequence_SetItem/incorrect/metadata.ini | 3 +
.../PySequence_SetItem/incorrect/script.py | 22 +++
.../PySequence_SetItem/incorrect/stderr.txt | 17 +++
.../PySequence_SetItem/incorrect/stdout.txt | 111 +++++++++++++++
9 files changed, 477 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index c49a48e..1f6e08e 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -2723,6 +2723,35 @@ class CPython(Facet):
fnmeta,
'new ref from %s' % fnmeta.name)
+ def impl_PySequence_SetItem(self, stmt, v_o, v_i, v_item):
+ fnmeta = FnMeta(name='PySequence_SetItem',
+ prototype='int PySequence_SetItem(PyObject *o, Py_ssize_t i, PyObject *item)',
+ docurl='http://docs.python.org/c-api/sequence.html#PySequence_SetItem',
+ defined_in='Objects/abstract.c')
+ # safely handles NULL for the obj via null_error():
+ t_err = self.handle_null_error(stmt, 0, v_o, rawreturnvalue=-1)
+ if t_err:
+ return [t_err]
+
+ result = []
+
+ s_success = self.state.mkstate_concrete_return_of(stmt, 0)
+ # The function *doesn't* steal a reference to item
+
+ # For now, this only covers the "success" case. The call can fail
+ # returning -1:
+ # * raising TypeError if passed a non-sequence, or if the type's
+ # PySequenceMethods table lacks a sq_ass_item callback
+ # * if the call to sq_length fails, propagating some exception
+ # * if the call to sq_ass_item fails, propagating some exception
+ # but we don't track these possibilities yet.
+
+ result.append(Transition(self.state,
+ s_success,
+ '%s() succeeds' % fnmeta.name))
+
+ return result
+
########################################################################
# PyString_*
########################################################################
diff --git a/tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c b/tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c
new file mode 100644
index 0000000..7f19a5d
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c
@@ -0,0 +1,64 @@
+/*
+ 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>
+
+PyObject *
+handle_SetItem(PyObject *self, PyObject *args)
+{
+ PyObject *list;
+ PyObject *item;
+ int rv;
+
+ list = PyList_New(1);
+ if (!list) {
+ return NULL;
+ }
+
+ item = PyLong_FromLong(42);
+ if (!item) {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ /* Set the item in the list via PySequence_SetItem() */
+ rv = PySequence_SetItem(list, 0, item);
+ if (rv != 0) {
+ Py_DECREF(list);
+ Py_DECREF(item);
+ return NULL;
+ }
+
+ /* the function doesn't steal a ref, so leaving item here would be a leak */
+ Py_DECREF(item);
+
+ return list;
+}
+static PyMethodDef test_methods[] = {
+ {"test_method", handle_SetItem, 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/PySequence_SetItem/correct/script.py b/tests/cpychecker/refcounts/PySequence_SetItem/correct/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/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)
diff --git a/tests/cpychecker/refcounts/PySequence_SetItem/correct/stdout.txt b/tests/cpychecker/refcounts/PySequence_SetItem/correct/stdout.txt
new file mode 100644
index 0000000..f4988aa
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/correct/stdout.txt
@@ -0,0 +1,146 @@
+Trace 0:
+ Transitions:
+ 'when PyList_New() succeeds'
+ 'taking False path'
+ 'when PyLong_FromLong() succeeds'
+ 'taking False path'
+ 'PySequence_SetItem() succeeds'
+ 'taking False path'
+ 'when taking True path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29), region=RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29)))
+ str(): (struct PyObject *)&RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29)) from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29
+ r->ob_refcnt: refs: 1 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ 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/PySequence_SetItem/correct/input.c', line=23), 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/PySequence_SetItem/correct/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyLongObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:34:
+ repr(): RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=34))
+ str(): PyLongObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:34
+ r->ob_refcnt: refs: 0 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=34), region=RegionForGlobal(gcc.VarDecl('PyLong_Type')))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:24
+
+Trace 1:
+ Transitions:
+ 'when PyList_New() succeeds'
+ 'taking False path'
+ 'when PyLong_FromLong() succeeds'
+ 'taking False path'
+ 'PySequence_SetItem() succeeds'
+ 'taking False path'
+ 'when taking False path'
+ 'calling tp_dealloc on PyLongObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:34'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29), region=RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29)))
+ str(): (struct PyObject *)&RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29)) from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29
+ r->ob_refcnt: refs: 1 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ 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/PySequence_SetItem/correct/input.c', line=23), 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/PySequence_SetItem/correct/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyLongObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:34:
+ repr(): RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=34))
+ str(): PyLongObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:34
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:24
+
+Trace 2:
+ Transitions:
+ 'when PyList_New() succeeds'
+ 'taking False path'
+ 'when PyLong_FromLong() fails'
+ 'taking True path'
+ 'when taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=37), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:37
+ 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/PySequence_SetItem/correct/input.c', line=23), 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/PySequence_SetItem/correct/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29:
+ repr(): RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29))
+ str(): PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29
+ r->ob_refcnt: refs: 0 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:34
+
+Trace 3:
+ Transitions:
+ 'when PyList_New() succeeds'
+ 'taking False path'
+ 'when PyLong_FromLong() fails'
+ 'taking True path'
+ 'when taking False path'
+ 'calling tp_dealloc on PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=37), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:37
+ 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/PySequence_SetItem/correct/input.c', line=23), 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/PySequence_SetItem/correct/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29:
+ repr(): RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=29))
+ str(): PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:34
+
+Trace 4:
+ Transitions:
+ 'when PyList_New() fails'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c', line=31), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:31
+ 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/PySequence_SetItem/correct/input.c', line=23), 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/PySequence_SetItem/correct/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PySequence_SetItem/correct/input.c:29
diff --git a/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c
new file mode 100644
index 0000000..fb8ba36
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c
@@ -0,0 +1,63 @@
+/*
+ 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>
+
+PyObject *
+handle_SetItem(PyObject *self, PyObject *args)
+{
+ PyObject *list;
+ PyObject *item;
+ int rv;
+
+ list = PyList_New(1);
+ if (!list) {
+ return NULL;
+ }
+
+ item = PyLong_FromLong(42);
+ if (!item) {
+ Py_DECREF(list);
+ return NULL;
+ }
+
+ /* Set the item in the list via PySequence_SetItem() */
+ rv = PySequence_SetItem(list, 0, item);
+ if (rv != 0) {
+ Py_DECREF(list);
+ Py_DECREF(item);
+ return NULL;
+ }
+
+ /* ERROR: we are still keeping a reference on item */
+
+ return list;
+}
+static PyMethodDef test_methods[] = {
+ {"test_method", handle_SetItem, 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/PySequence_SetItem/incorrect/metadata.ini b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/metadata.ini
new file mode 100644
index 0000000..0d5711c
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/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/PySequence_SetItem/incorrect/script.py b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/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/refcounts/PySequence_SetItem/incorrect/stderr.txt b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/stderr.txt
new file mode 100644
index 0000000..bcaf1f1
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/stderr.txt
@@ -0,0 +1,17 @@
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c: In function 'handle_SetItem':
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:51:nn: warning: ob_refcnt of '*item' is 1 too high [enabled by default]
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:51:nn: note: was expecting final ob_refcnt to be N + 0 (for some unknown N)
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:51:nn: note: but final ob_refcnt is N + 1
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34:nn: note: PyLongObject allocated at: item = PyLong_FromLong(42);
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29:nn: note: when PyList_New() succeeds at: list = PyList_New(1);
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:30:nn: note: taking False path at: if (!list) {
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34:nn: note: reaching: item = PyLong_FromLong(42);
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34:nn: note: when PyLong_FromLong() succeeds at: item = PyLong_FromLong(42);
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34:nn: note: ob_refcnt is now refs: 1 + N where N >= 0
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:35:nn: note: taking False path at: if (!item) {
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:41:nn: note: reaching: rv = PySequence_SetItem(list, 0, item);
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:41:nn: note: PySequence_SetItem() succeeds at: rv = PySequence_SetItem(list, 0, item);
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:42:nn: note: taking False path at: if (rv != 0) {
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:50:nn: note: reaching: return list;
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:51:nn: note: returning
+tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:24:nn: note: graphical error report for function 'handle_SetItem' written out to 'tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c.handle_SetItem-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/stdout.txt b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/stdout.txt
new file mode 100644
index 0000000..5f20363
--- /dev/null
+++ b/tests/cpychecker/refcounts/PySequence_SetItem/incorrect/stdout.txt
@@ -0,0 +1,111 @@
+Trace 0:
+ Transitions:
+ 'when PyList_New() succeeds'
+ 'taking False path'
+ 'when PyLong_FromLong() succeeds'
+ 'taking False path'
+ 'PySequence_SetItem() succeeds'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=29), region=RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=29)))
+ str(): (struct PyObject *)&RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=29)) from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29
+ r->ob_refcnt: refs: 1 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=29), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ 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/PySequence_SetItem/incorrect/input.c', line=23), 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/PySequence_SetItem/incorrect/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyLongObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34:
+ repr(): RegionOnHeap('PyLongObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=34))
+ str(): PyLongObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34
+ r->ob_refcnt: refs: 1 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=34), region=RegionForGlobal(gcc.VarDecl('PyLong_Type')))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:24
+
+Trace 1:
+ Transitions:
+ 'when PyList_New() succeeds'
+ 'taking False path'
+ 'when PyLong_FromLong() fails'
+ 'taking True path'
+ 'when taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=37), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:37
+ 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/PySequence_SetItem/incorrect/input.c', line=23), 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/PySequence_SetItem/incorrect/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29:
+ repr(): RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=29))
+ str(): PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29
+ r->ob_refcnt: refs: 0 + N where N >= 0
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=29), region=RegionForGlobal(gcc.VarDecl('PyList_Type')))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34
+
+Trace 2:
+ Transitions:
+ 'when PyList_New() succeeds'
+ 'taking False path'
+ 'when PyLong_FromLong() fails'
+ 'taking True path'
+ 'when taking False path'
+ 'calling tp_dealloc on PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=37), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:37
+ 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/PySequence_SetItem/incorrect/input.c', line=23), 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/PySequence_SetItem/incorrect/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29:
+ repr(): RegionOnHeap('PyListObject', gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=29))
+ str(): PyListObject allocated at tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29
+ r->ob_refcnt: None
+ r->ob_type: None
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:34
+
+Trace 3:
+ Transitions:
+ 'when PyList_New() fails'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c', line=31), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:31
+ 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/PySequence_SetItem/incorrect/input.c', line=23), 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/PySequence_SetItem/incorrect/input.c', line=23), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from tests/cpychecker/refcounts/PySequence_SetItem/incorrect/input.c:29
12 years, 3 months
[gcc-python-plugin] cpychecker: generalize null_error() for callsites that return (int)-1
by David Hugh Malcolm
commit 99f444376dde6ff4dc8a54ead335c4980729cdff
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Feb 27 11:53:29 2012 -0500
cpychecker: generalize null_error() for callsites that return (int)-1
libcpychecker/refcounts.py | 10 ++++++++--
1 files changed, 8 insertions(+), 2 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 0cdcdc4..c49a48e 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -687,7 +687,7 @@ class CPython(Facet):
'ob_refcnt')
yield (r_obj, v_ob_refcnt)
- def handle_null_error(self, stmt, idx, ptr):
+ def handle_null_error(self, stmt, idx, ptr, rawreturnvalue=0):
# Handle Objects/abstract.c's null_error()
# idx is the 0-based index of the argument
check_isinstance(stmt, gcc.Gimple)
@@ -697,7 +697,13 @@ class CPython(Facet):
self.state.raise_split_value(ptr, stmt.loc)
if ptr.is_null_ptr():
if stmt.lhs:
- value = ConcreteValue(stmt.lhs.type, stmt.loc, 0)
+ # null_error() returns a NULL PyObject*
+ # some callsites where the fn returns a PyObject* have:
+ # return null_error()
+ # but others where the fn returns an int have:
+ # null_error()
+ # return -1
+ value = ConcreteValue(stmt.lhs.type, stmt.loc, rawreturnvalue)
else:
value = None
t_failure = self.state.mktrans_assignment(stmt.lhs,
12 years, 3 months
[gcc-python-plugin] cpychecker: remove bogus text from docstring
by David Hugh Malcolm
commit 69d1ea676435b02ea1d1d6aaa4a84c6d3a23d768
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Feb 24 13:44:22 2012 -0500
cpychecker: remove bogus text from docstring
libcpychecker/refcounts.py | 2 --
1 files changed, 0 insertions(+), 2 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index be0e466..0cdcdc4 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -644,8 +644,6 @@ class CPython(Facet):
function that either:
- returns either a borrowed ref, or
- fails with NULL and sets an exception
- Optionally, a name for the new object can be supplied; otherwise
- a sane default will be used.
"""
check_isinstance(fnmeta, FnMeta)
s_success = self.mkstate_borrowed_ref(stmt, fnmeta)
12 years, 3 months
[gcc-python-plugin] cpychecker: implement PyWeakref_GetObject
by David Hugh Malcolm
commit 1a2b735554c9c3cc4d416f827512309a54709b1e
Author: Daniele Varrazzo <daniele.varrazzo(a)gmail.com>
Date: Fri Feb 24 13:41:23 2012 -0500
cpychecker: implement PyWeakref_GetObject
libcpychecker/refcounts.py | 20 ++++++++
.../refcounts/PyWeakref_GetObject/NULL/input.c | 38 +++++++++++++++
.../refcounts/PyWeakref_GetObject/NULL/script.py | 22 +++++++++
.../refcounts/PyWeakref_GetObject/NULL/stdout.txt | 9 ++++
.../refcounts/PyWeakref_GetObject/correct/input.c | 50 ++++++++++++++++++++
.../PyWeakref_GetObject/correct/script.py | 22 +++++++++
.../PyWeakref_GetObject/correct/stdout.txt | 44 +++++++++++++++++
.../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 | 44 +++++++++++++++++
12 files changed, 332 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 967417d..be0e466 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -3263,6 +3263,26 @@ 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')
+ if isinstance(v_op, UnknownValue):
+ self.state.raise_split_value(v_op, stmt.loc)
+ if v_op.is_null_ptr():
+ s_failure = self.state.mkstate_concrete_return_of(stmt, 0)
+ s_failure.cpython.bad_internal_call(stmt.loc)
+ return [Transition(self.state,
+ s_failure,
+ '%s() fails due to NULL argument' % fnmeta.name)]
+ 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/NULL/input.c b/tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/input.c
new file mode 100644
index 0000000..040038d
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/input.c
@@ -0,0 +1,38 @@
+/*
+ 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>
+
+/*
+ Test of handling of in call to PyWeakref_GetObject(NULL)
+*/
+
+PyObject *
+test(void)
+{
+ return PyWeakref_GetObject(NULL);
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/script.py b/tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/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/PyWeakref_GetObject/NULL/stdout.txt b/tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/stdout.txt
new file mode 100644
index 0000000..ecdbc03
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/stdout.txt
@@ -0,0 +1,9 @@
+Trace 0:
+ Transitions:
+ 'PyWeakref_GetObject() fails due to NULL argument'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/input.c', line=29), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/input.c:29
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_SystemError')) from tests/cpychecker/refcounts/PyWeakref_GetObject/NULL/input.c:29
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(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>
+
+/*
+ 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(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/PyWeakref_GetObject/correct/stdout.txt b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/stdout.txt
new file mode 100644
index 0000000..95eae44
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/correct/stdout.txt
@@ -0,0 +1,44 @@
+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'
+ 'PyWeakref_GetObject() fails due to NULL argument'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c', line=41), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:41
+ 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 *)&RegionForGlobal(gcc.VarDecl('PyExc_SystemError')) from tests/cpychecker/refcounts/PyWeakref_GetObject/correct/input.c:32
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(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>
+
+/*
+ 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(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/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..abc1b4e
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/stdout.txt
@@ -0,0 +1,44 @@
+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'
+ 'PyWeakref_GetObject() fails due to NULL argument'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c', line=37), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:37
+ 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 *)&RegionForGlobal(gcc.VarDecl('PyExc_SystemError')) from tests/cpychecker/refcounts/PyWeakref_GetObject/incorrect/input.c:32
12 years, 3 months
[gcc-python-plugin] misc/fedora: continued work on analyzing mass run on Fedora 17's src.rpms
by David Hugh Malcolm
commit 771455b3128b1323e80bdda53939d8d140a84c68
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Feb 17 14:41:14 2012 -0500
misc/fedora: continued work on analyzing mass run on Fedora 17's src.rpms
misc/fedora/bugreporting.py | 12 +++++
misc/fedora/bugreports.txt | 110 +++++++++++++++++++++++++++++++++++++++++++
misc/fedora/makeindex.py | 39 ++++++++++++----
misc/fedora/mass-rebuild.py | 2 +-
4 files changed, 153 insertions(+), 10 deletions(-)
---
diff --git a/misc/fedora/bugreporting.py b/misc/fedora/bugreporting.py
index d27ed15..d977a22 100644
--- a/misc/fedora/bugreporting.py
+++ b/misc/fedora/bugreporting.py
@@ -17,6 +17,7 @@
from collections import OrderedDict, namedtuple
import glob
+import os
import re
import urllib
@@ -45,6 +46,12 @@ class NewBug:
class Srpm(namedtuple('Srpm',
('name', 'version', 'release'))):
+ @classmethod
+ def from_path(cls, path):
+ m = re.match('(\S+)-(\S+)-(\S+)', os.path.basename(path))
+ n, v, r = m.groups()
+ return cls(n, v, r)
+
def __str__(self):
return '%s-%s-%s' % (self.name, self.version, self.release)
@@ -129,6 +136,11 @@ class BugReportDb:
print('out of %i total src.rpms (that link against libpython2.7)'
% num_src_rpms)
+ @classmethod
+ def add_status(cls, srpm, notes):
+ with open('bugreports.txt', 'a') as f:
+ f.write('%s %s\n' % (srpm, notes))
+
def main():
# Test code: open the user's webbrowser to a page with various fields
# prepopulated, for manual review.
diff --git a/misc/fedora/bugreports.txt b/misc/fedora/bugreports.txt
index 7e904e5..c18dd2c 100644
--- a/misc/fedora/bugreports.txt
+++ b/misc/fedora/bugreports.txt
@@ -94,6 +94,76 @@ ghmm-0.7-7.svn2286.fc17 TODO: isn't being built with the correct bu
gnumeric-1.11.1-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
graphviz-2.28.0-14.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
ice-3.4.2-8.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+kig-4.8.0-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+kross-interpreters-4.8.0-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+libldb-1.1.4-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+libreoffice-3.5.0.3-5.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+libselinux-2.1.9-7.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+libssh2-python-0.7.1-4.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+libtevent-0.9.15-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+listen-0.6.5-7.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+marble-4.8.0-6.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+mercurial-2.1-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+policycoreutils-2.1.10-21.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+py-bcrypt-0.2-6.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+py-radix-0.5-9.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pyaudio-0.2.3-5.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pycdio-0.17-3.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pyicu-1.2-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pykde4-4.8.0-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pyliblzma-0.5.3-6.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pyodbc-2.1.5-6.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pyparted-3.8-3.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pypop-0.7.0-8.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pystatgrab-0.5-9.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-PSI-0.3-0.4.b2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-arm4-1.2-5.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-basemap-0.99.4-13.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-coverage-3.5.1-0.1.b1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-cryptsetup-0.1.4-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-ctags-1.0.5-4.fc15 TODO: isn't being built with the correct build flags, so plugin is not run
+python-cvxopt-1.1.4-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-dmidecode-3.10.13-3.fc15 TODO: isn't being built with the correct build flags, so plugin is not run
+python-ethtool-0.7-2.fc16 TODO: isn't being built with the correct build flags, so plugin is not run
+python-genshi-0.6-4.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-guppy-0.1.9-5.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-igraph-0.5.4-4.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-imdb-4.7-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-iptables-0.1.0-3.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-iwlib-1.1-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-kerberos-1.1-8.1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-kinterbasdb-3.3.0-4.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-matplotlib-1.0.1-18.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-mecab-0.991-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-msgpack-0.1.12-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-netifaces-0.5-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-numeric-24.2-17.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-numexpr-2.0.1-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-poppler-qt4-0.16.2-3.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-psycopg2-2.4.4-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-ptrace-0.6.3-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-pywt-0.2.0-3.fc15 TODO: isn't being built with the correct build flags, so plugin is not run
+python-schedutils-0.2-8.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-shapely-1.2.13-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-simplejson-2.3.0-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-simpleparse-2.1.1-4.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-storm-0.19-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-sybase-0.39-8.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-sysv_ipc-0.4.2-6.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-tables-2.3.1-4.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+python-twisted-runner-11.1.0-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+pywcs-1.11-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+qmtest-2.4.1-6.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+quodlibet-2.3.92-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+rdiff-backup-1.2.8-7.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+setools-3.3.7-21.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+superkaramba-4.8.0-1.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+texworks-0.4.3-4.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+wxPython-2.8.12.0-2.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+yum-metadata-parser-1.1.4-6.fc17 TODO: isn't being built with the correct build flags, so plugin is not run
+
+
+
# FIXME: checker fails completely on C++, with errors of the form:
# Traceback (most recent call last):
@@ -126,6 +196,45 @@ gnome-commander-1.2.8.15-2.fc16 FIXME: C++
groonga-1.3.0-2.fc17 FIXME: C++
hugin-2011.4.0-3.fc17 FIXME: C++
k3d-0.8.0.2-6.fc17 FIXME: C++
+kvirc-4.0.4-3.fc17 FIXME: C++
+lcms-1.19-5.fc17 FIXME: C++
+libdigidocpp-0.3.0-13.fc17 FIXME: C++
+libimobiledevice-1.1.1-5.fc17 FIXME: C++
+libprelude-1.0.0-11.fc17 FIXME: C++
+mapserver-6.0.1-5.fc17 FIXME: C++
+matahari-0.6.0-3.fc17 FIXME: C++
+natus-0.1.5-2.fc15 FIXME: C++
+openbabel-2.3.1-1.fc17 FIXME: C++
+opencv-2.3.1-7.fc17 FIXME: C++
+paraview-3.12.0-7.fc17 FIXME: C++
+pcapy-0.10.5-10.fc17 FIXME: C++
+picard-0.16-2.fc17 FIXME: C++
+picviz-0.6-7.fc17 FIXME: C++
+plplot-5.9.9-5.svn12161.fc17 FIXME: C++
+portmidi-217-5.fc17 FIXME: C++
+pybox2d-2.0.2b2-7.fc17 FIXME: C++
+pycryptopp-0.5.29-3.fc17 FIXME: C++
+pymilia-0.3.0-11.fc17 FIXME: C++
+pymol-1.4.1-7.20110502svn3947.fc17 FIXME: C++
+python-pyside-1.1.0-1.fc17 FIXME: C++
+python-tag-0.94.8-3.fc17 FIXME: C++
+qgis-1.7.3-1.fc17 FIXME: C++
+rekall-2.4.6-18.fc17 FIXME: C++
+rmol-0.25.3-3.fc17 FIXME: C++
+root-5.32.00-1.fc17 FIXME: C++
+scim-python-0.1.13rc1-10.fc17 FIXME: C++
+scribus-1.4.0-3.fc17 FIXME: C++
+semantik-0.7.3-3.fc17 FIXME: C++
+sevmgr-0.2.0-1.fc17 FIXME: C++
+shiboken-1.1.0-1.fc17 FIXME: C++
+simfqt-0.1.3-2.fc17 FIXME: C++
+stdair-0.45.1-1.fc17 FIXME: C++
+trademgen-0.2.2-2.fc17 FIXME: C++
+travelccm-0.5.3-2.fc17 FIXME: C++
+vigra-1.8.0-2.fc17 FIXME: C++
+vtk-5.8.0-3.fc17 FIXME: C++
+xmms2-0.8-3.fc17 FIXME: C++
+zorba-2.1.0-4.fc17 FIXME: C++
# Other C++ issues:
bro-1.5.1-4.fc17 FIXME: configure: error: C++ compiler cannot create executables
@@ -203,3 +312,4 @@ iscsi-initiator-utils-6.2.0.872-18.fc17 TODO
ibus-anthy-1.2.6-5.fc17 TODO: SWIG-built
input-pad-1.0.1-3.fc17 TODO: SWIG-built
+ldns-1.6.12-1.fc17 FIXME: many errors
diff --git a/misc/fedora/makeindex.py b/misc/fedora/makeindex.py
index 1a4c1bd..06c71df 100644
--- a/misc/fedora/makeindex.py
+++ b/misc/fedora/makeindex.py
@@ -24,6 +24,8 @@ import re
from BeautifulSoup import BeautifulSoup
+from bugreporting import BugReportDb, Srpm
+
class ErrorReport(namedtuple('ErrorReport',
('htmlpath', 'htmlid', 'filename', 'function', 'errmsg', 'htmlpre'))):
def href(self):
@@ -31,15 +33,17 @@ class ErrorReport(namedtuple('ErrorReport',
def contains_failure(self):
# Does this appear to be within an error-handling path?
- print('htmlpre: %s' % self.htmlpre)
+ if 0: #self.function == 'clawsmail_compose_new':
+ if self.errmsg == 'calling PyObject_Call with NULL as argument 1 (class) at composewindowtype.c:523':
+ print('htmlpre: %s' % self.htmlpre)
m = re.match('.*when (\S+) fails.*',
str(self.htmlpre),
re.MULTILINE|re.DOTALL)
if m:
- print m.groups()
+ #print m.groups()
return True
else:
- print 'not matched'
+ #print 'not matched'
return False
def is_within_initialization(self):
@@ -98,7 +102,7 @@ def get_errors_from_file(htmlpath):
table = div.table
if not table:
continue
- print div['id']
+ #print div['id']
#print 'div: %r' % div
rows = table('tr')
def get_second_col(row):
@@ -237,6 +241,7 @@ class BuildLog:
def __init__(self, path):
self.seen_plugin = False
self.unimplemented_functions = set()
+ self.cplusplus_failure = False
buildlog = os.path.join(path, 'build.log')
with open(buildlog) as f:
@@ -250,6 +255,9 @@ class BuildLog:
if m:
self.unimplemented_functions.add(m.group(1))
+ if "AttributeError: 'NoneType' object has no attribute 'vars'" in line:
+ self.cplusplus_failure = True
+
def generate_index_html(path, title):
outpath = os.path.join(path, 'index.html')
with open(outpath, 'w') as f:
@@ -265,25 +273,34 @@ def generate_index_html(path, title):
f.write(' <p>The GCC arguments for invoking the plugin were\n'
' not seen in the build logs: did the plugin actually\n'
' get run?</p>')
+ srpm = Srpm.from_path(path)
+ BugReportDb.add_status(srpm,
+ "TODO: isn't being built with the correct build flags, so plugin is not run")
+
+ if buildlog.cplusplus_failure:
+ f.write(' <p>C++ failure</p>\n')
+ srpm = Srpm.from_path(path)
+ BugReportDb.add_status(srpm, "FIXME: C++")
# Gather the ErrorReport by severity
triager = Triager()
# mapping from Severity to list of ErrorReport
severities = {}
+ num_reports = 0
for dirpath, dirnames, filenames in os.walk(path):
#print dirpath, dirnames, filenames
for filename in filenames:
if filename.endswith('-refcount-errors.html'):
- print ' ', os.path.join(dirpath, filename)
+ #print ' ', os.path.join(dirpath, filename)
htmlpath = os.path.join(dirpath, filename)
for er in get_errors_from_file(htmlpath):
- print(er.filename)
- print(er.function)
- print(er.errmsg)
+ #print(er.filename)
+ #print(er.function)
+ #print(er.errmsg)
sev = triager.classify(er)
- print(sev)
+ #print(sev)
if sev in severities:
severities[sev].append(er)
else:
@@ -294,6 +311,7 @@ def generate_index_html(path, title):
f.write(' %s\n' % sev.description)
f.write(' <table>\n')
for er in severities[sev]:
+ num_reports += 1
href = os.path.relpath(er.href(), path)
f.write(' <tr> <td><a href=%s>%s</a></td> <td><a href=%s>%s</a></td> <td><a href=%s>%s</a></td> </tr>'
% (href, er.filename,
@@ -310,6 +328,9 @@ def generate_index_html(path, title):
f.write(' <li><pre>%s</pre></li>\n' % fnname)
f.write(' </ul>\n')
+ if num_reports == 0 and not buildlog.unimplemented_functions:
+ f.write(' <p>Nothing was reported; did the plugin run correctly?\n')
+
f.write(' </body>\n')
f.write('</html>\n')
diff --git a/misc/fedora/mass-rebuild.py b/misc/fedora/mass-rebuild.py
index 4625240..b435aa3 100644
--- a/misc/fedora/mass-rebuild.py
+++ b/misc/fedora/mass-rebuild.py
@@ -224,12 +224,12 @@ gcc-python-plugin post-0.9 git %(gitversion)s running the checker in an *f16* ch
# Rebuild all src.rpm files found in "SRPMS" as necessary:
if 1:
for srpmpath in sorted(glob.glob('SRPMS/*.src.rpm')):
-
srpmname, version, release = nvr_from_srpm_path(srpmpath)
bugdb = BugReportDb()
# print(bugdb.bugs)
bugdb.print_summary()
+ print('Processing %s' % srpmname)
statuses = bugdb.find(srpmname)
if statuses:
for status in statuses:
12 years, 3 months
[gcc-python-plugin] misc/fedora: scrape build logs; warning about the plugin not being run, or about unimplemented funct
by David Hugh Malcolm
commit d08a4358c622ea2bea8c04b467576855474d1ea1
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Feb 17 14:00:25 2012 -0500
misc/fedora: scrape build logs; warning about the plugin not being run, or about unimplemented functions
misc/fedora/makeindex.py | 37 +++++++++++++++++++++++++++++++++++--
misc/fedora/mass-rebuild.py | 4 ++--
2 files changed, 37 insertions(+), 4 deletions(-)
---
diff --git a/misc/fedora/makeindex.py b/misc/fedora/makeindex.py
index 7ce8293..1a4c1bd 100644
--- a/misc/fedora/makeindex.py
+++ b/misc/fedora/makeindex.py
@@ -232,7 +232,25 @@ about internal API calls that can lead to an exception being set''')
<p>The triager didn't know how to classify these ones</p>
''')
-def gather_html_reports(path, title):
+class BuildLog:
+ # Wrapper around a "build.log" scraped from the mock build
+ def __init__(self, path):
+ self.seen_plugin = False
+ self.unimplemented_functions = set()
+
+ buildlog = os.path.join(path, 'build.log')
+ with open(buildlog) as f:
+ for line in f.readlines():
+ if 0:
+ print repr(line)
+ if '-fplugin=python2 -fplugin-arg-python2-script=/test.py' in line:
+ self.seen_plugin = True
+ m = re.match('NotImplementedError: not yet implemented: (\S+)',
+ line)
+ if m:
+ self.unimplemented_functions.add(m.group(1))
+
+def generate_index_html(path, title):
outpath = os.path.join(path, 'index.html')
with open(outpath, 'w') as f:
f.write('<html><head><title>%s</title></head>\n' % title)
@@ -242,6 +260,12 @@ def gather_html_reports(path, title):
f.write(" <p>This is a summary of errors seen when compiling with <a href='https://fedorahosted.org/gcc-python-plugin/'>an experimental static analysis tool</a></p>")
f.write(' <p>Raw build logs can be seen <a href="build.log">here</a></p>\n')
+ buildlog = BuildLog(path)
+ if not buildlog.seen_plugin:
+ f.write(' <p>The GCC arguments for invoking the plugin were\n'
+ ' not seen in the build logs: did the plugin actually\n'
+ ' get run?</p>')
+
# Gather the ErrorReport by severity
triager = Triager()
@@ -277,6 +301,15 @@ def gather_html_reports(path, title):
href, er.errmsg))
f.write(' </table>\n')
+ if buildlog.unimplemented_functions:
+ f.write(' <h2>Implementation notes for gcc-with-cpychecker</h2>\n')
+ f.write(' <p>The following "Py" functions were used but aren\'t\n'
+ ' yet explicitly handled by gcc-with-cpychecker</p>\n'
+ ' <ul>\n')
+ for fnname in sorted(buildlog.unimplemented_functions):
+ f.write(' <li><pre>%s</pre></li>\n' % fnname)
+ f.write(' </ul>\n')
+
f.write(' </body>\n')
f.write('</html>\n')
@@ -286,7 +319,7 @@ def main():
for resultdir in os.listdir('LOGS'):
resultpath = os.path.join('LOGS', resultdir)
print(resultpath)
- gather_html_reports(resultpath, 'Errors seen in %s' % resultdir)
+ generate_index_html(resultpath, 'Errors seen in %s' % resultdir)
if __name__ == '__main__':
main()
diff --git a/misc/fedora/mass-rebuild.py b/misc/fedora/mass-rebuild.py
index 22415e4..4625240 100644
--- a/misc/fedora/mass-rebuild.py
+++ b/misc/fedora/mass-rebuild.py
@@ -25,7 +25,7 @@ import sys
import webbrowser
from bugreporting import NewBug, BugReportDb
-from makeindex import gather_html_reports
+from makeindex import generate_index_html
def nvr_from_srpm_path(path):
filename = os.path.basename(path)
@@ -239,7 +239,7 @@ if 1:
resultdir = get_result_dir(srpmpath)
if not os.path.exists(resultdir):
local_rebuild_of_srpm_in_mock(srpmpath, MOCK_CONFIG)
- gather_html_reports(resultdir, 'Errors seen in %s' % resultdir)
+ generate_index_html(resultdir, 'Errors seen in %s' % resultdir)
prepare_bug_report(srpmpath)
break
12 years, 3 months
[gcc-python-plugin] misc/fedora: print a summary of status as we run
by David Hugh Malcolm
commit 1e1724c1b5c788fb346442ba619a55620bc8d35d
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Feb 17 13:49:41 2012 -0500
misc/fedora: print a summary of status as we run
misc/fedora/bugreporting.py | 29 +++++++++++++++++++++++++++++
misc/fedora/mass-rebuild.py | 1 +
2 files changed, 30 insertions(+), 0 deletions(-)
---
diff --git a/misc/fedora/bugreporting.py b/misc/fedora/bugreporting.py
index 273479d..d27ed15 100644
--- a/misc/fedora/bugreporting.py
+++ b/misc/fedora/bugreporting.py
@@ -16,6 +16,7 @@
# <http://www.gnu.org/licenses/>.
from collections import OrderedDict, namedtuple
+import glob
import re
import urllib
@@ -100,6 +101,34 @@ class BugReportDb:
result.append(status)
return result
+ def summary(self):
+ num_bugs = 0
+ fixmes = 0
+ others = 0
+ for status in self.statuses:
+ if isinstance(status, BugReport):
+ num_bugs += 1
+ elif isinstance(status, Unreported):
+ if 'FIXME' in status.notes or 'TODO' in status.notes:
+ fixmes += 1
+ else:
+ others += 1
+ num_src_rpms = len(glob.glob('SRPMS/*.src.rpm'))
+ return num_bugs, fixmes, others, num_src_rpms
+
+ def print_summary(self):
+ num_bugs, fixmes, others, num_src_rpms = self.summary()
+ def print_amount(desc, count):
+ print('* %i %s (%i%%)'
+ % (count, desc, count * 100 / num_src_rpms))
+ print_amount('bugs filed for src.rpms, where the checker found genuine problems', num_bugs)
+ print_amount('src.rpms not requiring a bug to be filed', others)
+ print_amount('src.rpms requiring followup work', fixmes)
+ print_amount('src.rpms not yet processed',
+ num_src_rpms - (num_bugs + fixmes +others))
+ print('out of %i total src.rpms (that link against libpython2.7)'
+ % num_src_rpms)
+
def main():
# Test code: open the user's webbrowser to a page with various fields
# prepopulated, for manual review.
diff --git a/misc/fedora/mass-rebuild.py b/misc/fedora/mass-rebuild.py
index ba82743..22415e4 100644
--- a/misc/fedora/mass-rebuild.py
+++ b/misc/fedora/mass-rebuild.py
@@ -229,6 +229,7 @@ if 1:
bugdb = BugReportDb()
# print(bugdb.bugs)
+ bugdb.print_summary()
statuses = bugdb.find(srpmname)
if statuses:
for status in statuses:
12 years, 3 months