summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArmin Rigo <arigo@tunes.org>2017-05-09 18:07:19 +0200
committerArmin Rigo <arigo@tunes.org>2017-05-09 18:07:19 +0200
commita780b6772eeced3b5b57e313a5b9cf394f722f9e (patch)
tree4de3378c8d0bbf6d09db4f3aab898e8a65c6b58a
parentb274305dc3a28495b12ac25430fbad9d66ba40d0 (diff)
downloadcffi-a780b6772eeced3b5b57e313a5b9cf394f722f9e.tar.gz
Try to systematically include the line number
-rw-r--r--cffi/cparser.py15
-rw-r--r--cffi/error.py9
-rw-r--r--doc/source/cdef.rst2
-rw-r--r--doc/source/whatsnew.rst9
-rw-r--r--testing/cffi0/test_parsing.py14
5 files changed, 39 insertions, 10 deletions
diff --git a/cffi/cparser.py b/cffi/cparser.py
index 1c90d80..f7e2e35 100644
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -16,6 +16,7 @@ try:
except ImportError:
lock = None
+CDEF_SOURCE_STRING = "<cdef source string>"
_r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$",
re.DOTALL | re.MULTILINE)
_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)"
@@ -266,7 +267,7 @@ class Parser(object):
' __dotdotdot__;')
# this forces pycparser to consider the following in the file
# called <cdef source string> from line 1
- csourcelines.append('# 1 "<cdef source string>"')
+ csourcelines.append('# 1 "%s"' % (CDEF_SOURCE_STRING,))
csourcelines.append(csource)
fullcsource = '\n'.join(csourcelines)
if lock is not None:
@@ -287,7 +288,7 @@ class Parser(object):
# the user gives explicit ``# NUM "FILE"`` directives.
line = None
msg = str(e)
- match = re.match(r"<cdef source string>:(\d+):", msg)
+ match = re.match(r"%s:(\d+):" % (CDEF_SOURCE_STRING,), msg)
if match:
linenum = int(match.group(1), 10)
csourcelines = csource.splitlines()
@@ -327,10 +328,12 @@ class Parser(object):
break
else:
assert 0
+ current_decl = None
#
try:
self._inside_extern_python = '__cffi_extern_python_stop'
for decl in iterator:
+ current_decl = decl
if isinstance(decl, pycparser.c_ast.Decl):
self._parse_decl(decl)
elif isinstance(decl, pycparser.c_ast.Typedef):
@@ -354,7 +357,13 @@ class Parser(object):
elif decl.__class__.__name__ == 'Pragma':
pass # skip pragma, only in pycparser 2.15
else:
- raise CDefError("unrecognized construct", decl)
+ raise CDefError("unexpected <%s>: this construct is valid "
+ "C but not valid in cdef()" %
+ decl.__class__.__name__, decl)
+ except CDefError as e:
+ if len(e.args) == 1:
+ e.args = e.args + (current_decl,)
+ raise
except FFIError as e:
msg = self._convert_pycparser_error(e, csource)
if msg:
diff --git a/cffi/error.py b/cffi/error.py
index 75a63d9..ec19964 100644
--- a/cffi/error.py
+++ b/cffi/error.py
@@ -5,10 +5,13 @@ class FFIError(Exception):
class CDefError(Exception):
def __str__(self):
try:
- line = 'line %d: ' % (self.args[1].coord.line,)
+ current_decl = self.args[1]
+ filename = current_decl.coord.file
+ linenum = current_decl.coord.line
+ prefix = '%s:%d: ' % (filename, linenum)
except (AttributeError, TypeError, IndexError):
- line = ''
- return '%s%s' % (line, self.args[0])
+ prefix = ''
+ return '%s%s' % (prefix, self.args[0])
class VerificationError(Exception):
""" An error raised when verification fails
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
index e19fe1b..048c6ef 100644
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -254,7 +254,7 @@ preprocessor-like directives in the following format: ``# NUMBER
string passed to ``cdef()`` and there is an error two lines later, then
it is reported with an error message that starts with ``foo.h:43:`` (the
line which is given the number 42 is the line immediately after the
-directive). *New in version 1.11:* CFFI automatically puts the line
+directive). *New in version 1.10.1:* CFFI automatically puts the line
``# 1 "<cdef source string>"`` just before the string you give to
``cdef()``.
diff --git a/doc/source/whatsnew.rst b/doc/source/whatsnew.rst
index 077b390..82daaf0 100644
--- a/doc/source/whatsnew.rst
+++ b/doc/source/whatsnew.rst
@@ -3,6 +3,15 @@ What's New
======================
+v1.10.1
+=======
+
+* Fixed the line numbers reported in case of ``cdef()`` errors.
+ Also, I just noticed, but pycparser always supported the preprocessor
+ directive ``# 42 "foo.h"`` to mean "from the next line, we're in file
+ foo.h starting from line 42", which it puts in the error messages.
+
+
v1.10
=====
diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py
index 5481f56..ad318ad 100644
--- a/testing/cffi0/test_parsing.py
+++ b/testing/cffi0/test_parsing.py
@@ -228,8 +228,9 @@ def test_cannot_have_only_variadic_part():
# this checks that we get a sensible error if we try "int foo(...);"
ffi = FFI()
e = py.test.raises(CDefError, ffi.cdef, "int foo(...);")
- assert str(e.value) == \
- "foo: a function with only '(...)' as argument is not correct C"
+ assert str(e.value) == (
+ "<cdef source string>:1: foo: a function with only '(...)' "
+ "as argument is not correct C")
def test_parse_error():
ffi = FFI()
@@ -291,7 +292,8 @@ def test_bool():
def test_unknown_argument_type():
ffi = FFI()
e = py.test.raises(CDefError, ffi.cdef, "void f(foobarbazzz);")
- assert str(e.value) == ("f arg 1: unknown type 'foobarbazzz' (if you meant"
+ assert str(e.value) == ("<cdef source string>:1: f arg 1:"
+ " unknown type 'foobarbazzz' (if you meant"
" to use the old C syntax of giving untyped"
" arguments, it is not supported)")
@@ -449,3 +451,9 @@ def test_extern_python_group():
ffi._parser._declarations['extern_python foobar'] !=
ffi._parser._declarations['function bok'] ==
ffi._parser._declarations['extern_python bzrrr'])
+
+def test_error_invalid_syntax_for_cdef():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, 'void foo(void) {}')
+ assert str(e.value) == ('<cdef source string>:1: unexpected <FuncDef>: '
+ 'this construct is valid C but not valid in cdef()')