From 3938d0ffefd80028f038ca8a05ce565a74a3719a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Toma=C5=BE=20=C5=A0u=C5=A1tar?= Date: Wed, 14 Jun 2023 19:31:10 +0200 Subject: [PATCH] fix #49 for anonymous unions (#50) * fix #49 for anonymous unions * fix formatting * fix mypy and tests * fix for structs * fix formatting --- cxxheaderparser/parser.py | 27 +++++++++++++- tests/test_class.py | 77 +++++++++++++++++++++++++++++++++++++++ tests/test_union.py | 10 ++++- 3 files changed, 111 insertions(+), 3 deletions(-) diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index f6ecdbc..3148400 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -952,7 +952,7 @@ def _parse_enum_decl( self.visitor.on_enum(self.state, enum) # Finish it up - self._finish_class_or_enum(enum.typename, is_typedef, mods) + self._finish_class_or_enum(enum.typename, is_typedef, mods, "enum") def _parse_enumerator_list(self) -> typing.List[Enumerator]: """ @@ -1131,7 +1131,12 @@ def _parse_class_decl( self.visitor.on_class_start(state) def _finish_class_decl(self, state: ClassBlockState) -> None: - self._finish_class_or_enum(state.class_decl.typename, state.typedef, state.mods) + self._finish_class_or_enum( + state.class_decl.typename, + state.typedef, + state.mods, + state.class_decl.classkey, + ) def _process_access_specifier( self, tok: LexToken, doxygen: typing.Optional[str] @@ -2473,6 +2478,7 @@ def _finish_class_or_enum( name: PQName, is_typedef: bool, mods: ParsedTypeModifiers, + classkey: typing.Optional[str], ) -> None: parsed_type = Type(name) @@ -2481,6 +2487,23 @@ def _finish_class_or_enum( self._consume_gcc_attribute(tok) if not is_typedef and self.lex.token_if(";"): + # if parent scope is a class, add the anonymous + # union or struct to the parent fields + if isinstance(self.state, ClassBlockState): + class_state = self.state + + access = self._current_access + assert access is not None + if ( + classkey is not None + and classkey in ["union", "struct"] + and isinstance(name.segments[-1], AnonymousName) + ): + f = Field( + type=Type(name), + access=access, + ) + self.visitor.on_class_field(class_state, f) return while True: diff --git a/tests/test_class.py b/tests/test_class.py index c8ec97d..0fe0b3d 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -1633,6 +1633,83 @@ def test_class_anon_struct_as_classvar() -> None: ) +def test_class_anon_struct_as_unnamed_classvar() -> None: + content = """ + struct AnonHolderClass { + struct { + int x; + int y; + }; + int z; + }; + """ + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[NameSpecifier(name="AnonHolderClass")], + classkey="struct", + ) + ), + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ), + access="public", + ), + fields=[ + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="x", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="y", + ), + ], + ) + ], + fields=[ + Field( + access="public", + type=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ) + ), + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + ], + ) + ] + ) + ) + + def test_initializer_with_initializer_list_1() -> None: content = """ struct ComplexInit : SomeBase { diff --git a/tests/test_union.py b/tests/test_union.py index ffbb9c9..0c7c41b 100644 --- a/tests/test_union.py +++ b/tests/test_union.py @@ -138,6 +138,14 @@ def test_union_anon_in_struct() -> None: ) ], fields=[ + Field( + access="public", + type=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="union" + ) + ), + ), Field( access="public", type=Type( @@ -146,7 +154,7 @@ def test_union_anon_in_struct() -> None: ) ), name="z", - ) + ), ], ) ]