[gcc-python-plugin] docs: add more information on dependencies and building
by David Hugh Malcolm
commit 429979a5e278674648a40bda64c00de8ca87cbbc
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Nov 28 18:07:38 2011 -0500
docs: add more information on dependencies and building
docs/basics.rst | 44 ++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 44 insertions(+), 0 deletions(-)
---
diff --git a/docs/basics.rst b/docs/basics.rst
index 513cf6e..dfe81e0 100644
--- a/docs/basics.rst
+++ b/docs/basics.rst
@@ -40,6 +40,40 @@ The plugin has the following requirements:
http://pygments.org/
+ * graphviz: many of the interesting examples use "dot" to draw diagrams
+ (e.g. control-flow graphs), so it's worth having graphviz installed.
+
+Various distributions ship with pre-built copies of the plugin. If you're
+using Fedora, you can install the plugin via RPM on Fedora 16 onwards using:
+
+.. code-block:: bash
+
+ yum install gcc-python2-plugin
+
+as root for the Python 2 build of the plugin, or:
+
+.. code-block:: bash
+
+ yum install gcc-python3-plugin
+
+for the Python 3 build of the plugin.
+
+If you plan to build the plugin from scratch, you can install the dependencies
+you need on Fedora by running the following as root:
+
+.. code-block:: bash
+
+ yum install gcc-plugin-devel python-devel python-six python-pygments graphviz
+
+for building against Python 2, or:
+
+.. code-block:: bash
+
+ yum install gcc-plugin-devel python3-devel python3-six python3-pygments graphviz
+
+when building for Python 3.
+
+
Basic usage of the plugin
=========================
@@ -61,6 +95,16 @@ You can also use::
to demonstrate the new compiler errors.
+By default, the `Makefile` builds the plugin using the first ``python-config``
+tool found in `$PATH` (e.g. `/usr/bin/python-config`), which is typically the
+system copy of Python 2. You can override this (e.g. to build against
+Python 3) by overriding the `PYTHON` and `PYTHON_CONFIG` Makefile variables
+with:
+
+.. code-block:: bash
+
+ make PYTHON=python3 PYTHON_CONFIG=python3-config
+
There isn't a well-defined process yet for installing the plugin (though the
rpm specfile in the source tree contains some work-in-progress towards this).
12 years, 6 months
[gcc-python-plugin] cpychecker: de-duplicate error reports
by David Hugh Malcolm
commit a90611cfbd57a75c80351f3250e2388255e3943b
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Mon Nov 28 16:45:40 2011 -0500
cpychecker: de-duplicate error reports
Implement simple de-duplication of error reports.
Add a remove_duplicates() method to the Reporter class, to walk the gathered
Report instances, and partition them into equivalence classes before flushing
them. Only the first Report within each such partition is flushed (together
with a note about the number of duplicates seen).
For now we implement only a simple equivalence relationship: two reports are
equivalent if they are for the same function, at the same location, and with
the same summary text.
This successfully removes a genuine duplicate within the test suite:
tests/cpychecker/refcounts/PyDict_SetItem/incorrect
and when compiling the plugin using gcc-with-cpychecker
libcpychecker/diagnostics.py | 57 ++++++++++++++++++-
libcpychecker/refcounts.py | 7 ++-
.../refcounts/PyDict_SetItem/incorrect/stderr.txt | 7 +--
3 files changed, 61 insertions(+), 10 deletions(-)
---
diff --git a/libcpychecker/diagnostics.py b/libcpychecker/diagnostics.py
index 892360a..4cf94cd 100644
--- a/libcpychecker/diagnostics.py
+++ b/libcpychecker/diagnostics.py
@@ -17,11 +17,14 @@
"""
Error reporting interface, supporting regular GCC messages plus higher-level
-HTML visualizations
+HTML visualizations, with de-duplication.
+
+GCC diagnostic messages are buffered up within Report instances and eventually
+flushed, allowing us to de-duplicate error reports.
"""
import gcc
-from gccutils import get_src_for_loc
+from gccutils import get_src_for_loc, check_isinstance
from libcpychecker.visualizations import HtmlRenderer
from libcpychecker.utils import log
@@ -91,6 +94,10 @@ class Reporter:
"""
Error-reporting interface. Gathers information, sending it to GCC's
regular diagnostic interface, but also storing it for e.g. HTML dumps
+
+ Error reports can be de-duplicated by finding sufficiently similar Report
+ instances, and only fully flushing one of them within each equivalence
+ class
"""
def __init__(self):
self.reports = []
@@ -133,6 +140,28 @@ class Reporter:
with open(filename, 'w') as f:
f.write(html)
+ def remove_duplicates(self):
+ """
+ Try to organize Report instances into equivalence classes, and only
+ keep the first Report within each class
+ """
+ for report in self.reports[:]:
+ # The report might have been removed during the iteration:
+ if report.is_duplicate:
+ continue
+ for candidate in self.reports[:]:
+ if report != candidate and not report.is_duplicate:
+ if candidate.is_duplicate_of(report):
+ report.add_duplicate(candidate)
+ self.reports.remove(candidate)
+
+ # Add a note to each report that survived about any duplicates:
+ for report in self.reports:
+ if report.duplicates:
+ report.add_note(report.loc,
+ ('found %i similar trace(s) to this'
+ % len(report.duplicates)))
+
def flush(self):
for r in self.reports:
r.flush()
@@ -169,6 +198,10 @@ class Report:
self.notes = []
self._saved_diagnostics = [] # list of SavedDiagnostic
+ # De-duplication handling:
+ self.is_duplicate = False
+ self.duplicates = [] # list of Report
+
def add_error(self, loc, msg):
# Add a gcc.error() to the buffer of GCC diagnostics
self._saved_diagnostics.append(SavedError(loc, msg))
@@ -201,7 +234,25 @@ class Report:
def get_annotator_for_trace(self, trace):
return self._annotators.get(trace)
-
+ def is_duplicate_of(self, other):
+ check_isinstance(other, Report)
+
+ # Simplistic equivalence classes for now:
+ # the same function, source location, and message; everything
+ # else can be different
+ if self.fun != other.fun:
+ return False
+ if self.loc != other.loc:
+ return False
+ if self.msg != other.msg:
+ return False
+
+ return True
+
+ def add_duplicate(self, other):
+ assert not self.is_duplicate
+ self.duplicates.append(other)
+ other.is_duplicate = True
class Note:
"""
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 4165d34..63c379a 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -2384,8 +2384,13 @@ def check_refcounts(fun, dump_traces=False, show_traces=False,
# (all traces analysed)
+ # Organize the Report instances into equivalence classes, simplifying
+ # the list of reports:
+ rep.remove_duplicates()
+
# Flush the reporter's messages, which will actually emit gcc errors and
- # warnings (if any):
+ # warnings (if any), for those Report instances that survived
+ # de-duplication
rep.flush()
if rep.got_errors():
diff --git a/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt
index 3f2b7fc..a527ce4 100644
--- a/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt
+++ b/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt
@@ -11,10 +11,5 @@ tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:34:8: note: taking F
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: reaching: key = PyLong_FromLong(500);
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: when PyLong_FromLong() fails at: key = PyLong_FromLong(500);
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:39:11: note: when PyLong_FromLong() succeeds at: value = PyLong_FromLong(1000);
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 2 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:33:10: note: when PyDict_New() succeeds at: dict = PyDict_New();
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:34:8: note: taking False path at: if (!dict) {
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: reaching: key = PyLong_FromLong(500);
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: when PyLong_FromLong() fails at: key = PyLong_FromLong(500);
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:39:11: note: when PyLong_FromLong() fails at: value = PyLong_FromLong(1000);
+tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: note: found 1 similar trace(s) to this
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:28:1: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c.test-refcount-errors.html'
12 years, 6 months
[gcc-python-plugin] cpychecker: add buffering of gcc diagnostics within Report instances
by David Hugh Malcolm
commit 566a0ffe55cba662ae6708c56b96dd819b4ca7d3
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Thu Nov 24 12:11:44 2011 -0500
cpychecker: add buffering of gcc diagnostics within Report instances
Previously, gcc diagnostics (error, inform, etc) were emitted immediately as
Report instances were created.
With this change, the diagnostics are buffered within each Report instance,
and flushed at the end.
This is work towards de-duplication of error reports, so that sufficiently
similar reports can be summarized by a single error report.
libcpychecker/diagnostics.py | 71 +++++++++++++++++++++++++++++++++--------
libcpychecker/refcounts.py | 5 +++
2 files changed, 62 insertions(+), 14 deletions(-)
---
diff --git a/libcpychecker/diagnostics.py b/libcpychecker/diagnostics.py
index 91e5d3b..892360a 100644
--- a/libcpychecker/diagnostics.py
+++ b/libcpychecker/diagnostics.py
@@ -56,10 +56,10 @@ class TestAnnotator(Annotator):
transition.dest.loc.get_stmt()))))
return result
-def describe_trace(trace, fun, annotator):
+def describe_trace(trace, report, annotator):
"""
- Print more details about the path through the function that
- leads to the error, using gcc.inform()
+ Buffer up more details about the path through the function that
+ leads to the error, using report.add_inform()
"""
awaiting_target = None
for t in trace.transitions:
@@ -67,25 +67,25 @@ def describe_trace(trace, fun, annotator):
srcloc = t.src.get_gcc_loc_or_none()
if t.desc:
if srcloc:
- gcc.inform(t.src.get_gcc_loc(fun),
- ('%s at: %s'
- % (t.desc, get_src_for_loc(srcloc))))
+ report.add_inform(t.src.get_gcc_loc(report.fun),
+ ('%s at: %s'
+ % (t.desc, get_src_for_loc(srcloc))))
else:
- gcc.inform(t.src.get_gcc_loc(fun),
- '%s' % t.desc)
+ report.add_inform(t.src.get_gcc_loc(report.fun),
+ '%s' % t.desc)
if t.src.loc.bb != t.dest.loc.bb:
# Tell the user where conditionals reach:
destloc = t.dest.get_gcc_loc_or_none()
if destloc:
- gcc.inform(destloc,
- 'reaching: %s' % get_src_for_loc(destloc))
+ report.add_inform(destloc,
+ 'reaching: %s' % get_src_for_loc(destloc))
if annotator:
notes = annotator.get_notes(t)
for note in notes:
if note.loc and note.loc == srcloc:
- gcc.inform(note.loc, note.msg)
+ report.add_inform(note.loc, note.msg)
class Reporter:
"""
@@ -99,12 +99,14 @@ class Reporter:
def make_error(self, fun, loc, msg):
assert isinstance(fun, gcc.Function)
assert isinstance(loc, gcc.Location)
- gcc.error(loc, msg)
self._got_errors = True
err = Report(fun, loc, msg)
self.reports.append(err)
+
+ err.add_error(loc, msg)
+
return err
def make_debug_dump(self, fun, loc, msg):
@@ -131,6 +133,28 @@ class Reporter:
with open(filename, 'w') as f:
f.write(html)
+ def flush(self):
+ for r in self.reports:
+ r.flush()
+
+class SavedDiagnostic:
+ """
+ A saved GCC diagnostic, which we can choose to emit or suppress at a later
+ date
+ """
+ def __init__(self, loc, msg):
+ assert isinstance(loc, gcc.Location)
+ assert isinstance(msg, str)
+ self.loc = loc
+ self.msg = msg
+
+class SavedError(SavedDiagnostic):
+ def flush(self):
+ gcc.error(self.loc, self.msg)
+
+class SavedInform(SavedDiagnostic):
+ def flush(self):
+ gcc.inform(self.loc, self.msg)
class Report:
"""
@@ -143,14 +167,33 @@ class Report:
self.trace = None
self._annotators = {}
self.notes = []
+ self._saved_diagnostics = [] # list of SavedDiagnostic
+
+ def add_error(self, loc, msg):
+ # Add a gcc.error() to the buffer of GCC diagnostics
+ self._saved_diagnostics.append(SavedError(loc, msg))
+
+ def add_inform(self, loc, msg):
+ # Add a gcc.inform() to the buffer of GCC diagnostics
+ self._saved_diagnostics.append(SavedInform(loc, msg))
+
+ def flush(self):
+ # Flush the buffer of GCC diagnostics
+ for d in self._saved_diagnostics:
+ d.flush()
def add_trace(self, trace, annotator=None):
self.trace = trace
self._annotators[trace] = annotator
- describe_trace(trace, self.fun, annotator)
+ describe_trace(trace, self, annotator)
def add_note(self, loc, msg):
- gcc.inform(loc, msg)
+ """
+ Add a note at the given location. This is added both to
+ the buffer of GCC diagnostics, and also to a saved list that's
+ available to the HTML renderer.
+ """
+ self.add_inform(loc, msg)
note = Note(loc, msg)
self.notes.append(note)
return note
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 9e5d3e1..4165d34 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -2207,6 +2207,7 @@ def check_refcounts(fun, dump_traces=False, show_traces=False,
'Debug dump of trace %i' % i)
r.add_trace(trace, DebugAnnotator())
rep.dump_html(fun, filename)
+ rep.flush()
gcc.inform(fun.start,
('graphical debug report for function %r written out to %r'
% (fun.decl.name, filename)))
@@ -2383,6 +2384,10 @@ def check_refcounts(fun, dump_traces=False, show_traces=False,
# (all traces analysed)
+ # Flush the reporter's messages, which will actually emit gcc errors and
+ # warnings (if any):
+ rep.flush()
+
if rep.got_errors():
filename = ('%s.%s-refcount-errors.html'
% (gcc.get_dump_base_name(), fun.decl.name))
12 years, 6 months
[gcc-python-plugin] use cpychecker's custom attribute to associate run-time and compile-time type information
by David Hugh Malcolm
commit 223bccfc32603be851e0d9b62aee8fd999c329eb
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Nov 23 17:48:30 2011 -0500
use cpychecker's custom attribute to associate run-time and compile-time type information
Use the custom attribute defined in ff3eb4ac1f666b7b3a23da2d529659356098a5db
when defining the wrapper types in the plugin itself, so that when recompiling
the plugin with cpychecker, the checker can use this information.
Doing so successfully suppresses false warnings from the checker.
gcc-python.h | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
---
diff --git a/gcc-python.h b/gcc-python.h
index 99d552b..72c0c59 100644
--- a/gcc-python.h
+++ b/gcc-python.h
@@ -47,6 +47,13 @@
#define CPYCHECKER_STEALS_REFERENCE_TO_ARG(n)
#endif
+#if defined(WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE)
+ #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) \
+ __attribute__((cpychecker_type_object_for_typedef(typename)))
+#else
+ #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename)
+#endif
+
/*
Macro DECLARE_SIMPLE_WRAPPER():
ARG_structname:
@@ -79,7 +86,8 @@
extern PyObject * \
gcc_python_make_wrapper_##ARG_typename(ARG_wrappedtype ARG_fieldname); \
\
- extern PyTypeObject ARG_typeobj; \
+ extern PyTypeObject ARG_typeobj \
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(#ARG_structname); \
\
/* end of macro */
12 years, 6 months
[gcc-python-plugin] cpychecker: provide a way to associate run-time and compile-time type information
by David Hugh Malcolm
commit ff3eb4ac1f666b7b3a23da2d529659356098a5db
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Nov 23 17:42:24 2011 -0500
cpychecker: provide a way to associate run-time and compile-time type information
Provide a new attribute:
__attribute__((cpychecker_type_object_for_typedef(typename)))
for associating a PyTypeObject instance with a C typedef.
The checker can then use this when verifying PyArg_ParseTuple with the "O!"
format code
docs/cpychecker.rst | 73 ++++++++++++++++++++
libcpychecker/PyArg_ParseTuple.py | 2 +-
libcpychecker/attributes.py | 17 +++++
libcpychecker/types.py | 4 +
.../PyArg_ParseTuple/code_O_bang/input.c | 25 ++++++-
.../PyArg_ParseTuple/code_O_bang/stderr.txt | 10 ++--
6 files changed, 122 insertions(+), 9 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index 6b548f8..684d653 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -333,6 +333,79 @@ call to PyArg_ParseTupleAndKeywords is not NULL-terminated.
regular functions against `int` and those in the modified functions against
`Py_ssize_t`.
+Associating PyTypeObject instances with compile-time types
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The "O!" format code to ``PyArg_ParseTuple`` takes a ``PyTypeObject`` followed
+by the address of an object. This second argument can point to a
+``PyObject*``, but it can also point to a pointer to a derived class.
+
+For example, CPython's own implementation contains code like this:
+
+.. code-block:: c
+
+ static PyObject *
+ unicodedata_decomposition(PyObject *self, PyObject *args)
+ {
+ PyUnicodeObject *v;
+
+ /* ...snip... */
+
+ if (!PyArg_ParseTuple(args, "O!:decomposition",
+ &PyUnicode_Type, &v))
+
+ /* ...etc... */
+
+in which the input argument is written out into the ``PyUnicodeObject*``,
+provided that it is indeed a unicode instance.
+
+When the cpychecker verifies the types in this format string it verifies that
+the run-time type of the ``PyTypeObject`` matches the compile-time type
+(``PyUnicodeObject *``). It is able to do this since it contains hard-coded
+associations between these worlds for all of Python's built-in types: for the
+above case, it "knows" that ``PyUnicode_Type`` is associated with
+``PyUnicodeObject``.
+
+If you need to provide a similar association for an extension type, the checker
+provides a custom GCC attribute:
+
+.. code-block:: c
+
+ __attribute__((cpychecker_type_object_for_typedef(typename)))
+
+which can be used to mark PyTypeObject instance, giving the name of the typedef
+that PyObject instances of that type can be safely cast to.
+
+.. code-block:: c
+
+ /* The checker automatically defines this preprocessor name when creating
+ the custom attribute: */
+ #if defined(WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE)
+ #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) \
+ __attribute__((cpychecker_type_object_for_typedef(typename)))
+ #else
+ /* This handles the case where we're compiling with a "vanilla"
+ compiler that doesn't supply this attribute: */
+ #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename)
+ #endif
+
+ /* Define some PyObject subclass, as both a struct and a typedef */
+ struct OurObjectStruct {
+ PyObject_HEAD
+ /* other fields */
+ };
+ typedef struct OurObjectStruct OurExtensionObject;
+
+ /*
+ Declare the PyTypeObject, using the custom attribute to associate it with
+ the typedef above:
+ */
+ extern PyTypeObject UserDefinedExtension_Type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("OurExtensionObject");
+
+Given the above, the checker will associate the given ``PyTypeObject`` with the
+given typedef.
+
Limitations and caveats
-----------------------
diff --git a/libcpychecker/PyArg_ParseTuple.py b/libcpychecker/PyArg_ParseTuple.py
index 274f694..90cee00 100644
--- a/libcpychecker/PyArg_ParseTuple.py
+++ b/libcpychecker/PyArg_ParseTuple.py
@@ -162,7 +162,7 @@ class TypeCheckResultType(AwkwardType):
other_type = self.typecheck.get_other_type()
if other_type:
return ('%s (based on PyTypeObject: %r) or %s'
- % (describe_type(other_type.pointer),
+ % (describe_type(other_type.pointer.pointer),
self.typecheck.typeobject.name,
describe_type(self.base_type)))
else:
diff --git a/libcpychecker/attributes.py b/libcpychecker/attributes.py
index b87cc8d..d234438 100644
--- a/libcpychecker/attributes.py
+++ b/libcpychecker/attributes.py
@@ -17,6 +17,7 @@
import gcc
from gccutils import check_isinstance
+from libcpychecker.types import register_type_object
# Recorded attribute data:
fnnames_returning_borrowed_refs = set()
@@ -62,3 +63,19 @@ def register_our_attributes():
False, False, False,
attribute_callback_for_steals_reference_to_arg)
gcc.define_macro('WITH_CPYCHECKER_STEALS_REFERENCE_TO_ARG_ATTRIBUTE')
+
+ # Handler for __attribute__((cpychecker_type_object_for_struct(type)))
+ # and #ifdef WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE
+ def attribute_callback_type_object_for_typedef(*args):
+ if 0:
+ print('attribute_callback_type_object_for_typedef(%r)' % (args, ))
+ check_isinstance(args[0], gcc.VarDecl)
+ check_isinstance(args[1], gcc.StringCst)
+ typedef_name = args[1].constant
+ register_type_object(args[0], typedef_name)
+
+ gcc.register_attribute('cpychecker_type_object_for_typedef',
+ 1, 1,
+ False, False, False,
+ attribute_callback_type_object_for_typedef)
+ gcc.define_macro('WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE')
diff --git a/libcpychecker/types.py b/libcpychecker/types.py
index a450a92..598e930 100644
--- a/libcpychecker/types.py
+++ b/libcpychecker/types.py
@@ -107,3 +107,7 @@ def get_type_for_typeobject(typeobject):
type_name = type_dict[typeobject.name]
return get_global_typedef(type_name)
+
+def register_type_object(typeobject, typedef):
+ check_isinstance(typeobject, gcc.VarDecl)
+ type_dict[typeobject.name] = typedef
diff --git a/tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c
index 5836715..591fde6 100644
--- a/tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c
+++ b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c
@@ -60,8 +60,21 @@ extern PyTypeObject *unknown_type_obj_ptr;
Example of an extension type, where cpychecker has no way to associate
the PyTypeObject with, say, "struct ExtensionObject"
*/
+PyTypeObject UnknownExtension_Type = {};
-PyTypeObject Extension_Type = {};
+/*
+ Example of a PyTypeObject where the user supplies the value at compile time:
+*/
+typedef struct UserDefinedObject UserDefinedObject;
+#if WITH_CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF_ATTRIBUTE
+ #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename) \
+ __attribute__((cpychecker_type_object_for_typedef(typename)))
+#else
+ #define CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF(typename)
+#endif
+PyTypeObject UserDefinedExtension_Type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF("UserDefinedObject")
+ = {};
PyObject *
handle_subclasses(PyObject *self, PyObject *args)
@@ -72,8 +85,9 @@ handle_subclasses(PyObject *self, PyObject *args)
PyLongObject *long_obj;
struct UnknownObject *unknown_obj;
struct ExtensionObject *extension_obj;
+ UserDefinedObject *user_defined_obj;
- if (!PyArg_ParseTuple(args, "O!O!O!O!O!O!",
+ if (!PyArg_ParseTuple(args, "O!O!O!O!O!O!O!",
/* This is correct, PyCode_Type -> PyCodeObject */
&PyCode_Type, &code_obj,
@@ -94,7 +108,12 @@ handle_subclasses(PyObject *self, PyObject *args)
/* This must report a warning: we don't know about
the association between Extension_Type and
struct ExtensionObject: */
- &Extension_Type, &extension_obj)) {
+ &UnknownExtension_Type, &extension_obj,
+
+ /* This is correct, the user has explicitly
+ associated the typeobj with the struct: */
+ &UserDefinedExtension_Type, &user_defined_obj
+ )) {
return NULL;
}
diff --git a/tests/cpychecker/PyArg_ParseTuple/code_O_bang/stderr.txt b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/stderr.txt
index 3f19795..a19f179 100644
--- a/tests/cpychecker/PyArg_ParseTuple/code_O_bang/stderr.txt
+++ b/tests/cpychecker/PyArg_ParseTuple/code_O_bang/stderr.txt
@@ -1,19 +1,19 @@
tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c: In function 'handle_subclasses':
-tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:72:19: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!" [-fpermissive]
+tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:85:19: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!O!" [-fpermissive]
argument 10 ("gcc.VarDecl('long_obj')") had type
"struct PyLongObject *"
but was expecting
- "struct PyUnicodeObject *" (based on PyTypeObject: 'PyUnicode_Type') or "struct PyObject * *"
+ "struct PyUnicodeObject * *" (based on PyTypeObject: 'PyUnicode_Type') or "struct PyObject * *"
for format code "O!"
-tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:76:26: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!" [-fpermissive]
+tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:90:26: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!O!" [-fpermissive]
argument 12 ("&unknown_obj") had type
"struct UnknownObject * *"
but was expecting
""struct PyObject * *"" (unable to determine relevant PyTypeObject)
for format code "O!"
-tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:76:26: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!" [-fpermissive]
+tests/cpychecker/PyArg_ParseTuple/code_O_bang/input.c:90:26: error: Mismatching type in call to PyArg_ParseTuple with format code "O!O!O!O!O!O!O!" [-fpermissive]
argument 14 ("&extension_obj") had type
"struct ExtensionObject * *"
but was expecting
- ""struct PyObject * *"" (unfamiliar with PyTypeObject: 'Extension_Type')
+ ""struct PyObject * *"" (unfamiliar with PyTypeObject: 'UnknownExtension_Type')
for format code "O!"
12 years, 6 months
[gcc-python-plugin] cpychecker: implement PyObject_Call
by David Hugh Malcolm
commit 0e3fdd0ba3b3b5d402b476847ab6b0dba8a26f7c
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Nov 23 15:44:18 2011 -0500
cpychecker: implement PyObject_Call
libcpychecker/refcounts.py | 10 ++++++++++
1 files changed, 10 insertions(+), 0 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 453c087..9e5d3e1 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1481,6 +1481,16 @@ class CPython(Facet):
return [t_success, t_failure]
+ 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:
+ # PyObject *
+ # PyObject_Call(PyObject *func, PyObject *arg, PyObject *kw)
+ # "func" and "args" must not be NULL, but "kw" can be:
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_o)
+ self.state.raise_any_null_ptr_func_arg(stmt, 1, v_args)
+ return self.make_transitions_for_new_ref_or_fail(stmt)
+
def impl_PyObject_CallMethod(self, stmt, v_o, v_name, v_format, *args):
# http://docs.python.org/c-api/object.html#PyObject_CallMethod
#
12 years, 6 months
[gcc-python-plugin] cpychecker: use 1-based argument indices when warning about NULL function arguments
by David Hugh Malcolm
commit ca147524ce6df6f1d1ab91b0300825bb1af0b382
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Nov 23 15:36:54 2011 -0500
cpychecker: use 1-based argument indices when warning about NULL function arguments
Update NullPtrArgument.__str__ so that error messages refer to argument indices
starting at index 1, rather than index 0. Internally, the indices are still
0-based, though.
libcpychecker/absinterp.py | 8 ++++++--
.../refcounts/PyDict_SetItem/incorrect/stderr.txt | 6 +++---
.../refcounts/PyDict_SetItem/incorrect/stdout.txt | 6 +++---
.../PyObject_HasAttrString/incorrect/stderr.txt | 2 +-
.../PyObject_HasAttrString/incorrect/stdout.txt | 2 +-
.../PyString_AsString/incorrect/stderr.txt | 2 +-
.../PyString_AsString/incorrect/stdout.txt | 2 +-
.../refcounts/PyTuple_Size/incorrect/stderr.txt | 2 +-
.../refcounts/PyTuple_Size/incorrect/stdout.txt | 2 +-
9 files changed, 18 insertions(+), 14 deletions(-)
---
diff --git a/libcpychecker/absinterp.py b/libcpychecker/absinterp.py
index 22904de..464efc6 100644
--- a/libcpychecker/absinterp.py
+++ b/libcpychecker/absinterp.py
@@ -811,16 +811,18 @@ class NullPtrArgument(PredictedValueError):
PredictedValueError.__init__(self, state, stmt.args[idx], ptr, isdefinite)
self.stmt = stmt
self.idx = idx
+ # this is a 0-based index; it is changed to a 1-based index when
+ # printed
def __str__(self):
if self.isdefinite:
return ('calling %s with NULL (%s) as argument %i at %s'
% (self.stmt.fn, self.expr,
- self.idx, self.state.loc.get_stmt().loc))
+ self.idx + 1, self.state.loc.get_stmt().loc))
else:
return ('possibly calling %s with NULL (%s) as argument %i at %s'
% (self.stmt.fn, self.expr,
- self.idx, self.state.loc.get_stmt().loc))
+ self.idx + 1, self.state.loc.get_stmt().loc))
@@ -1656,6 +1658,8 @@ class State(object):
raise NullPtrDereference(self, expr, ptr, isdefinite)
def raise_any_null_ptr_func_arg(self, stmt, idx, ptr):
+ # idx is the 0-based index of the argument
+
check_isinstance(stmt, gcc.Gimple)
check_isinstance(idx, int)
check_isinstance(ptr, AbstractValue)
diff --git a/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt
index e4df254..3f2b7fc 100644
--- a/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt
+++ b/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stderr.txt
@@ -1,17 +1,17 @@
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c: In function 'test':
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: error: calling PyDict_SetItem with NULL (gcc.VarDecl('value')) as argument 2 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
+tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: error: calling PyDict_SetItem with NULL (gcc.VarDecl('value')) as argument 3 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:33:10: note: when PyDict_New() succeeds at: dict = PyDict_New();
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:34:8: note: taking False path at: if (!dict) {
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: reaching: key = PyLong_FromLong(500);
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: when PyLong_FromLong() succeeds at: key = PyLong_FromLong(500);
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:39:11: note: when PyLong_FromLong() fails at: value = PyLong_FromLong(1000);
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 1 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
+tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 2 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:33:10: note: when PyDict_New() succeeds at: dict = PyDict_New();
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:34:8: note: taking False path at: if (!dict) {
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: reaching: key = PyLong_FromLong(500);
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: when PyLong_FromLong() fails at: key = PyLong_FromLong(500);
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:39:11: note: when PyLong_FromLong() succeeds at: value = PyLong_FromLong(1000);
-tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 1 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
+tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45:29: error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 2 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:33:10: note: when PyDict_New() succeeds at: dict = PyDict_New();
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:34:8: note: taking False path at: if (!dict) {
tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:38:9: note: reaching: key = PyLong_FromLong(500);
diff --git a/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stdout.txt
index ee667e7..b17bd8d 100644
--- a/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stdout.txt
+++ b/tests/cpychecker/refcounts/PyDict_SetItem/incorrect/stdout.txt
@@ -545,7 +545,7 @@ Trace 12:
'when PyLong_FromLong() succeeds'
'when PyLong_FromLong() fails'
error: NullPtrArgument()
- error: calling PyDict_SetItem with NULL (gcc.VarDecl('value')) as argument 2 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
+ error: calling PyDict_SetItem with NULL (gcc.VarDecl('value')) as argument 3 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/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')")
@@ -576,7 +576,7 @@ Trace 13:
'when PyLong_FromLong() fails'
'when PyLong_FromLong() succeeds'
error: NullPtrArgument()
- error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 1 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
+ error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 2 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/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')")
@@ -607,7 +607,7 @@ Trace 14:
'when PyLong_FromLong() fails'
'when PyLong_FromLong() fails'
error: NullPtrArgument()
- error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 1 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/input.c:45
+ error: calling PyDict_SetItem with NULL (gcc.VarDecl('key')) as argument 2 at tests/cpychecker/refcounts/PyDict_SetItem/incorrect/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')")
diff --git a/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stderr.txt
index 6682ce5..2ba1980 100644
--- a/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stderr.txt
+++ b/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stderr.txt
@@ -1,3 +1,3 @@
tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c: In function 'test':
-tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c:29:50: error: calling PyObject_HasAttrString with NULL (0B) as argument 1 at tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c:29
+tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c:29:50: error: calling PyObject_HasAttrString with NULL (0B) as argument 2 at tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c:29
tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c:28:1: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stdout.txt
index a274f66..853ac7b 100644
--- a/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stdout.txt
+++ b/tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/stdout.txt
@@ -1,7 +1,7 @@
Trace 0:
Transitions:
error: NullPtrArgument()
- error: calling PyObject_HasAttrString with NULL (0B) as argument 1 at tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c:29
+ error: calling PyObject_HasAttrString with NULL (0B) as argument 2 at tests/cpychecker/refcounts/PyObject_HasAttrString/incorrect/input.c:29
Region("region-for-arg-gcc.ParmDecl('self')"):
repr(): Region("region-for-arg-gcc.ParmDecl('self')")
str(): Region("region-for-arg-gcc.ParmDecl('self')")
diff --git a/tests/cpychecker/refcounts/PyString_AsString/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyString_AsString/incorrect/stderr.txt
index d3d42d4..28f69bc 100644
--- a/tests/cpychecker/refcounts/PyString_AsString/incorrect/stderr.txt
+++ b/tests/cpychecker/refcounts/PyString_AsString/incorrect/stderr.txt
@@ -1,4 +1,4 @@
tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c: In function 'test':
-tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:38:33: error: calling PyString_AsString with NULL (gcc.VarDecl('repr_args')) as argument 0 at tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:38
+tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:38:33: error: calling PyString_AsString with NULL (gcc.VarDecl('repr_args')) as argument 1 at tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:38
tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:31:15: note: when PyObject_Repr() fails at: repr_args = PyObject_Repr(args);
tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:27:1: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyString_AsString/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyString_AsString/incorrect/stdout.txt
index 9c976b1..adcbb2f 100644
--- a/tests/cpychecker/refcounts/PyString_AsString/incorrect/stdout.txt
+++ b/tests/cpychecker/refcounts/PyString_AsString/incorrect/stdout.txt
@@ -120,7 +120,7 @@ Trace 4:
Transitions:
'when PyObject_Repr() fails'
error: NullPtrArgument()
- error: calling PyString_AsString with NULL (gcc.VarDecl('repr_args')) as argument 0 at tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:38
+ error: calling PyString_AsString with NULL (gcc.VarDecl('repr_args')) as argument 1 at tests/cpychecker/refcounts/PyString_AsString/incorrect/input.c:38
Region("region-for-arg-gcc.ParmDecl('self')"):
repr(): Region("region-for-arg-gcc.ParmDecl('self')")
str(): Region("region-for-arg-gcc.ParmDecl('self')")
diff --git a/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stderr.txt b/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stderr.txt
index e151fcd..85cbedd 100644
--- a/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stderr.txt
+++ b/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stderr.txt
@@ -1,4 +1,4 @@
tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c: In function 'test':
-tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c:37:17: error: calling PyTuple_Size with NULL (gcc.VarDecl('dict')) as argument 0 at tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c:37
+tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c:37:17: error: calling PyTuple_Size with NULL (gcc.VarDecl('dict')) as argument 1 at tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c:37
tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c:30:10: note: when PyDict_New() fails at: dict = PyDict_New();
tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c:28:1: note: graphical error report for function 'test' written out to 'tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c.test-refcount-errors.html'
diff --git a/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stdout.txt b/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stdout.txt
index 4057eb3..0361323 100644
--- a/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stdout.txt
+++ b/tests/cpychecker/refcounts/PyTuple_Size/incorrect/stdout.txt
@@ -48,7 +48,7 @@ Trace 2:
Transitions:
'when PyDict_New() fails'
error: NullPtrArgument()
- error: calling PyTuple_Size with NULL (gcc.VarDecl('dict')) as argument 0 at tests/cpychecker/refcounts/PyTuple_Size/incorrect/input.c:37
+ error: calling PyTuple_Size with NULL (gcc.VarDecl('dict')) as argument 1 at tests/cpychecker/refcounts/PyTuple_Size/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')")
12 years, 6 months
[gcc-python-plugin] docs: markup all usage of PyObject* in the text
by David Hugh Malcolm
commit c2029695f88db0ce68991818f8969cc5dbbe42ad
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Wed Nov 23 12:41:42 2011 -0500
docs: markup all usage of PyObject* in the text
docs/cpychecker.rst | 18 +++++++++---------
1 files changed, 9 insertions(+), 9 deletions(-)
---
diff --git a/docs/cpychecker.rst b/docs/cpychecker.rst
index 5ced382..6b548f8 100644
--- a/docs/cpychecker.rst
+++ b/docs/cpychecker.rst
@@ -57,9 +57,9 @@ directory).
Reference-count checking
------------------------
The checker attempts to analyze all possible paths through each function,
-tracking the various PyObject* objects encountered.
+tracking the various ``PyObject*`` objects encountered.
-For each path through the function and PyObject*, it determines what the
+For each path through the function and ``PyObject*``, it determines what the
reference count ought to be at the end of the function, issuing an error report
for any that are incorrect.
@@ -142,14 +142,14 @@ the JavaScript it uses via URLs to the web).
Assumptions and configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-For any function returning a PyObject*, it assumes that the PyObject* should be
-either a new reference to an object, or NULL (with an exception set) - the
-function's caller should "own" a reference to that object. For all
-other PyObject*, it assumes that there should be no references owned by the
+For any function returning a ``PyObject*``, it assumes that the ``PyObject*``
+should be either a new reference to an object, or NULL (with an exception set)
+- the function's caller should "own" a reference to that object. For all
+other ``PyObject*``, it assumes that there should be no references owned by the
function when the function terminates.
It will assume this behavior for any function (or call through a function
-pointer) that returns a PyObject*.
+pointer) that returns a ``PyObject*``.
It is possible to override this behavior using custom compiler attributes as
follows:
@@ -238,7 +238,7 @@ error reports for code that appears to not gracefully handle an error.
(TODO: show example)
-As noted above, it assumes that any function that returns a PyObject* can
+As noted above, it assumes that any function that returns a ``PyObject*`` can
return can either NULL (setting an exception), or a new reference. It knows
about much of the other parts of the CPython C API, including many other
functions that can fail.
@@ -272,7 +272,7 @@ The checker will emit errors for various events:
Errors in exception-handling
----------------------------
The checker keeps track of the per-thread exception state. It will issue a
-warning about any paths through functions returning a PyObject* that return
+warning about any paths through functions returning a ``PyObject*`` that return
NULL for which the per-thread exception state has not been set::
input.c: In function 'test':
12 years, 6 months
[gcc-python-plugin] remove "is_py3k()" conditionals on methods
by David Hugh Malcolm
commit 49ed288643b62eb977c92b4b2bc6047a3a950b64
Author: David Malcolm <dmalcolm(a)redhat.com>
Date: Tue Nov 22 15:03:56 2011 -0500
remove "is_py3k()" conditionals on methods
This is a revert of c87948bcbd89504991defeabeec36975d909fcb1
is_py3k() returns True early-on in the compile, regardless of whether we're
compiling Python 2 or Python 3 code, and hence the conditionalization of the
PyInt_* and PyString_* hooks was failing: they were being erroneously
eliminated for Python 2.
Remove the conditionals to fix the test suite on Python 2
libcpychecker/refcounts.py | 286 +++++++++++++++++++++-----------------------
1 files changed, 138 insertions(+), 148 deletions(-)
---
diff --git a/libcpychecker/refcounts.py b/libcpychecker/refcounts.py
index 6c80ab3..453c087 100644
--- a/libcpychecker/refcounts.py
+++ b/libcpychecker/refcounts.py
@@ -1142,67 +1142,62 @@ class CPython(Facet):
########################################################################
# Py_Int*
########################################################################
- if is_py3k():
- # If we're compiling Python 3 code, the PyInt_ entrypoints no longer
- # exist:
- pass
- else:
- def impl_PyInt_AsLong(self, stmt, v_op):
- # Declared in intobject.h as:
- # PyAPI_FUNC(long) PyInt_AsLong(PyObject *);
- # Defined in Objects/intobject.c
- #
- # http://docs.python.org/c-api/int.html#PyInt_AsLong
-
- # Can fail (gracefully) with NULL, and with non-int objects
-
- returntype = stmt.fn.type.dereference.type
-
- if self.object_ptr_has_global_ob_type(v_op, 'PyInt_Type'):
- # We know it's a PyIntObject; the call will succeed:
- # FIXME: cast:
- v_ob_ival = self.state.get_value_of_field_by_region(v_op.region,
- 'ob_ival')
- t_success = self.state.mktrans_assignment(stmt.lhs,
- v_ob_ival,
- 'PyInt_AsLong() returns ob_ival')
- return [t_success]
+ def impl_PyInt_AsLong(self, stmt, v_op):
+ # Declared in intobject.h as:
+ # PyAPI_FUNC(long) PyInt_AsLong(PyObject *);
+ # Defined in Objects/intobject.c
+ #
+ # http://docs.python.org/c-api/int.html#PyInt_AsLong
- # We don't know if it's a PyIntObject (or subclass); the call could
- # fail:
+ # Can fail (gracefully) with NULL, and with non-int objects
+
+ returntype = stmt.fn.type.dereference.type
+
+ if self.object_ptr_has_global_ob_type(v_op, 'PyInt_Type'):
+ # We know it's a PyIntObject; the call will succeed:
+ # FIXME: cast:
+ v_ob_ival = self.state.get_value_of_field_by_region(v_op.region,
+ 'ob_ival')
t_success = self.state.mktrans_assignment(stmt.lhs,
- UnknownValue.make(returntype, stmt.loc),
- 'PyInt_AsLong() succeeds')
- t_failure = self.state.mktrans_assignment(stmt.lhs,
- ConcreteValue(returntype, stmt.loc, -1),
- 'PyInt_AsLong() fails')
- t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc)
- return [t_success, t_failure]
-
- def impl_PyInt_FromLong(self, stmt, v_ival):
- # Declared in intobject.h as:
- # PyAPI_FUNC(PyObject *) PyInt_FromLong(long);
- # Defined in Objects/intobject.c
- #
- # CPython2 shares objects for integers in the range:
- # -5 <= ival < 257
- # within intobject.c's "small_ints" array and these are preallocated
- # by _PyInt_Init(). Thus, for these values, we know that the call
- # cannot fail
-
- r_newobj, t_success, t_failure = self.object_ctor(stmt,
- 'PyIntObject',
- 'PyInt_Type')
- # Set ob_size:
- r_ob_size = t_success.dest.make_field_region(r_newobj, 'ob_ival')
- t_success.dest.value_for_region[r_ob_size] = v_ival
-
- if isinstance(v_ival, ConcreteValue):
- if v_ival.value >= -5 and v_ival.value < 257:
- # We know that failure isn't possible:
- return [t_success]
-
- return [t_success, t_failure]
+ v_ob_ival,
+ 'PyInt_AsLong() returns ob_ival')
+ return [t_success]
+
+ # We don't know if it's a PyIntObject (or subclass); the call could
+ # fail:
+ t_success = self.state.mktrans_assignment(stmt.lhs,
+ UnknownValue.make(returntype, stmt.loc),
+ 'PyInt_AsLong() succeeds')
+ t_failure = self.state.mktrans_assignment(stmt.lhs,
+ ConcreteValue(returntype, stmt.loc, -1),
+ 'PyInt_AsLong() fails')
+ t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc)
+ return [t_success, t_failure]
+
+ def impl_PyInt_FromLong(self, stmt, v_ival):
+ # Declared in intobject.h as:
+ # PyAPI_FUNC(PyObject *) PyInt_FromLong(long);
+ # Defined in Objects/intobject.c
+ #
+ # CPython2 shares objects for integers in the range:
+ # -5 <= ival < 257
+ # within intobject.c's "small_ints" array and these are preallocated
+ # by _PyInt_Init(). Thus, for these values, we know that the call
+ # cannot fail
+
+ r_newobj, t_success, t_failure = self.object_ctor(stmt,
+ 'PyIntObject',
+ 'PyInt_Type')
+ # Set ob_size:
+ r_ob_size = t_success.dest.make_field_region(r_newobj, 'ob_ival')
+ t_success.dest.value_for_region[r_ob_size] = v_ival
+
+ if isinstance(v_ival, ConcreteValue):
+ if v_ival.value >= -5 and v_ival.value < 257:
+ # We know that failure isn't possible:
+ return [t_success]
+
+ return [t_success, t_failure]
########################################################################
# PyList_*
@@ -1635,97 +1630,92 @@ class CPython(Facet):
########################################################################
# PyString_*
########################################################################
- if is_py3k():
- # If we're compiling Python 3 code, the PyString_ entrypoints no longer
- # exist:
- pass
- else:
- def impl_PyString_AsString(self, stmt, v_op):
- # Declared in stringobject.h as:
- # PyAPI_FUNC(char *) PyString_AsString(PyObject *);
- # Implemented in Objects/stringobject.c
- #
- # http://docs.python.org/c-api/string.html#PyString_AsString
- #
- # With PyStringObject and their subclasses, it returns
- # ((PyStringObject *)op) -> ob_sval
- # With other classes, this call can fail
-
- # It will segfault if called with NULL, since it uses PyString_Check,
- # which reads through the object's ob_type:
- self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op)
-
- returntype = stmt.fn.type.dereference.type
-
- if self.object_ptr_has_global_ob_type(v_op, 'PyString_Type'):
- # We know it's a PyStringObject; the call will succeed:
- # FIXME: cast:
- r_ob_sval = self.state.make_field_region(v_op.region, 'ob_sval')
- v_result = PointerToRegion(returntype, stmt.loc, r_ob_sval)
- t_success = self.state.mktrans_assignment(stmt.lhs,
- v_result,
- 'PyString_AsString() returns ob_sval')
- return [t_success]
+ def impl_PyString_AsString(self, stmt, v_op):
+ # Declared in stringobject.h as:
+ # PyAPI_FUNC(char *) PyString_AsString(PyObject *);
+ # Implemented in Objects/stringobject.c
+ #
+ # http://docs.python.org/c-api/string.html#PyString_AsString
+ #
+ # With PyStringObject and their subclasses, it returns
+ # ((PyStringObject *)op) -> ob_sval
+ # With other classes, this call can fail
+
+ # It will segfault if called with NULL, since it uses PyString_Check,
+ # which reads through the object's ob_type:
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_op)
+
+ returntype = stmt.fn.type.dereference.type
- # We don't know if it's a PyStringObject (or subclass); the call could
- # fail:
- r_nonnull = self.state.make_heap_region('buffer from PyString_AsString()', stmt)
- v_success = PointerToRegion(returntype, stmt.loc, r_nonnull)
+ if self.object_ptr_has_global_ob_type(v_op, 'PyString_Type'):
+ # We know it's a PyStringObject; the call will succeed:
+ # FIXME: cast:
+ r_ob_sval = self.state.make_field_region(v_op.region, 'ob_sval')
+ v_result = PointerToRegion(returntype, stmt.loc, r_ob_sval)
t_success = self.state.mktrans_assignment(stmt.lhs,
- v_success,
- 'when PyString_AsString() succeeds')
- t_failure = self.state.mktrans_assignment(stmt.lhs,
- ConcreteValue(returntype, stmt.loc, 0),
- 'when PyString_AsString() fails')
- t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc)
- return [t_success, t_failure]
-
- def impl_PyString_FromFormat(self, stmt, v_fmt, *v_args):
- # Declared in stringobject.h as:
- # PyAPI_FUNC(PyObject *) PyString_FromFormat(const char*, ...)
- # Py_GCC_ATTRIBUTE((format(printf, 1, 2)));
- # Returns a new reference
- # http://docs.python.org/c-api/string.html#PyString_FromFormat
- #
- # (We do not yet check that the format string matches the types of the
- # varargs)
- r_newobj, t_success, t_failure = self.object_ctor(stmt,
- 'PyStringObject',
- 'PyString_Type')
- return [t_success, t_failure]
-
- def impl_PyString_FromString(self, stmt, v_str):
- # Declared in stringobject.h as:
- # PyAPI_FUNC(PyObject *) PyString_FromString(const char *);
- #
- # http://docs.python.org/c-api/string.html#PyString_FromString
- #
-
- # The input _must_ be non-NULL; it is not checked:
- self.state.raise_any_null_ptr_func_arg(stmt, 0, v_str)
-
- r_newobj, t_success, t_failure = self.object_ctor(stmt,
- 'PyStringObject',
- 'PyString_Type')
- return [t_success, t_failure]
-
- def impl_PyString_FromStringAndSize(self, stmt, v_str, v_size):
- # Declared in stringobject.h as:
- # PyAPI_FUNC(PyObject *) PyString_FromStringAndSize(const char *, Py_ssize_t);
- #
- # http://docs.python.org/c-api/string.html#PyString_FromStringAndSize
- #
- # Defined in Objects/stringobject.c:
- # # PyObject *
- # PyString_FromStringAndSize(const char *str, Py_ssize_t size)
-
- # v_str, v_size = self.state.eval_stmt_args(stmt)
- # (the input can legitimately be NULL)
-
- r_newobj, t_success, t_failure = self.object_ctor(stmt,
- 'PyStringObject',
- 'PyString_Type')
- return [t_success, t_failure]
+ v_result,
+ 'PyString_AsString() returns ob_sval')
+ return [t_success]
+
+ # We don't know if it's a PyStringObject (or subclass); the call could
+ # fail:
+ r_nonnull = self.state.make_heap_region('buffer from PyString_AsString()', stmt)
+ v_success = PointerToRegion(returntype, stmt.loc, r_nonnull)
+ t_success = self.state.mktrans_assignment(stmt.lhs,
+ v_success,
+ 'when PyString_AsString() succeeds')
+ t_failure = self.state.mktrans_assignment(stmt.lhs,
+ ConcreteValue(returntype, stmt.loc, 0),
+ 'when PyString_AsString() fails')
+ t_failure.dest.cpython.set_exception('PyExc_MemoryError', stmt.loc)
+ return [t_success, t_failure]
+
+ def impl_PyString_FromFormat(self, stmt, v_fmt, *v_args):
+ # Declared in stringobject.h as:
+ # PyAPI_FUNC(PyObject *) PyString_FromFormat(const char*, ...)
+ # Py_GCC_ATTRIBUTE((format(printf, 1, 2)));
+ # Returns a new reference
+ # http://docs.python.org/c-api/string.html#PyString_FromFormat
+ #
+ # (We do not yet check that the format string matches the types of the
+ # varargs)
+ r_newobj, t_success, t_failure = self.object_ctor(stmt,
+ 'PyStringObject',
+ 'PyString_Type')
+ return [t_success, t_failure]
+
+ def impl_PyString_FromString(self, stmt, v_str):
+ # Declared in stringobject.h as:
+ # PyAPI_FUNC(PyObject *) PyString_FromString(const char *);
+ #
+ # http://docs.python.org/c-api/string.html#PyString_FromString
+ #
+
+ # The input _must_ be non-NULL; it is not checked:
+ self.state.raise_any_null_ptr_func_arg(stmt, 0, v_str)
+
+ r_newobj, t_success, t_failure = self.object_ctor(stmt,
+ 'PyStringObject',
+ 'PyString_Type')
+ return [t_success, t_failure]
+
+ def impl_PyString_FromStringAndSize(self, stmt, v_str, v_size):
+ # Declared in stringobject.h as:
+ # PyAPI_FUNC(PyObject *) PyString_FromStringAndSize(const char *, Py_ssize_t);
+ #
+ # http://docs.python.org/c-api/string.html#PyString_FromStringAndSize
+ #
+ # Defined in Objects/stringobject.c:
+ # # PyObject *
+ # PyString_FromStringAndSize(const char *str, Py_ssize_t size)
+
+ # v_str, v_size = self.state.eval_stmt_args(stmt)
+ # (the input can legitimately be NULL)
+
+ r_newobj, t_success, t_failure = self.object_ctor(stmt,
+ 'PyStringObject',
+ 'PyString_Type')
+ return [t_success, t_failure]
########################################################################
# PyStructSequence_*
12 years, 6 months