diff options
author | Armin Rigo <arigo@tunes.org> | 2017-05-09 18:07:19 +0200 |
---|---|---|
committer | Armin Rigo <arigo@tunes.org> | 2017-05-09 18:07:19 +0200 |
commit | a780b6772eeced3b5b57e313a5b9cf394f722f9e (patch) | |
tree | 4de3378c8d0bbf6d09db4f3aab898e8a65c6b58a | |
parent | b274305dc3a28495b12ac25430fbad9d66ba40d0 (diff) | |
download | cffi-a780b6772eeced3b5b57e313a5b9cf394f722f9e.tar.gz |
Try to systematically include the line number
-rw-r--r-- | cffi/cparser.py | 15 | ||||
-rw-r--r-- | cffi/error.py | 9 | ||||
-rw-r--r-- | doc/source/cdef.rst | 2 | ||||
-rw-r--r-- | doc/source/whatsnew.rst | 9 | ||||
-rw-r--r-- | testing/cffi0/test_parsing.py | 14 |
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()') |