aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorldore <laurent.dore@gmail.com>2018-04-28 05:09:24 +0200
committerEli Bendersky <eliben@users.noreply.github.com>2018-04-27 20:09:24 -0700
commit81a12ca210a75969ca0d2a606369e65968cf14a6 (patch)
treebb1c67a850731751ecbc64c80b21c802de84f1d8
parenta2a8f4a384bd86efc9faff9030fbb8d905a8ecee (diff)
downloadplatform_external_python_pycparser-81a12ca210a75969ca0d2a606369e65968cf14a6.tar.gz
platform_external_python_pycparser-81a12ca210a75969ca0d2a606369e65968cf14a6.tar.bz2
platform_external_python_pycparser-81a12ca210a75969ca0d2a606369e65968cf14a6.zip
Add support for empty struct (#66) (#254)
-rw-r--r--pycparser/c_generator.py6
-rw-r--r--pycparser/c_parser.py35
-rw-r--r--tests/test_c_generator.py12
-rwxr-xr-xtests/test_c_parser.py20
4 files changed, 63 insertions, 10 deletions
diff --git a/pycparser/c_generator.py b/pycparser/c_generator.py
index 4c86f84..8f2e69a 100644
--- a/pycparser/c_generator.py
+++ b/pycparser/c_generator.py
@@ -300,10 +300,12 @@ class CGenerator(object):
body_function = self._generate_struct_union_body
else:
assert name == 'enum'
- members = () if n.values is None else n.values.enumerators
+ members = None if n.values is None else n.values.enumerators
body_function = self._generate_enum_body
s = name + ' ' + (n.name or '')
- if members:
+ if members is not None:
+ # None means no members
+ # Empty sequence means an empty list of members
s += '\n'
s += self._make_indent()
self.indent_level += 2
diff --git a/pycparser/c_parser.py b/pycparser/c_parser.py
index 47d958f..0ce41bd 100644
--- a/pycparser/c_parser.py
+++ b/pycparser/c_parser.py
@@ -908,6 +908,7 @@ class CParser(PLYParser):
| struct_or_union TYPEID
"""
klass = self._select_struct_union_class(p[1])
+ # None means no list of members
p[0] = klass(
name=p[2],
decls=None,
@@ -915,22 +916,40 @@ class CParser(PLYParser):
def p_struct_or_union_specifier_2(self, p):
""" struct_or_union_specifier : struct_or_union brace_open struct_declaration_list brace_close
+ | struct_or_union brace_open brace_close
"""
klass = self._select_struct_union_class(p[1])
- p[0] = klass(
- name=None,
- decls=p[3],
- coord=self._token_coord(p, 2))
+ if len(p) == 4:
+ # Empty sequence means an empty list of members
+ p[0] = klass(
+ name=None,
+ decls=[],
+ coord=self._token_coord(p, 2))
+ else:
+ p[0] = klass(
+ name=None,
+ decls=p[3],
+ coord=self._token_coord(p, 2))
+
def p_struct_or_union_specifier_3(self, p):
""" struct_or_union_specifier : struct_or_union ID brace_open struct_declaration_list brace_close
+ | struct_or_union ID brace_open brace_close
| struct_or_union TYPEID brace_open struct_declaration_list brace_close
+ | struct_or_union TYPEID brace_open brace_close
"""
klass = self._select_struct_union_class(p[1])
- p[0] = klass(
- name=p[2],
- decls=p[4],
- coord=self._token_coord(p, 2))
+ if len(p) == 5:
+ # Empty sequence means an empty list of members
+ p[0] = klass(
+ name=p[2],
+ decls=[],
+ coord=self._token_coord(p, 2))
+ else:
+ p[0] = klass(
+ name=p[2],
+ decls=p[4],
+ coord=self._token_coord(p, 2))
def p_struct_or_union(self, p):
""" struct_or_union : STRUCT
diff --git a/tests/test_c_generator.py b/tests/test_c_generator.py
index 4e38f28..3727f91 100644
--- a/tests/test_c_generator.py
+++ b/tests/test_c_generator.py
@@ -213,6 +213,18 @@ class TestCtoC(unittest.TestCase):
return 0;
}''')
+ def test_issue66(self):
+ # A non-existing body must not be generated
+ # (previous valid behavior, still working)
+ self._assert_ctoc_correct(r'''
+ struct foo;
+ ''')
+ # An empty body must be generated
+ # (added behavior)
+ self._assert_ctoc_correct(r'''
+ struct foo {};
+ ''')
+
def test_issue83(self):
self._assert_ctoc_correct(r'''
void x(void) {
diff --git a/tests/test_c_parser.py b/tests/test_c_parser.py
index 3b336bf..9dfb132 100755
--- a/tests/test_c_parser.py
+++ b/tests/test_c_parser.py
@@ -998,6 +998,26 @@ class TestCParser_fundamentals(TestCParser_base):
self.assertEqual(parsed_struct.type.type.decls[0].bitsize.value, '6')
self.assertEqual(parsed_struct.type.type.decls[1].bitsize.value, '2')
+ def test_struct_empty(self):
+ """
+ Tests that parsing an empty struct works.
+
+ Empty structs do NOT follow C99 (See 6.2.5-20 of the C99 standard).
+ This is nevertheless supported by some compilers (clang, gcc),
+ especially when using FORTIFY code.
+ Some compilers (visual) will fail to compile with an error.
+ """
+ # an empty struct. This is NOT C99 compliant
+ s1 = """
+ struct foo { };
+ """
+
+ parsed_struct = self.parse(s1).ext[0]
+
+ self.assertEqual(expand_decl(parsed_struct),
+ ['Decl', None, ['Struct', 'foo', []]]
+ )
+
def test_tags_namespace(self):
""" Tests that the tags of structs/unions/enums reside in a separate namespace and
can be named after existing types.