[prev in list] [next in list] [prev in thread] [next in thread]
List: gcc-python-plugin-commits
Subject: [gcc-python-plugin] cpychecker: add cpychecker_steals_reference_to_arg(n) attribute
From: dmalcolm () fedoraproject ! org (dmalcolm)
Date: 2011-10-27 0:29:01
Message-ID: 20111027002902.AF68B120434 () lists ! fedorahosted ! org
[Download RAW message or body]
commit e95fb39f025e5bfdb68c45822980a373a09d2859
Author: David Malcolm <dmalcolm at redhat.com>
Date: Wed Oct 26 20:28:26 2011 -0400
cpychecker: add cpychecker_steals_reference_to_arg(n) attribute
docs/cpychecker.rst | 66 +++++++++++++++++-
libcpychecker/absinterp.py | 45 +++++++++++-
libcpychecker/attributes.py | 38 +++++++++--
libcpychecker/refcounts.py | 14 ++++-
.../correct-marking/input.c | 60 ++++++++++++++++
.../correct-marking/script.py | 22 ++++++
.../correct-marking/stdout.txt | 20 ++++++
.../steals-reference-to-arg/correct-usage/input.c | 68 ++++++++++++++++++
.../correct-usage/script.py | 22 ++++++
.../correct-usage/stdout.txt | 48 +++++++++++++
.../incorrect-marking/input.c | 55 +++++++++++++++
.../incorrect-marking/script.py | 22 ++++++
.../incorrect-marking/stderr.txt | 10 +++
.../incorrect-marking/stdout.txt | 20 ++++++
.../incorrect-usage/input.c | 72 ++++++++++++++++++++
.../incorrect-usage/script.py | 22 ++++++
.../incorrect-usage/stderr.txt | 13 ++++
.../incorrect-usage/stdout.txt | 43 ++++++++++++
18 files changed, 645 insertions(+), 15 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index a520ae9..5ced382 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -151,7 +151,19 @@ function when the function terminates.
It will assume this behavior for any function (or call through a function
pointer) that returns a PyObject*.
-It is possible to override this behavior using custom compiler attributes:
+It is possible to override this behavior using custom compiler attributes as
+follows:
+
+Marking functions that return borrowed references
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The checker provides a custom GCC attribute:
+
+.. code-block:: c
+
+ __attribute__((cpychecker_returns_borrowed_ref))
+
+which can be used to mark function declarations:
.. code-block:: c
@@ -168,9 +180,55 @@ It is possible to override this behavior using custom compiler \
attributes: CPYCHECKER_RETURNS_BORROWED_REF;
Given the above, the checker will assume that invocations of ``foo()`` are
-returning a borrowed reference (or NULL), rather than a new reference, and
-will apply the same policy when verifying the implementation of ``foo()``
-itself.
+returning a borrowed reference (or NULL), rather than a new reference. It
+will also check that this is that case when verifying the implementation of
+``foo()`` itself.
+
+Marking functions that steal references to their arguments
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The checker provides a custom GCC attribute:
+
+.. code-block:: c
+
+ __attribute__((cpychecker_steals_reference_to_arg(n)))
+
+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_STEALS_REFERENCE_TO_ARG_ATTRIBUTE)
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) \
+ __attribute__((cpychecker_steals_reference_to_arg(n)))
+ #else
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n)
+ #endif
+
+ extern void foo(PyObject *obj)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(1);
+
+Given the above, the checker will assume that invocations of ``foo()`` steal
+a reference to the first argument (``obj``). It will also verify that this is
+the case when analyzing the implementation of ``foo()`` itself.
+
+More then one argument can be marked:
+
+.. code-block:: c
+
+ extern void bar(int i, PyObject *obj, int j, PyObject *other)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(2)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(4);
+
+The argument indices are 1-based (the above example is thus referring to
+``obj`` and to ``other``).
+
+All such arguments to the attribute should be ``PyObject*`` (or a pointer to a
+derived structure type).
+
+It is assumed that such references are stolen for all possible outcomes of the
+function - if a function can either succeed or fail, the reference is stolen in
+both possible worlds.
Error-handling checking
-----------------------
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 0117bc5..248102c 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -1865,10 +1865,14 @@ class State(object):
if fnname in fnnames_returning_borrowed_refs:
# The function being called was marked as returning a
# borrowed ref, rather than a new ref:
- return \
self.cpython.make_transitions_for_borrowed_ref_or_fail(stmt,
- 'borrowed ref from %s()' % \
fnname)
- return self.cpython.make_transitions_for_new_ref_or_fail(stmt,
- 'new ref from \
(unknown) %s' % fnname) + return self.apply_fncall_side_effects(
+ self.cpython.make_transitions_for_borrowed_ref_or_fail(stmt,
+ \
'borrowed ref from %s()' % fnname), + stmt)
+ return self.apply_fncall_side_effects(
+ self.cpython.make_transitions_for_new_ref_or_fail(stmt,
+ 'new ref from \
(unknown) %s' % fnname), + stmt)
# Unknown function of other type:
log('Invocation of unknown function: %r', fnname)
@@ -1882,6 +1886,25 @@ class State(object):
for i, arg in enumerate(stmt.args):
log('args[%i]: %s %r', i, arg, arg)
+ def get_function_name(self, stmt):
+ """
+ Try to get the function name for a gcc.GimpleCall statement as a
+ string, or None if we're unable to determine it.
+
+ For a simple function invocation this is easy, but if we're
+ calling through a function pointer we may or may not know.
+ """
+ check_isinstance(stmt, gcc.GimpleCall)
+
+ v_fn = self.eval_rvalue(stmt.fn, stmt.loc)
+ if isinstance(v_fn, PointerToRegion):
+ if isinstance(v_fn.region, RegionForGlobal):
+ if isinstance(v_fn.region.vardecl, gcc.FunctionDecl):
+ return v_fn.region.vardecl.name
+
+ # Unable to determine it:
+ return None
+
def apply_fncall_side_effects(self, transitions, stmt):
"""
Given a list of Transition instances for a call to a function with
@@ -1895,6 +1918,20 @@ class State(object):
check_isinstance(stmt, gcc.GimpleCall)
args = self.eval_stmt_args(stmt)
+
+ fnname = self.get_function_name(stmt)
+
+ # cpython: handle functions marked as stealing references to their
+ # arguments:
+ from libcpychecker.attributes import stolen_refs_by_fnname
+ if fnname in stolen_refs_by_fnname:
+ for t_iter in transitions:
+ check_isinstance(t_iter, Transition)
+ 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)
+
for t_iter in transitions:
check_isinstance(t_iter, Transition)
for v_arg in args:
diff --git a/libcpychecker/attributes.py b/libcpychecker/attributes.py
index 5207c5b..b87cc8d 100644
--- a/libcpychecker/attributes.py
+++ b/libcpychecker/attributes.py
@@ -21,18 +21,44 @@ from gccutils import check_isinstance
# Recorded attribute data:
fnnames_returning_borrowed_refs = set()
-def attribute_callback_for_returns_borrowed_ref(*args):
- if 0:
- print('attribute_callback_for_returns_borrowed_ref(%r)' % args)
- check_isinstance(args[0], gcc.FunctionDecl)
- fnname = args[0].name
- fnnames_returning_borrowed_refs.add(fnname)
+# A dictionary mapping from fnname to set of argument indices:
+stolen_refs_by_fnname = {}
def register_our_attributes():
# Callback, called by the gcc.PLUGIN_ATTRIBUTES event
+
+ # Handler for __attribute__((cpychecker_returns_borrowed_ref))
+ # and #ifdef WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE
+ def attribute_callback_for_returns_borrowed_ref(*args):
+ if 0:
+ print('attribute_callback_for_returns_borrowed_ref(%r)' % args)
+ check_isinstance(args[0], gcc.FunctionDecl)
+ fnname = args[0].name
+ fnnames_returning_borrowed_refs.add(fnname)
+
gcc.register_attribute('cpychecker_returns_borrowed_ref',
0, 0,
False, False, False,
attribute_callback_for_returns_borrowed_ref)
gcc.define_macro('WITH_CPYCHECKER_RETURNS_BORROWED_REF_ATTRIBUTE')
+ # Handler for __attribute__((cpychecker_steals_reference_to_arg(n)))
+ # and #ifdef WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE
+ def attribute_callback_for_steals_reference_to_arg(*args):
+ if 0:
+ print('attribute_callback_for_steals_reference_to_arg(%r)' % (args, ))
+ check_isinstance(args[0], gcc.FunctionDecl)
+ check_isinstance(args[1], gcc.IntegerCst)
+ fnname = args[0].name
+ argindex = int(args[1].constant)
+
+ if fnname in stolen_refs_by_fnname:
+ stolen_refs_by_fnname[fnname].add(argindex)
+ else:
+ stolen_refs_by_fnname[fnname] = set([argindex])
+
+ gcc.register_attribute('cpychecker_steals_reference_to_arg',
+ 1, 1,
+ False, False, False,
+ attribute_callback_for_steals_reference_to_arg)
+ gcc.define_macro('WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE')
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 3bfa382..57ecd31 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -26,7 +26,8 @@ import gcc
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
+from libcpychecker.attributes import fnnames_returning_borrowed_refs, \
+ stolen_refs_by_fnname
from libcpychecker.diagnostics import Reporter, Annotator, Note
from libcpychecker.PyArg_ParseTuple import PyArgParseFmt, FormatStringError, \
TypeCheckCheckerType, TypeCheckResultType
@@ -2271,6 +2272,17 @@ def check_refcounts(fun, dump_traces=False, show_traces=False,
exp_refcnt = len(exp_refs)
log('exp_refs: %r', exp_refs)
+ if fun.decl.name in stolen_refs_by_fnname:
+ # Then this function is marked as stealing references to one or
+ # more of its arguments:
+ for argindex in stolen_refs_by_fnname[fun.decl.name]:
+ # Get argument's value (in initial state of trace):
+ parm = fun.decl.arguments[argindex - 1]
+ v_parm = trace.states[0].eval_rvalue(parm, None)
+ if isinstance(v_parm, PointerToRegion):
+ if region == v_parm.region:
+ exp_refcnt -= 1
+
# Helper function for when ob_refcnt is wrong:
def emit_refcount_error(msg):
err = rep.make_error(fun, endstate.get_gcc_loc(fun), msg)
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c
new file mode 100644
index 0000000..202d11d
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c
@@ -0,0 +1,60 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm at redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of compiling a function that's been (correctly) marked as stealing
+ a reference
+*/
+
+#if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE)
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) \
+ __attribute__((cpychecker_steals_reference_to_arg(n)))
+#else
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n)
+ #error (This should have been defined)
+#endif
+
+extern PyObject *test(PyObject *foo, PyObject *bar)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(1)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(2);
+
+PyObject *saved_objA;
+PyObject *saved_objB;
+
+PyObject *
+test(PyObject *foo, PyObject *bar)
+{
+ /*
+ This code steals references to both of its arguments
+ */
+ saved_objA = foo;
+ saved_objB = bar;
+
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/script.py \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm at 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/steals-reference-to-arg/correct-marking/stdout.txt \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/stdout.txt
new file mode 100644
index 0000000..15cad05
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/stdout.txt
@@ -0,0 +1,20 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c', \
line=51), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct \
PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c:51
+ r->ob_refcnt: refs: 1 + N where N >= 1
+ r->ob_type: None
+ Region("region-for-arg-gcc.ParmDecl('foo')"):
+ repr(): Region("region-for-arg-gcc.ParmDecl('foo')")
+ str(): Region("region-for-arg-gcc.ParmDecl('foo')")
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c', \
line=43), region=Region("region-for-type-of-arg-gcc.ParmDecl('foo')")) + \
Region("region-for-arg-gcc.ParmDecl('bar')"): + repr(): \
Region("region-for-arg-gcc.ParmDecl('bar')") + str(): \
Region("region-for-arg-gcc.ParmDecl('bar')") + r->ob_refcnt: refs: 0 + N where N \
>= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
> loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c', \
> line=43), region=Region("region-for-type-of-arg-gcc.ParmDecl('bar')"))
+ Exception:
+ (struct PyObject *)0 from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-marking/input.c:44
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c \
new file mode 100644 index 0000000..4670c44
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c
@@ -0,0 +1,68 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm at redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of correctly calling a function that's been marked as stealing a
+ reference to its arguments
+*/
+
+#if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE)
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) \
+ __attribute__((cpychecker_steals_reference_to_arg(n)))
+#else
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n)
+ #error (This should have been defined)
+#endif
+
+extern void foo(PyObject *obj)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(1);
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ PyObject *obj;
+
+ obj = PyLong_FromLong(42);
+ if (!obj) {
+ return NULL;
+ }
+
+ /*
+ We now own a reference to "obj"
+
+ Pass it to foo(), which steals it.
+
+ Given that foo has been marked with
+ __attribute__((cpychecker_steals_reference_to_arg(1)))
+ the checker should not complain.
+ */
+ foo(obj);
+
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/script.py \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm at 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/steals-reference-to-arg/correct-usage/stdout.txt \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/stdout.txt
new file mode 100644
index 0000000..4a08c84
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/stdout.txt
@@ -0,0 +1,48 @@
+Trace 0:
+ Transitions:
+ 'when PyLong_FromLong() succeeds'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c', \
line=59), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct \
PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c:59
+ 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/steals-reference-to-arg/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/steals-reference-to-arg/correct-usage/input.c', \
> line=39), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ PyLongObject allocated at \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c:43:
+ repr(): RegionOnHeap('PyLongObject', \
gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c', \
line=43)) + str(): PyLongObject allocated at \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c:43
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c', \
line=43), region=RegionForGlobal(gcc.VarDecl('PyLong_Type'))) + Exception:
+ (struct PyObject *)0 from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c:40
+
+Trace 1:
+ Transitions:
+ 'when PyLong_FromLong() fails'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c', \
line=45), value=0) + str(): (struct PyObject *)0 from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/correct-usage/input.c:45
+ 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/steals-reference-to-arg/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/steals-reference-to-arg/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/steals-reference-to-arg/correct-usage/input.c:43
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c
new file mode 100644
index 0000000..976bcc0
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c
@@ -0,0 +1,55 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm at redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of compiling a function that's been incorrectly marked as stealing
+ a reference, when it does not.
+*/
+
+#if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE)
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) \
+ __attribute__((cpychecker_steals_reference_to_arg(n)))
+#else
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n)
+ #error (This should have been defined)
+#endif
+
+extern PyObject *test(PyObject *foo, PyObject *bar)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(1)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(2);
+
+PyObject *
+test(PyObject *foo, PyObject *bar)
+{
+ /*
+ This code doesn't steals references to its arguments; the marking is
+ incorrect, and this should be flagged by the checker.
+ */
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/script.py \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm at 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/steals-reference-to-arg/incorrect-marking/stderr.txt \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/stderr.txt
new file mode 100644
index 0000000..18ea731
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/stderr.txt
@@ -0,0 +1,10 @@
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c: \
In function 'test': +tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
error: ob_refcnt of '*foo' is 1 too high \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
note: was expecting final ob_refcnt to be N + -1 (for some unknown N) \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
note: but final ob_refcnt is N + 0 \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
note: returning at: Py_RETURN_NONE; \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
error: ob_refcnt of '*bar' is 1 too high \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
note: was expecting final ob_refcnt to be N + -1 (for some unknown N) \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
note: but final ob_refcnt is N + 0 \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46:5: \
note: returning at: Py_RETURN_NONE; \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:41:1: \
note: graphical error report for function 'test' written out to \
'tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/stdout.txt \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/stdout.txt
new file mode 100644
index 0000000..a4ca8ec
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/stdout.txt
@@ -0,0 +1,20 @@
+Trace 0:
+ Transitions:
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c', \
line=46), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct'))) + str(): (struct \
PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:46
+ r->ob_refcnt: refs: 1 + N where N >= 1
+ r->ob_type: None
+ Region("region-for-arg-gcc.ParmDecl('foo')"):
+ repr(): Region("region-for-arg-gcc.ParmDecl('foo')")
+ str(): Region("region-for-arg-gcc.ParmDecl('foo')")
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c', \
line=40), region=Region("region-for-type-of-arg-gcc.ParmDecl('foo')")) + \
Region("region-for-arg-gcc.ParmDecl('bar')"): + repr(): \
Region("region-for-arg-gcc.ParmDecl('bar')") + str(): \
Region("region-for-arg-gcc.ParmDecl('bar')") + r->ob_refcnt: refs: 0 + N where N \
>= 1 + r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
> loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c', \
> line=40), region=Region("region-for-type-of-arg-gcc.ParmDecl('bar')"))
+ Exception:
+ (struct PyObject *)0 from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-marking/input.c:41
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c
new file mode 100644
index 0000000..4ce5d0a
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c
@@ -0,0 +1,72 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm at redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of calling a function that's been marked as stealing a
+ reference to its arguments, but incorrectly treating the argument as
+ still being owned by us.
+*/
+
+#if defined(WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE)
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n) \
+ __attribute__((cpychecker_steals_reference_to_arg(n)))
+#else
+ #define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n)
+ #error (This should have been defined)
+#endif
+
+extern void foo(PyObject *obj)
+ CPYCHECKER_STEALS_REFERENCE_TO_ARG(1);
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ PyObject *obj;
+
+ obj = PyLong_FromLong(42);
+ if (!obj) {
+ return NULL;
+ }
+
+ /*
+ We now own a reference to "obj"
+
+ Pass it to foo(), which steals it.
+
+ */
+ foo(obj);
+
+ /*
+ Given that foo has been marked with
+ __attribute__((cpychecker_steals_reference_to_arg(1)))
+ this is an error: we no longer own the reference to "obj"
+ that's expected of us when returning a non-NULL PyObject*:
+ */
+ return obj;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/script.py \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/script.py
new file mode 100644
index 0000000..6bb62fd
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/script.py
@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+# Copyright 2011 David Malcolm <dmalcolm at 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/steals-reference-to-arg/incorrect-usage/stderr.txt \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/stderr.txt
new file mode 100644
index 0000000..4add37a
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/stderr.txt
@@ -0,0 +1,13 @@
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c: \
In function 'test': +tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:64:1: \
error: ob_refcnt of return value is 1 too low \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:64:1: \
note: was expecting final ob_refcnt to be N + 1 (for some unknown N) \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:64:1: \
note: due to object being referenced by: return value \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:64:1: \
note: but final ob_refcnt is N + 0 \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:44:9: \
note: PyLongObject allocated at: obj = PyLong_FromLong(42); \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:44:9: \
note: when PyLong_FromLong() succeeds at: obj = PyLong_FromLong(42); \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:44:9: \
note: ob_refcnt is now refs: 1 + N where N >= 0 \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:45:8: \
note: taking False path at: if (!obj) { \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:55:8: \
note: reaching: foo(obj); \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:55:8: \
note: ob_refcnt is now refs: 0 + N where N >= 1 \
+tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:64:1: \
note: returning +tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:41:1: \
note: graphical error report for function 'test' written out to \
'tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/stdout.txt \
b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/stdout.txt
new file mode 100644
index 0000000..829f7d1
--- /dev/null
+++ b/tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/stdout.txt
@@ -0,0 +1,43 @@
+Trace 0:
+ Transitions:
+ 'when PyLong_FromLong() succeeds'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c', \
line=44), region=RegionOnHeap('PyLongObject', \
gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c', \
line=44))) + str(): (struct PyObject *)&RegionOnHeap('PyLongObject', \
gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c', \
line=44)) from tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:44
+ r->ob_refcnt: refs: 0 + N where N >= 1
+ r->ob_type: PointerToRegion(gcctype='struct PyTypeObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c', \
line=44), region=RegionForGlobal(gcc.VarDecl('PyLong_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/attributes/steals-reference-to-arg/incorrect-usage/input.c', \
> line=40), 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/steals-reference-to-arg/incorrect-usage/input.c', \
line=40), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')")) + Exception:
+ (struct PyObject *)0 from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:41
+
+Trace 1:
+ Transitions:
+ 'when PyLong_FromLong() fails'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', \
loc=gcc.Location(file='tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c', \
line=46), value=0) + str(): (struct PyObject *)0 from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:46
+ 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/steals-reference-to-arg/incorrect-usage/input.c', \
line=40), 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/steals-reference-to-arg/incorrect-usage/input.c', \
> line=40), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_MemoryError')) from \
tests/cpychecker/refcounts/attributes/steals-reference-to-arg/incorrect-usage/input.c:44
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic