diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index 91f0430..37f1a08 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -328,13 +328,15 @@ def _create_value(self, toks: LexTokenList) -> Value: # Parsing begins here # - def parse(self) -> None: - """ - Parse the header contents + def _parse_token(self, tok: LexToken, doxygen: typing.Optional[str]) -> bool: + """Parse a single token from the current parser state. + + Returns True if the caller should keep the current doxygen comment for + the next token. """ # non-ambiguous parsing functions for each token type - _translation_unit_tokens: typing.Dict[ + translation_unit_tokens: typing.Dict[ str, typing.Callable[[LexToken, typing.Optional[str]], typing.Any] ] = { "__attribute__": self._consume_gcc_attribute, @@ -359,7 +361,21 @@ def parse(self) -> None: ";": lambda _1, _2: None, } - _keep_doxygen = {"__declspec", "alignas", "__attribute__", "DBL_LBRACKET"} + keep_doxygen = {"__declspec", "alignas", "__attribute__", "DBL_LBRACKET"} + + fn = translation_unit_tokens.get(tok.type) + if fn: + fn(tok, doxygen) + return tok.type in keep_doxygen + + # this processes ambiguous declarations + self._parse_declarations(tok, doxygen) + return False + + def parse(self) -> None: + """ + Parse the header contents + """ tok = None @@ -377,15 +393,7 @@ def parse(self) -> None: if not tok: break - fn = _translation_unit_tokens.get(tok.type) - if fn: - fn(tok, doxygen) - - if tok.type not in _keep_doxygen: - doxygen = None - else: - # this processes ambiguous declarations - self._parse_declarations(tok, doxygen) + if not self._parse_token(tok, doxygen): doxygen = None except Exception as e: @@ -1188,12 +1196,46 @@ def _parse_using_typealias( mods.validate(var_ok=False, meth_ok=False, msg="parsing typealias") - dtype = self._parse_cv_ptr(parsed_type) + if parsed_type.typename.classkey in ("class", "struct", "union"): + tok = self.lex.token_if_in_set(self._class_enum_stage2) + if tok: + self._parse_class_decl( + parsed_type.typename, + tok, + doxygen, + template, + False, + tok.location, + mods, + [], + ) + self._parse_typealias_class_body() + + dtype = self._parse_cv_ptr_or_fn(parsed_type, nonptr_fn=True) + + tok = self.lex.token_if("[") + while tok: + if isinstance(dtype, FunctionType): + raise CxxParseError("arrays of functions are illegal", tok) + dtype = self._parse_array_type(tok, dtype) + tok = self.lex.token_if("[") alias = UsingAlias(id_tok.value, dtype, template, self._current_access, doxygen) self.visitor.on_using_alias(self.state, alias) + def _parse_typealias_class_body(self) -> None: + class_state = self.state + + while self.state is not class_state.parent: + doxygen = self.lex.get_doxygen() + tok = self.lex.token() + + if tok.type == "}" and self.state is class_state: + self._pop_state() + else: + self._parse_token(tok, doxygen) + def _parse_using( self, tok: LexToken, diff --git a/cxxheaderparser/types.py b/cxxheaderparser/types.py index c9d9f42..5d90906 100644 --- a/cxxheaderparser/types.py +++ b/cxxheaderparser/types.py @@ -967,7 +967,7 @@ class UsingAlias: """ alias: str - type: DecoratedType + type: typing.Union[DecoratedType, FunctionType] template: typing.Optional[TemplateDecl] = None diff --git a/tests/test_using.py b/tests/test_using.py index de25763..f99ac91 100644 --- a/tests/test_using.py +++ b/tests/test_using.py @@ -1,8 +1,11 @@ # Note: testcases generated via `python -m cxxheaderparser.gentest` from cxxheaderparser.types import ( + AnonymousName, + Array, BaseClass, ClassDecl, + Field, Function, FunctionType, FundamentalSpecifier, @@ -20,6 +23,7 @@ Type, UsingAlias, UsingDecl, + Value, ) from cxxheaderparser.simple import ( ClassScope, @@ -748,6 +752,631 @@ def test_using_enum_global() -> None: ) +def test_alias_declaration_array_type() -> None: + content = """ + using aType = int[4]; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + using_alias=[ + UsingAlias( + alias="aType", + type=Array( + array_of=Type( + typename=PQName(segments=[FundamentalSpecifier(name="int")]) + ), + size=Value(tokens=[Token(value="4")]), + ), + ) + ] + ) + ) + + +def test_alias_declaration_function_type() -> None: + content = """ + using fType = void(int); + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + using_alias=[ + UsingAlias( + alias="fType", + type=FunctionType( + return_type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="void")] + ) + ), + parameters=[ + Parameter( + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ) + ) + ], + ), + ) + ] + ) + ) + + +def test_alias_declaration_anonymous_struct_type() -> None: + content = """ + using lType = struct {int x;int y;int z;int t;}; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="lType", + type=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ) + ), + ) + ], + ) + ) + + +def test_alias_declaration_named_struct_type() -> None: + content = """ + using sType = struct l{int x;int y;int z;int t;}; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[NameSpecifier(name="l")], classkey="struct" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="sType", + type=Type( + typename=PQName( + segments=[NameSpecifier(name="l")], classkey="struct" + ) + ), + ) + ], + ) + ) + + +def test_alias_declaration_anonymous_union_type() -> None: + content = """ + using vType = union {int x;int y;int z;int t;}; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="union" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="vType", + type=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="union" + ) + ), + ) + ], + ) + ) + + +def test_alias_declaration_named_union_type() -> None: + content = """ + using uType = union v{int x;int y;int z;int t;}; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[NameSpecifier(name="v")], classkey="union" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="uType", + type=Type( + typename=PQName( + segments=[NameSpecifier(name="v")], classkey="union" + ) + ), + ) + ], + ) + ) + + +def test_alias_declaration_anonymous_struct_reference_type() -> None: + content = """ + using lrefType = struct {int x;int y;int z;int t;}&; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="lrefType", + type=Reference( + ref_to=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ) + ) + ), + ) + ], + ) + ) + + +def test_alias_declaration_anonymous_union_reference_type() -> None: + content = """ + using vrefType = union {int x;int y;int z;int t;}&; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="union" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="vrefType", + type=Reference( + ref_to=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="union" + ) + ) + ), + ) + ], + ) + ) + + +def test_alias_declaration_anonymous_struct_pointer_type() -> None: + content = """ + using lptrType = struct {int x;int y;int z;int t;}*; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="lptrType", + type=Pointer( + ptr_to=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="struct" + ) + ) + ), + ) + ], + ) + ) + + +def test_alias_declaration_anonymous_union_pointer_type() -> None: + content = """ + using vptrType = union {int x;int y;int z;int t;}*; + """ + + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="union" + ) + ), + 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", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="z", + ), + Field( + access="public", + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name="t", + ), + ], + ) + ], + using_alias=[ + UsingAlias( + alias="vptrType", + type=Pointer( + ptr_to=Type( + typename=PQName( + segments=[AnonymousName(id=1)], classkey="union" + ) + ) + ), + ) + ], + ) + ) + def test_using_enum_in_struct() -> None: content = """ struct S {