Author: Armin Rigo <[email protected]>
Branch:
Changeset: r2929:cc66a9e5ae92
Date: 2017-05-09 17:32 +0200
http://bitbucket.org/cffi/cffi/changeset/cc66a9e5ae92/
Log: Issue 314
Fix the line numbers by discovering that ``# NUMBER "LINE"`` is
supported by pycparser and using it.
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -258,15 +258,21 @@
ctn.discard(name)
typenames += sorted(ctn)
#
- csourcelines = ['typedef int %s;' % typename for typename in typenames]
+ csourcelines = []
+ csourcelines.append('# 1 "<cdef automatic initialization code>"')
+ for typename in typenames:
+ csourcelines.append('typedef int %s;' % typename)
csourcelines.append('typedef int __dotdotdotint__, __dotdotdotfloat__,'
' __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(csource)
- csource = '\n'.join(csourcelines)
+ fullcsource = '\n'.join(csourcelines)
if lock is not None:
lock.acquire() # pycparser is not thread-safe...
try:
- ast = _get_parser().parse(csource)
+ ast = _get_parser().parse(fullcsource)
except pycparser.c_parser.ParseError as e:
self.convert_pycparser_error(e, csource)
finally:
@@ -276,17 +282,17 @@
return ast, macros, csource
def _convert_pycparser_error(self, e, csource):
- # xxx look for ":NUM:" at the start of str(e) and try to interpret
- # it as a line number
+ # xxx look for "<cdef source string>:NUM:" at the start of str(e)
+ # and interpret that as a line number. This will not work if
+ # the user gives explicit ``# NUM "FILE"`` directives.
line = None
msg = str(e)
- if msg.startswith(':') and ':' in msg[1:]:
- linenum = msg[1:msg.find(':',1)]
- if linenum.isdigit():
- linenum = int(linenum, 10)
- csourcelines = csource.splitlines()
- if 1 <= linenum <= len(csourcelines):
- line = csourcelines[linenum-1]
+ match = re.match(r"<cdef source string>:(\d+):", msg)
+ if match:
+ linenum = int(match.group(1), 10)
+ csourcelines = csource.splitlines()
+ if 1 <= linenum <= len(csourcelines):
+ line = csourcelines[linenum-1]
return line
def convert_pycparser_error(self, e, csource):
diff --git a/doc/source/cdef.rst b/doc/source/cdef.rst
--- a/doc/source/cdef.rst
+++ b/doc/source/cdef.rst
@@ -248,6 +248,16 @@
);
"""))
+Note also that pycparser, the underlying C parser, recognizes
+preprocessor-like directives in the following format: ``# NUMBER
+"FILE"``. For example, if you put ``# 42 "foo.h"`` in the middle of the
+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
+``# 1 "<cdef source string>"`` just before the string you give to
+``cdef()``.
+
.. _`ffi.set_unicode()`:
diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py
--- a/testing/cffi0/test_parsing.py
+++ b/testing/cffi0/test_parsing.py
@@ -234,7 +234,20 @@
def test_parse_error():
ffi = FFI()
e = py.test.raises(CDefError, ffi.cdef, " x y z ")
- assert re.match(r'cannot parse "x y z"\n:\d+:', str(e.value))
+ assert str(e.value).startswith(
+ 'cannot parse "x y z"\n<cdef source string>:1:')
+ e = py.test.raises(CDefError, ffi.cdef, "\n\n\n x y z ")
+ assert str(e.value).startswith(
+ 'cannot parse "x y z"\n<cdef source string>:4:')
+
+def test_error_custom_lineno():
+ ffi = FFI()
+ e = py.test.raises(CDefError, ffi.cdef, """
+# 42 "foobar"
+
+ a b c d
+ """)
+ assert str(e.value).startswith('parse error\nfoobar:43:')
def test_cannot_declare_enum_later():
ffi = FFI()
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit