[gcc-python-plugin] cpychecker: implement PyObject_AsFileDescriptor
by David Hugh Malcolm
commit 7b13c83daecdf59783ef96501adbe35bfe3aa893
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Dec 9 17:51:41 2011 -0500
cpychecker: implement PyObject_AsFileDescriptor
libcpychecker/refcounts.py | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 83396cd..eca5786 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1550,6 +1550,21 @@ class CPython(Facet):
return [t_success, t_failure]
+ def impl_PyObject_AsFileDescriptor(self, stmt, v_o):
+ # http://docs.python.org/c-api/object.html#PyObject_AsFileDescriptor
+ # int PyObject_AsFileDescriptor(PyObject *o)
+ # Implemented in Objects/fileobject.c
+ # Uses PyInt_Check(o) macro, which will segfault on NULL
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_o)
+
+ fnname = stmt.fn.operand.name
+
+ # For now, don't try to implement the internal logic:
+ t_return = self.state.mktrans_assignment(stmt.lhs,
+ UnknownValue.make(stmt.lhs.type, stmt.loc),
+ 'when %s() returns' % fnname)
+ return [t_return]
+
def impl_PyObject_Call(self, stmt, v_o, v_args, v_kw):
# http://docs.python.org/c-api/object.html#PyObject_Call
# Implemented in Objects/abstract.c:
12 years, 5 months
[gcc-python-plugin] cpychecker: implement PyModule_GetDict
by David Hugh Malcolm
commit 123245bfdd79901369bf8f5c4bbb86dcd4099258
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Dec 9 17:51:07 2011 -0500
cpychecker: implement PyModule_GetDict
libcpychecker/refcounts.py | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index ff6ea38..83396cd 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1490,6 +1490,14 @@ class CPython(Facet):
return self.state.make_transitions_for_fncall(stmt, s_success, s_failure)
+ def impl_PyModule_GetDict(self, stmt, v_module):
+ # http://docs.python.org/c-api/module.html#PyModule_GetDict
+ # PyObject* PyModule_GetDict(PyObject *module)
+ # returns borrowed ref. Always succeeds
+ fnname = stmt.fn.operand.name
+ s_success = self.mkstate_borrowed_ref(stmt, 'module __dict__ from %s' % fnname)
+ return [Transition(self.state, s_success, None)]
+
########################################################################
# PyObject_*
########################################################################
12 years, 5 months
[gcc-python-plugin] cpychecker: implement PyLong_FromLongLong
by David Hugh Malcolm
commit bf90f1e8268e646e24027dc365bafd9213a81165
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Dec 9 17:50:04 2011 -0500
cpychecker: implement PyLong_FromLongLong
libcpychecker/refcounts.py | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 29dc62b..ff6ea38 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1366,6 +1366,14 @@ class CPython(Facet):
'PyLong_Type')
return [t_success, t_failure]
+ def impl_PyLong_FromLongLong(self, stmt, v_v):
+ # http://docs.python.org/c-api/long.html#PyLong_FromLongLong
+ # PyObject* PyLong_FromLongLong(PY_LONG_LONG v)
+ r_newobj, t_success, t_failure = self.object_ctor(stmt,
+ 'PyLongObject',
+ 'PyLong_Type')
+ return [t_success, t_failure]
+
def impl_PyLong_FromString(self, stmt, v_str, v_pend, v_base):
# Declared in longobject.h as:
# PyAPI_FUNC(PyObject *) PyLong_FromString(char *, char **, int);
12 years, 5 months
[gcc-python-plugin] cpychecker: implement PyErr_WarnEx
by David Hugh Malcolm
commit 1c53333a35982b1335c9febf3ce7af05111bb087
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Dec 9 17:49:16 2011 -0500
cpychecker: implement PyErr_WarnEx
libcpychecker/refcounts.py | 12 ++++++++++++
1 files changed, 12 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 02df388..29dc62b 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1101,6 +1101,18 @@ class CPython(Facet):
t_next.dest.cpython.exception_rvalue = v_exc
return [t_next]
+ def impl_PyErr_WarnEx(self, stmt, v_category, v_text, v_stack_level):
+ # http://docs.python.org/c-api/exceptions.html#PyErr_WarnEx
+ # int
+ # PyErr_WarnEx(PyObject *category, const char *text, Py_ssize_t stack_level)
+ # returns 0 on OK
+ # returns -1 if an exception is raised
+ # Defined in Python/_warnings.c
+ s_success = self.state.mkstate_concrete_return_of(stmt, 0)
+ s_failure = self.state.mkstate_concrete_return_of(stmt, -1)
+ s_failure.cpython.set_exception('PyExc_MemoryError', stmt.loc)
+ return self.state.make_transitions_for_fncall(stmt, s_success, s_failure)
+
########################################################################
# PyEval_InitThreads()
########################################################################
12 years, 5 months
[gcc-python-plugin] cpychecker: implement Py_AtExit
by David Hugh Malcolm
commit fcf91def0b8f93c893ad2d095b8959082ae4e7bc
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Dec 9 17:48:38 2011 -0500
cpychecker: implement Py_AtExit
libcpychecker/refcounts.py | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 450ec31..02df388 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -775,6 +775,19 @@ class CPython(Facet):
return result
########################################################################
+ def impl_Py_AtExit(self, stmt, v_func):
+ # http://docs.python.org/c-api/sys.html#Py_AtExit
+
+ fnname = stmt.fn.operand.name
+
+ # Dummy implementation
+ t_return = self.state.mktrans_assignment(stmt.lhs,
+ UnknownValue.make(stmt.lhs.type, stmt.loc),
+ 'when %s() returns' % fnname)
+ return [t_return]
+
+
+ ########################################################################
# PyBool_*
########################################################################
def impl_PyBool_FromLong(self, stmt, v_long):
12 years, 5 months
[gcc-python-plugin] cpychecker: implement PyArg_Parse
by David Hugh Malcolm
commit 43d72ffbc1077228ef65ad5a0ae05561885a0f03
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Dec 8 19:00:35 2011 -0500
cpychecker: implement PyArg_Parse
docs/cpychecker.rst | 3 +
libcpychecker/formatstrings.py | 10 ++++
libcpychecker/refcounts.py | 14 +++++
.../PyArg_Parse/incorrect_codes_S_and_U/input.c | 45 +++++++++++++++
.../PyArg_Parse/incorrect_codes_S_and_U/script.py | 19 +++++++
.../PyArg_Parse/incorrect_codes_S_and_U/stderr.txt | 13 +++++
.../refcounts/PyArg_Parse/correct_simple/input.c | 57 ++++++++++++++++++++
.../refcounts/PyArg_Parse/correct_simple/script.py | 22 ++++++++
.../PyArg_Parse/correct_simple/stdout.txt | 43 +++++++++++++++
9 files changed, 226 insertions(+), 0 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index 62aa6b3..2d38e52 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -309,6 +309,9 @@ It currently verifies the arguments to the following API entrypoints:
* `PyArg_ParseTupleAndKeywords
<http://docs.python.org/c-api/arg.html#PyArg_ParseTupleAndKeywords>`_
+ * `PyArg_Parse
+ <http://docs.python.org/c-api/arg.html#PyArg_Parse>`_
+
* `Py_BuildValue
<http://docs.python.org/c-api/arg.html#Py_BuildValue>`_
diff --git a/libcpychecker/formatstrings.py b/libcpychecker/formatstrings.py
index 3037f6d..b116a9b 100644
--- a/libcpychecker/formatstrings.py
+++ b/libcpychecker/formatstrings.py
@@ -431,6 +431,16 @@ def check_pyargs(fun):
PyArgParseFmt,
'PyArg_ParseTuple',
1, 2, True)
+ elif stmt.fndecl.name == 'PyArg_Parse':
+ check_callsite(stmt,
+ PyArgParseFmt,
+ 'PyArg_Parse',
+ 1, 2, False)
+ elif stmt.fndecl.name == '_PyArg_Parse_SizeT':
+ check_callsite(stmt,
+ PyArgParseFmt,
+ 'PyArg_Parse',
+ 1, 2, True)
elif stmt.fndecl.name == 'PyArg_ParseTupleAndKeywords':
check_keyword_array(stmt, 3)
check_callsite(stmt,
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 5082cae..450ec31 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -661,6 +661,20 @@ class CPython(Facet):
return self.state.make_transitions_for_fncall(stmt, s_success, s_failure)
+ def impl_PyArg_Parse(self, stmt, v_args, v_fmt, *v_varargs):
+ # Declared in modsupport.h:
+ # PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...);
+ # Also, with #ifdef PY_SSIZE_T_CLEAN
+ # #define PyArg_Parse _PyArg_Parse_SizeT
+ return self._handle_PyArg_function(stmt, v_fmt, v_varargs, with_size_t=False)
+
+ def impl__PyArg_Parse_SizeT(self, stmt, v_args, v_fmt, *v_varargs):
+ # Declared in modsupport.h:
+ # PyAPI_FUNC(int) PyArg_Parse(PyObject *, const char *, ...);
+ # Also, with #ifdef PY_SSIZE_T_CLEAN
+ # #define PyArg_Parse _PyArg_Parse_SizeT
+ return self._handle_PyArg_function(stmt, v_fmt, v_varargs, with_size_t=True)
+
def impl_PyArg_ParseTuple(self, stmt, v_args, v_fmt, *v_varargs):
# Declared in modsupport.h:
# PyAPI_FUNC(int) PyArg_ParseTuple(PyObject *, const char *, ...) Py_FORMAT_PARSETUPLE(PyArg_ParseTuple, 2, 3);
diff --git a/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/input.c b/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/input.c
new file mode 100644
index 0000000..ac5e962
--- /dev/null
+++ b/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/input.c
@@ -0,0 +1,45 @@
+/*
+ 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>
+
+/*
+ Verify that the checker warns about incorrect usage of
+ PyArg_Parse
+*/
+PyObject *
+incorrect_usage_of_S_and_U(PyObject *self, PyObject *args)
+{
+ int val1;
+ int val2;
+
+ /* Incorrect: mismatching types: */
+ if (!PyArg_Parse(args, "SU", &val1, &val2)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/script.py b/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/script.py
new file mode 100644
index 0000000..e120c53
--- /dev/null
+++ b/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/script.py
@@ -0,0 +1,19 @@
+# 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()
diff --git a/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/stderr.txt b/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/stderr.txt
new file mode 100644
index 0000000..fc82029
--- /dev/null
+++ b/tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/stderr.txt
@@ -0,0 +1,13 @@
+tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/input.c: In function 'incorrect_usage_of_S_and_U':
+tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/input.c:33:21: error: Mismatching type in call to PyArg_Parse with format code "SU" [-fpermissive]
+ argument 3 ("&val1") had type
+ "int *" (pointing to 32 bits)
+ but was expecting
+ one of "struct PyStringObject * *" or "struct PyObject * *"
+ for format code "S"
+tests/cpychecker/PyArg_Parse/incorrect_codes_S_and_U/input.c:33:21: error: Mismatching type in call to PyArg_Parse with format code "SU" [-fpermissive]
+ argument 4 ("&val2") had type
+ "int *" (pointing to 32 bits)
+ but was expecting
+ one of "struct PyUnicodeObject * *" or "struct PyObject * *"
+ for format code "U"
diff --git a/tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c b/tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c
new file mode 100644
index 0000000..01172e1
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c
@@ -0,0 +1,57 @@
+/*
+ Copyright 2011 David Malcolm <dmalcolm(a)redhat.com>
+ Copyright 2011 Red Hat, Inc.
+
+ This is free software: you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see
+ <http://www.gnu.org/licenses/>.
+*/
+
+#include <Python.h>
+
+/*
+ Test of correct reference-handling in a call to PyArg_Parse
+ that uses simple format codes that don't affect reference counting
+*/
+
+extern void foo(int i, const char *msg, Py_UNICODE *unicode, int size);
+
+PyObject *
+test(PyObject *self, PyObject *args)
+{
+ int i;
+ const char *msg;
+ Py_UNICODE *unicode;
+ int size;
+
+ if (!PyArg_Parse(args, "isu#:test",
+ &i, &msg, &unicode, &size)) {
+ return NULL;
+ }
+
+ foo(i, msg, unicode, size);
+
+ Py_RETURN_NONE;
+}
+static PyMethodDef test_methods[] = {
+ {"test_method", test, METH_OLDARGS, 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/PyArg_Parse/correct_simple/script.py b/tests/cpychecker/refcounts/PyArg_Parse/correct_simple/script.py
new file mode 100644
index 0000000..fdd5ba3
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyArg_Parse/correct_simple/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/PyArg_Parse/correct_simple/stdout.txt b/tests/cpychecker/refcounts/PyArg_Parse/correct_simple/stdout.txt
new file mode 100644
index 0000000..e839a4e
--- /dev/null
+++ b/tests/cpychecker/refcounts/PyArg_Parse/correct_simple/stdout.txt
@@ -0,0 +1,43 @@
+Trace 0:
+ Transitions:
+ 'when PyArg_Parse() succeeds'
+ 'taking False path'
+ 'returning'
+ Return value:
+ repr(): PointerToRegion(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c', line=44), region=RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')))
+ str(): (struct PyObject *)&RegionForGlobal(gcc.VarDecl('_Py_NoneStruct')) from tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c:44
+ 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/PyArg_Parse/correct_simple/input.c', line=30), 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/PyArg_Parse/correct_simple/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)0 from tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c:31
+
+Trace 1:
+ Transitions:
+ 'when PyArg_Parse() fails'
+ 'taking True path'
+ 'returning'
+ Return value:
+ repr(): ConcreteValue(gcctype='struct PyObject *', loc=gcc.Location(file='tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c', line=39), value=0)
+ str(): (struct PyObject *)0 from tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c:39
+ 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/PyArg_Parse/correct_simple/input.c', line=30), 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/PyArg_Parse/correct_simple/input.c', line=30), region=Region("region-for-type-of-arg-gcc.ParmDecl('args')"))
+ Exception:
+ (struct PyObject *)&RegionForGlobal(gcc.VarDecl('PyExc_TypeError')) from tests/cpychecker/refcounts/PyArg_Parse/correct_simple/input.c:37
12 years, 5 months
[gcc-python-plugin] remove redundant test
by David Hugh Malcolm
commit deff14672f0796272baf17dfc2bf949a740c692c
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Dec 8 18:17:15 2011 -0500
remove redundant test
Remove test code for get_all_PyMethodDef_methods, as this code
was removed in e0d2ea5caa9397a8f96e6a7c915de449137ae497
tests/cpychecker/locate-methods/getopts.py | 18 ------------
tests/cpychecker/locate-methods/input.c | 40 ----------------------------
tests/cpychecker/locate-methods/script.py | 33 -----------------------
tests/cpychecker/locate-methods/stdout.txt | 7 -----
4 files changed, 0 insertions(+), 98 deletions(-)
12 years, 5 months
[gcc-python-plugin] implement verification of PyMethodDef tables
by David Hugh Malcolm
commit e0d2ea5caa9397a8f96e6a7c915de449137ae497
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Dec 8 18:14:45 2011 -0500
implement verification of PyMethodDef tables
From the "Ideas for future tests", implement checking of PyMethodDef tables,
verifying that the signatures match the flags (by number of arguments, at
least). Also, verify that they are NULL terminated.
This found an actual mismatch within the test suite, in:
tests/cpychecker/refcounts/PyArg_ParseTupleAndKeywords/correct_O
with this error message:
input.c:48:22: error: flags do not match callback signature for 'test' within PyMethodDef table
input.c:48:22: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)" (2 arguments)
input.c:48:22: note: actual type of underlying callback: struct PyObject * <Tc58> (struct PyObject *, struct PyObject *, struct PyObject *) (3 arguments)
input.c:48:22: note: see http://docs.python.org/c-api/structures.html#PyMethodDef
docs/cpychecker.rst | 60 ++++++-
libcpychecker/__init__.py | 62 +++-----
libcpychecker/initializers.py | 170 ++++++++++++++++++++
tests/cpychecker/PyMethodDef/correct/input.c | 81 +++++++++
tests/cpychecker/PyMethodDef/correct/script.py | 21 +++
.../cpychecker/PyMethodDef/incorrect-types/input.c | 84 ++++++++++
.../PyMethodDef/incorrect-types/script.py | 21 +++
.../PyMethodDef/incorrect-types/stderr.txt | 12 ++
.../PyMethodDef/missing-sentinel/input.c | 52 ++++++
.../PyMethodDef/missing-sentinel/script.py | 21 +++
.../PyMethodDef/missing-sentinel/stderr.txt | 1 +
.../PyArg_ParseTupleAndKeywords/correct_O/input.c | 2 +-
12 files changed, 539 insertions(+), 48 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index c1d0641..62aa6b3 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -419,6 +419,60 @@ Given the above, the checker will associate the given ``PyTypeObject`` with the
given typedef.
+Verification of PyMethodDef tables
+----------------------------------
+
+The checker will verify the types within tables of `PyMethodDef
+<http://docs.python.org/c-api/structures.html#PyMethodDef>`_ initializers: the
+callbacks are typically cast to ``PyCFunction``, but the exact type needs to
+correspond to the flags given. For example ``(METH_VARARGS | METH_KEYWORDS)``
+implies a different function signature to the default, which the vanilla C
+compiler has no way of verifying.
+
+.. code-block:: c
+
+ /*
+ BUG: there's a mismatch between the signature of the callback and
+ that implied by ml_flags below.
+ */
+ static PyObject *widget_display(PyObject *self, PyObject *args);
+
+ static PyMethodDef widget_methods[] = {
+ {"display",
+ (PyCFunction)widget_display,
+ (METH_VARARGS | METH_KEYWORDS), /* ml_flags */
+ NULL},
+
+ {NULL, NULL, 0, NULL} /* terminator */
+ };
+
+Given the above, the checker will emit an error like this::
+
+ input.c:59:6: error: flags do not match callback signature for 'widget_display' within PyMethodDef table
+ input.c:59:6: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag (3 arguments)
+ input.c:59:6: note: actual type of underlying callback: struct PyObject * <Tc53> (struct PyObject *, struct PyObject *) (2 arguments)
+ input.c:59:6: note: see http://docs.python.org/c-api/structures.html#PyMethodDef
+
+It will also warn about tables of ``PyMethodDef`` initializers that are
+lacking a ``NULL`` sentinel value to terminate the iteration:
+
+.. code-block:: c
+
+ static PyMethodDef widget_methods[] = {
+ {"display",
+ (PyCFunction)widget_display,
+ 0, /* ml_flags */
+ NULL},
+
+ /* BUG: this array is missing a NULL value to terminate
+ the list of methods, leading to a possible segfault
+ at run-time */
+ };
+
+Given the above, the checker will emit this error::
+
+ input.c:39:6: error: missing NULL sentinel value at end of PyMethodDef table
+
Limitations and caveats
-----------------------
@@ -460,12 +514,6 @@ Ideas for future tests
Here's a list of some other C coding bugs I intend for the tool to detect:
- * type-checking for PyMethodDef tables: the callbacks are typically cast
- to PyCFunction, but the exact type needs to correspond to the flags
- given (e.g. ``(METH_VARARGS | METH_KEYWORDS)`` implies a different function
- signature to the default, and the C compiler has currently no way of
- verifying that these agree with each other).
-
* tp_traverse errors (which can mess up the garbage collector); missing it
altogether, or omitting fields
diff --git a/libcpychecker/__init__.py b/libcpychecker/__init__.py
index 46a9927..be2834f 100644
--- a/libcpychecker/__init__.py
+++ b/libcpychecker/__init__.py
@@ -20,10 +20,12 @@ from libcpychecker.formatstrings import check_pyargs
from libcpychecker.utils import log
from libcpychecker.refcounts import check_refcounts, get_traces
from libcpychecker.attributes import register_our_attributes
+from libcpychecker.initializers import check_initializers
-class CpyChecker(gcc.GimplePass):
+class CpyCheckerGimplePass(gcc.GimplePass):
"""
- The custom pass that implements our extra compile-time checks
+ The custom pass that implements the per-function part of
+ our extra compile-time checks
"""
def __init__(self,
dump_traces=False,
@@ -31,7 +33,7 @@ class CpyChecker(gcc.GimplePass):
verify_pyargs=True,
verify_refcounting=False,
show_possible_null_derefs=False):
- gcc.GimplePass.__init__(self, 'cpychecker')
+ gcc.GimplePass.__init__(self, 'cpychecker-gimple')
self.dump_traces = dump_traces
self.show_traces = show_traces
self.verify_pyargs = verify_pyargs
@@ -49,52 +51,30 @@ class CpyChecker(gcc.GimplePass):
check_refcounts(fun, self.dump_traces, self.show_traces,
self.show_possible_null_derefs)
-def is_a_method_callback(decl):
- methods = get_all_PyMethodDef_methods()
- log('methods: %s', methods)
- # FIXME
-
-
-def get_all_PyMethodDef_methods():
- # Locate all initializers for PyMethodDef, returning a list of
- # (gcc.Declaration, gcc.Location) for the relevant callback functions
- # (the ml_meth field, and the location of the initializer)
- log('get_all_PyMethodDef_methods')
+class CpyCheckerIpaPass(gcc.SimpleIpaPass):
+ """
+ The custom pass that implements the whole-program part of
+ our extra compile-time checks
+ """
+ def __init__(self):
+ gcc.SimpleIpaPass.__init__(self, 'cpychecker-ipa')
- def get_ml_meth_decl(methoddef_initializer):
- for idx2, value2 in value.elements:
- if isinstance(idx2, gcc.Declaration):
- if idx2.name == 'ml_meth':
- if isinstance(value2, gcc.AddrExpr):
- log(' GOT A PyMethodDef.ml_meth initializer declaration: %s', value2)
- log(' value2.operand: %r', value2.operand) # gcc.Declaration
- log(' value2.operand: %s', value2.operand)
- log(' value2.operand.function: %s', value2.operand.function)
- return (value2.operand, value2.location)
- result = []
- vars = gcc.get_variables()
- for var in vars:
- if isinstance(var.decl, gcc.VarDecl):
- if isinstance(var.decl.type, gcc.ArrayType):
- if str(var.decl.type.type) == 'struct PyMethodDef':
- if var.decl.initial:
- for idx, value in var.decl.initial.elements:
- decl = get_ml_meth_decl(value)
- if decl:
- result.append(decl)
- return result
+ def execute(self):
+ check_initializers()
def main(**kwargs):
# Register our custom attributes:
gcc.register_callback(gcc.PLUGIN_ATTRIBUTES,
register_our_attributes)
- # Register our GCC pass:
- ps = CpyChecker(**kwargs)
-
+ # Register our GCC passes:
+ gimple_ps = CpyCheckerGimplePass(**kwargs)
if 1:
# non-SSA version:
- ps.register_before('*warn_function_return')
+ gimple_ps.register_before('*warn_function_return')
else:
# SSA version:
- ps.register_after('ssa')
+ gimple_ps.register_after('ssa')
+
+ ipa_ps = CpyCheckerIpaPass()
+ ipa_ps.register_before('*free_lang_data')
diff --git a/libcpychecker/initializers.py b/libcpychecker/initializers.py
new file mode 100644
index 0000000..6b3c691
--- /dev/null
+++ b/libcpychecker/initializers.py
@@ -0,0 +1,170 @@
+# 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/>.
+
+# Verification of data initializers (e.g. PyMethodDef tables)
+
+import gcc
+
+from gccutils import check_isinstance
+
+from libcpychecker.utils import log
+
+def check_initializers():
+ # Invoked by the "cpychecker-ipa" pass, once per compilation unit
+ verify_any_PyMethodDef_flags()
+
+from collections import OrderedDict
+class StructInitializer(object):
+ def __init__(self, ctor):
+ check_isinstance(ctor, gcc.Constructor)
+ self.ctor = ctor
+
+ # Mapping from string fieldname to gcc.Tree value:
+ self.fielddict = OrderedDict()
+ for key, tree in ctor.elements:
+ check_isinstance(key, gcc.FieldDecl)
+ self.fielddict[key.name] = tree
+
+ def __repr__(self):
+ attrs = ','.join(['%s=%s' % (k, v)
+ for k, v in self.fielddict.items()])
+ return '%s(%s)' % (self.__class__.__name__, attrs)
+
+ def int_field(self, fieldname):
+ """
+ Extract the initializer for the given field, as an int
+ """
+ tree = self.fielddict[fieldname]
+ check_isinstance(tree, gcc.IntegerCst)
+ return tree.constant
+
+ def char_ptr_field(self, fieldname):
+ tree = self.fielddict[fieldname]
+ if isinstance(tree, gcc.IntegerCst):
+ if tree.constant == 0:
+ return None # NULL
+ # go past casts:
+ if isinstance(tree, gcc.NopExpr):
+ tree = tree.operand
+ check_isinstance(tree, gcc.AddrExpr)
+ check_isinstance(tree.operand, gcc.StringCst)
+ return tree.operand.constant
+
+ def function_ptr_field(self, fieldname):
+ """
+ Extract the initializer for the given field, as a gcc.FunctionDecl,
+ or None for NULL.
+ """
+ tree = self.fielddict[fieldname]
+
+ # go past casts:
+ if isinstance(tree, gcc.NopExpr):
+ tree = tree.operand
+ if isinstance(tree, gcc.IntegerCst):
+ if tree.constant == 0:
+ return None # NULL
+ check_isinstance(tree, gcc.AddrExpr)
+ return tree.operand
+
+class PyMethodDefInitializer(StructInitializer):
+ def get_location(self):
+ return self.fielddict['ml_meth'].location
+
+# Adapted from Include/methodobject.h:
+METH_OLDARGS = 0x0000
+METH_VARARGS = 0x0001
+METH_KEYWORDS = 0x0002
+METH_NOARGS = 0x0004
+METH_O = 0x0008
+METH_CLASS = 0x0010
+METH_STATIC = 0x0020
+METH_COEXIST = 0x0040
+
+def verify_any_PyMethodDef_flags():
+ """
+ Check all initializers for PyMethodDef arrays.
+ Verify that the flags used match the real signature of the callback
+ function (albeit usually cast to a PyCFunction):
+ http://docs.python.org/c-api/structures.html#PyMethodDef
+ """
+ methods = get_all_PyMethodDef_initializers()
+ #from pprint import pprint
+ #pprint(methods)
+
+ for si in methods:
+ if 0:
+ print(si)
+ ml_meth = si.function_ptr_field('ml_meth')
+ ml_flags = si.int_field('ml_flags')
+ if 0:
+ print(' ml_meth: %r' % ml_meth)
+ print(' ml_flags: %r' % ml_flags)
+ check_isinstance(ml_flags, int)
+
+ if ml_meth is not None:
+ check_isinstance(ml_meth, gcc.FunctionDecl)
+ if ml_flags & METH_KEYWORDS:
+ expargs = 3
+ exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag'
+ else:
+ expargs = 2
+ exptypemsg = 'expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)"'
+ actualargs = len(ml_meth.type.argument_types)
+ if expargs != actualargs:
+ gcc.error(si.get_location(),
+ 'flags do not match callback signature for %r'
+ ' within PyMethodDef table'
+ % ml_meth.name)
+ gcc.inform(si.get_location(),
+ exptypemsg + ' (%s arguments)' % expargs)
+ gcc.inform(si.get_location(),
+ 'actual type of underlying callback: %s' % ml_meth.type
+ + ' (%s arguments)' % actualargs)
+ gcc.inform(si.get_location(),
+ 'see http://docs.python.org/c-api/structures.html#PyMethodDef')
+
+def get_all_PyMethodDef_initializers():
+ """
+ Locate all initializers for PyMethodDef, returning a list
+ of StructInitializer instances
+ """
+ log('get_all_PyMethodDef_initializers')
+
+ result = []
+ vars = gcc.get_variables()
+ for var in vars:
+ if isinstance(var.decl, gcc.VarDecl):
+ if isinstance(var.decl.type, gcc.ArrayType):
+ if str(var.decl.type.type) == 'struct PyMethodDef':
+ if var.decl.initial:
+ table = []
+ for idx, ctor in var.decl.initial.elements:
+ #print idx, ctor
+ si = PyMethodDefInitializer(ctor)
+ table.append(si)
+ # Warn about missing sentinel entry with
+ # ml->ml_name == NULL
+ ml_name = table[-1].char_ptr_field('ml_name')
+ if 0:
+ print('final ml_name: %r' % ml_name)
+ if ml_name is not None:
+ gcc.error(table[-1].get_location(),
+ 'missing NULL sentinel value at end of PyMethodDef table')
+ result += table
+ return result
+
+
diff --git a/tests/cpychecker/PyMethodDef/correct/input.c b/tests/cpychecker/PyMethodDef/correct/input.c
new file mode 100644
index 0000000..976e55d
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/correct/input.c
@@ -0,0 +1,81 @@
+/*
+ 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>
+
+/*
+ http://docs.python.org/c-api/structures.html#PyMethodDef
+*/
+
+/*
+ Verify that the analyser can cope with various function signatures within
+ a table of PyMethodDef initializers
+*/
+
+typedef struct MySubclass {
+ PyObject_HEAD
+ struct foo *f;
+} MySubclass;
+
+static PyObject *
+correct_pycfunction(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+correct_subclass(MySubclass *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+correct_pycfunction_with_keywords(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ Py_RETURN_NONE;
+}
+
+/* etc */
+
+static PyMethodDef methods[] = {
+ {"test1",
+ (PyCFunction)correct_pycfunction,
+ 0, /* ml_flags */
+ NULL},
+
+ {"test2",
+ (PyCFunction)correct_subclass,
+ 0, /* ml_flags */
+ NULL},
+
+ {"test3",
+ (PyCFunction)correct_pycfunction_with_keywords,
+ (METH_VARARGS | METH_KEYWORDS),
+ NULL},
+
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/PyMethodDef/correct/script.py b/tests/cpychecker/PyMethodDef/correct/script.py
new file mode 100644
index 0000000..0d5d05f
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/correct/script.py
@@ -0,0 +1,21 @@
+# -*- 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=False)
diff --git a/tests/cpychecker/PyMethodDef/correct/stdout.txt b/tests/cpychecker/PyMethodDef/correct/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/cpychecker/PyMethodDef/incorrect-types/input.c b/tests/cpychecker/PyMethodDef/incorrect-types/input.c
new file mode 100644
index 0000000..5ca700d
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/incorrect-types/input.c
@@ -0,0 +1,84 @@
+/*
+ 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>
+
+/*
+ http://docs.python.org/c-api/structures.html#PyMethodDef
+*/
+
+/*
+ Verify that the analyser warns about various incorrect function signatures
+ referred to from within a table of PyMethodDef initializers
+*/
+
+typedef struct MySubclass {
+ PyObject_HEAD
+ struct foo *f;
+} MySubclass;
+
+static PyObject *
+incorrect_pycfunction(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+incorrect_subclass(MySubclass *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+
+static PyObject *
+incorrect_pycfunction_with_keywords(PyObject *self, PyObject *args, PyObject *kwargs)
+{
+ Py_RETURN_NONE;
+}
+
+/* etc */
+
+static PyMethodDef methods[] = {
+ /* Mismatching flags vs signature: */
+ {"test1",
+ (PyCFunction)incorrect_pycfunction,
+ (METH_VARARGS | METH_KEYWORDS),
+ NULL},
+
+ /* Mismatching flags vs signature: */
+ {"test2",
+ (PyCFunction)incorrect_subclass,
+ (METH_VARARGS | METH_KEYWORDS),
+ NULL},
+
+ /* Mismatching flags vs signature: */
+ {"test3",
+ (PyCFunction)incorrect_pycfunction_with_keywords,
+ 0, /* ml_flags */
+ NULL},
+
+ {NULL, NULL, 0, NULL} /* Sentinel */
+};
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/PyMethodDef/incorrect-types/script.py b/tests/cpychecker/PyMethodDef/incorrect-types/script.py
new file mode 100644
index 0000000..0d5d05f
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/incorrect-types/script.py
@@ -0,0 +1,21 @@
+# -*- 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=False)
diff --git a/tests/cpychecker/PyMethodDef/incorrect-types/stderr.txt b/tests/cpychecker/PyMethodDef/incorrect-types/stderr.txt
new file mode 100644
index 0000000..785eee1
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/incorrect-types/stderr.txt
@@ -0,0 +1,12 @@
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:59:6: error: flags do not match callback signature for 'incorrect_pycfunction' within PyMethodDef table
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:59:6: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag (3 arguments)
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:59:6: note: actual type of underlying callback: struct PyObject * <Tc53> (struct PyObject *, struct PyObject *) (2 arguments)
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:59:6: note: see http://docs.python.org/c-api/structures.html#PyMethodDef
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:65:6: error: flags do not match callback signature for 'incorrect_subclass' within PyMethodDef table
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:65:6: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *args, PyObject *kwargs)" due to METH_KEYWORDS flag (3 arguments)
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:65:6: note: actual type of underlying callback: struct PyObject * <T152e> (struct MySubclass *, struct PyObject *) (2 arguments)
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:65:6: note: see http://docs.python.org/c-api/structures.html#PyMethodDef
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:71:6: error: flags do not match callback signature for 'incorrect_pycfunction_with_keywords' within PyMethodDef table
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:71:6: note: expected ml_meth callback of type "PyObject (fn)(someobject *, PyObject *)" (2 arguments)
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:71:6: note: actual type of underlying callback: struct PyObject * <Tc58> (struct PyObject *, struct PyObject *, struct PyObject *) (3 arguments)
+tests/cpychecker/PyMethodDef/incorrect-types/input.c:71:6: note: see http://docs.python.org/c-api/structures.html#PyMethodDef
diff --git a/tests/cpychecker/PyMethodDef/incorrect-types/stdout.txt b/tests/cpychecker/PyMethodDef/incorrect-types/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/cpychecker/PyMethodDef/missing-sentinel/input.c b/tests/cpychecker/PyMethodDef/missing-sentinel/input.c
new file mode 100644
index 0000000..e2838aa
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/missing-sentinel/input.c
@@ -0,0 +1,52 @@
+/*
+ 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>
+
+/*
+ http://docs.python.org/c-api/structures.html#PyMethodDef
+*/
+
+/*
+ Verify that the analyser warns about a table of PyMethodDef initializers
+ missing a sentinel value
+*/
+
+static PyObject *
+correct_pycfunction(PyObject *self, PyObject *args)
+{
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef methods[] = {
+ {"test",
+ (PyCFunction)correct_pycfunction,
+ 0, /* ml_flags */
+ NULL},
+
+ /* missing a sentinel value */
+};
+
+/*
+ PEP-7
+Local variables:
+c-basic-offset: 4
+indent-tabs-mode: nil
+End:
+*/
diff --git a/tests/cpychecker/PyMethodDef/missing-sentinel/script.py b/tests/cpychecker/PyMethodDef/missing-sentinel/script.py
new file mode 100644
index 0000000..0d5d05f
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/missing-sentinel/script.py
@@ -0,0 +1,21 @@
+# -*- 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=False)
diff --git a/tests/cpychecker/PyMethodDef/missing-sentinel/stderr.txt b/tests/cpychecker/PyMethodDef/missing-sentinel/stderr.txt
new file mode 100644
index 0000000..8eef9fe
--- /dev/null
+++ b/tests/cpychecker/PyMethodDef/missing-sentinel/stderr.txt
@@ -0,0 +1 @@
+tests/cpychecker/PyMethodDef/missing-sentinel/input.c:39:6: error: missing NULL sentinel value at end of PyMethodDef table
diff --git a/tests/cpychecker/PyMethodDef/missing-sentinel/stdout.txt b/tests/cpychecker/PyMethodDef/missing-sentinel/stdout.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/cpychecker/refcounts/PyArg_ParseTupleAndKeywords/correct_O/input.c b/tests/cpychecker/refcounts/PyArg_ParseTupleAndKeywords/correct_O/input.c
index eded3a5..9880342 100644
--- a/tests/cpychecker/refcounts/PyArg_ParseTupleAndKeywords/correct_O/input.c
+++ b/tests/cpychecker/refcounts/PyArg_ParseTupleAndKeywords/correct_O/input.c
@@ -45,7 +45,7 @@ test(PyObject *self, PyObject *args, PyObject *kwargs)
return obj;
}
static PyMethodDef test_methods[] = {
- {"test_method", (PyCFunction)test, METH_VARARGS, NULL},
+ {"test_method", (PyCFunction)test, (METH_VARARGS | METH_KEYWORDS), NULL},
{NULL, NULL, 0, NULL} /* Sentinel */
};
12 years, 5 months
[gcc-python-plugin] docs: add python-krbV to success stories
by David Hugh Malcolm
commit a14e797e9bc171e9127ce41a0fa7cba6ff9721b6
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Dec 2 18:41:47 2011 -0500
docs: add python-krbV to success stories
docs/success.rst | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
---
diff --git a/docs/success.rst b/docs/success.rst
index d7a3fde..d3c8696 100644
--- a/docs/success.rst
+++ b/docs/success.rst
@@ -70,6 +70,15 @@ plugin's :ref:`gcc-with-cpychecker <cpychecker>` script:
* https://fedorahosted.org/pycups/ticket/17
+python-krbV
+-----------
+
+Bug found in the `Python bindings for the Kerberos 5 API
+<https://fedorahosted.org/python-krbV/>`_ by compiling it with the
+plugin's :ref:`gcc-with-cpychecker <cpychecker>` script:
+
+ * https://fedorahosted.org/python-krbV/ticket/1
+
Bugs found in itself
--------------------
Bugs found and fixed in the gcc Python plugin itself, by running the the
12 years, 6 months
[gcc-python-plugin] docs: add notes on using gcc-with-cpychecker from distutils
by David Hugh Malcolm
commit 0e4def8cbf1a837ad6c484e94c1c101642b94ffd
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Fri Dec 2 16:59:16 2011 -0500
docs: add notes on using gcc-with-cpychecker from distutils
docs/cpychecker.rst | 14 ++++++++++++--
1 files changed, 12 insertions(+), 2 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index 61f3087..c1d0641 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -49,12 +49,22 @@ something like::
make CC=/path/to/built/plugin/gcc-with-cpychecker
+to override the Makefile variable `CC`.
+
You may need to supply an absolute path, especially if the "make" recursively
invokes "make" within subdirectories (thus having a different working
directory).
-(TODO: some way of integrating the checker with `distutils
-<http://docs.python.org/library/distutils.html>`_)
+Similarly, for projects that use `distutils
+<http://docs.python.org/library/distutils.html>`_, the code is typically built
+with an invocation like this::
+
+ python setup.py build
+
+This respects the environment variable `CC`, so typically you can replace the
+above with something like this in order to add the additional checks::
+
+ CC=/path/to/built/plugin/gcc-with-cpychecker python setup.py build
Reference-count checking
------------------------
12 years, 6 months