summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZecong Hu <huzecong@gmail.com>2020-07-18 08:48:31 -0400
committerGitHub <noreply@github.com>2020-07-18 05:48:31 -0700
commit6da5009936d0c220b14f8c70deb0a2c7f82ac922 (patch)
treeaf9739246eab5a361de9d0dc3bdb49635690e82b
parent61eac63fda12f9ba4504def70d0d76cd3ac1bbff (diff)
downloadpycparser-6da5009936d0c220b14f8c70deb0a2c7f82ac922.tar.gz
Fix issues #378, #379, #385 (#387)
* Fix #385: generate code with nested sizeofs * Fix #378: replace assertion with check Only the assertion inside `_build_function_definition` is replaced. The assertion is not appropriate because there are possible inputs that would trigger the assertion, they're just grammatically incorrect. Thus, it is replaced with a check that raises `ParseError` on failure. * Fix #379: parse struct with nested enum
-rw-r--r--pycparser/c_generator.py15
-rw-r--r--pycparser/c_parser.py12
-rw-r--r--tests/test_c_generator.py7
-rwxr-xr-xtests/test_c_parser.py39
4 files changed, 61 insertions, 12 deletions
diff --git a/pycparser/c_generator.py b/pycparser/c_generator.py
index 973d24a..53c26fd 100644
--- a/pycparser/c_generator.py
+++ b/pycparser/c_generator.py
@@ -59,17 +59,18 @@ class CGenerator(object):
return fref + '(' + self.visit(n.args) + ')'
def visit_UnaryOp(self, n):
- operand = self._parenthesize_unless_simple(n.expr)
- if n.op == 'p++':
- return '%s++' % operand
- elif n.op == 'p--':
- return '%s--' % operand
- elif n.op == 'sizeof':
+ if n.op == 'sizeof':
# Always parenthesize the argument of sizeof since it can be
# a name.
return 'sizeof(%s)' % self.visit(n.expr)
else:
- return '%s%s' % (n.op, operand)
+ operand = self._parenthesize_unless_simple(n.expr)
+ if n.op == 'p++':
+ return '%s++' % operand
+ elif n.op == 'p--':
+ return '%s--' % operand
+ else:
+ return '%s%s' % (n.op, operand)
def visit_BinaryOp(self, n):
lval_str = self._parenthesize_if(n.left,
diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py
index 744ede8..c2d82f7 100644
--- a/pycparser/c_parser.py
+++ b/pycparser/c_parser.py
@@ -413,8 +413,8 @@ class CParser(PLYParser):
# A similar problem can occur where the declaration ends up looking
# like an abstract declarator. Give it a name if this is the case.
#
- elif not isinstance(decls[0]['decl'],
- (c_ast.Struct, c_ast.Union, c_ast.IdentifierType)):
+ elif not isinstance(decls[0]['decl'], (
+ c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType)):
decls_0_tail = decls[0]['decl']
while not isinstance(decls_0_tail, c_ast.TypeDecl):
decls_0_tail = decls_0_tail.type
@@ -442,8 +442,9 @@ class CParser(PLYParser):
bitsize=decl.get('bitsize'),
coord=decl['decl'].coord)
- if isinstance(declaration.type,
- (c_ast.Struct, c_ast.Union, c_ast.IdentifierType)):
+ if isinstance(declaration.type, (
+ c_ast.Enum, c_ast.Struct, c_ast.Union,
+ c_ast.IdentifierType)):
fixed_decl = declaration
else:
fixed_decl = self._fix_decl_name_type(declaration, spec['type'])
@@ -464,7 +465,8 @@ class CParser(PLYParser):
def _build_function_definition(self, spec, decl, param_decls, body):
""" Builds a function definition.
"""
- assert 'typedef' not in spec['storage']
+ if 'typedef' in spec['storage']:
+ self._parse_error("Invalid typedef", decl.coord)
declaration = self._build_declarations(
spec=spec,
diff --git a/tests/test_c_generator.py b/tests/test_c_generator.py
index dd19a11..159c763 100644
--- a/tests/test_c_generator.py
+++ b/tests/test_c_generator.py
@@ -354,6 +354,13 @@ class TestCtoC(unittest.TestCase):
self.assertEqual(generator.visit(ast.ext[0].type.type.type),
'const int')
+ def test_nested_sizeof(self):
+ src = '1'
+ for _ in range(30):
+ src = 'sizeof(' + src + ')'
+ src = 'int x = ' + src + ';'
+ self._assert_ctoc_correct(src)
+
class TestCasttoC(unittest.TestCase):
def _find_file(self, name):
diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py
index b6ecdd5..d33941c 100755
--- a/tests/test_c_parser.py
+++ b/tests/test_c_parser.py
@@ -33,6 +33,13 @@ def expand_decl(decl):
elif typ in [Struct, Union]:
decls = [expand_decl(d) for d in decl.decls or []]
return [typ.__name__, decl.name, decls]
+ elif typ == Enum:
+ if decl.values is None:
+ values = None
+ else:
+ assert isinstance(decl.values, EnumeratorList)
+ values = [enum.name for enum in decl.values.enumerators]
+ return ['Enum', decl.name, values]
else:
nested = expand_decl(decl.type)
@@ -855,6 +862,31 @@ class TestCParser_fundamentals(TestCParser_base):
['Decl', 'heads',
['PtrDecl', ['PtrDecl', ['TypeDecl', ['IdentifierType', ['Node']]]]]]]]]])
+ def test_struct_enum(self):
+ s1 = """
+ struct Foo {
+ enum Bar { A = 1 };
+ };
+ """
+ self.assertEqual(expand_decl(self.parse(s1).ext[0]),
+ ['Decl', None,
+ ['Struct', 'Foo',
+ [['Decl', None,
+ ['Enum', 'Bar', ['A']]]]]])
+ s2 = """
+ struct Foo {
+ enum Bar { A = 1, B, C } bar;
+ enum Baz { D = A } baz;
+ } foo;
+ """
+ self.assertEqual(expand_decl(self.parse(s2).ext[0]),
+ ['Decl', 'foo',
+ ['TypeDecl', ['Struct', 'Foo',
+ [['Decl', 'bar',
+ ['TypeDecl', ['Enum', 'Bar', ['A', 'B', 'C']]]],
+ ['Decl', 'baz',
+ ['TypeDecl', ['Enum', 'Baz', ['D']]]]]]]])
+
def test_struct_with_extra_semis_inside(self):
s1 = """
struct {
@@ -1190,6 +1222,13 @@ class TestCParser_fundamentals(TestCParser_base):
for b in bad:
self.assertRaises(ParseError, self.parse, b)
+ def test_invalid_typedef_storage_qual_error(self):
+ """ Tests that using typedef as a storage qualifier is correctly flagged
+ as an error.
+ """
+ bad = 'typedef const int foo(int a) { return 0; }'
+ self.assertRaises(ParseError, self.parse, bad)
+
def test_duplicate_typedef(self):
""" Tests that redeclarations of existing types are parsed correctly.
This is non-standard, but allowed by many compilers.