From 3c5174b52fe15f9de81ff13832603a8b838df36e Mon Sep 17 00:00:00 2001 From: Dustin Spicuzza Date: Fri, 26 Nov 2021 00:51:39 -0500 Subject: [PATCH] Allow typedefs of function definitions - Fixes #13 --- cxxheaderparser/parser.py | 41 +++++++++++++++--- cxxheaderparser/types.py | 6 ++- tests/test_typedef.py | 89 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+), 8 deletions(-) diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index ee40c7b..f626301 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -1645,6 +1645,7 @@ class CxxParser: constructor: bool, destructor: bool, is_friend: bool, + is_typedef: bool, ) -> bool: """ Assumes the caller has already consumed the return type and name, this consumes the @@ -1667,7 +1668,7 @@ class CxxParser: params, vararg = self._parse_parameters() - if is_class_block: + if is_class_block and not is_typedef: props.update(dict.fromkeys(mods.meths.keys(), True)) method: Method @@ -1723,9 +1724,39 @@ class CxxParser: **props, ) self._parse_fn_end(fn) - self.visitor.on_function(state, fn) - return fn.has_body or fn.has_trailing_return + if is_typedef: + if len(pqname.segments) != 1: + raise CxxParseError( + "typedef name may not be a nested-name-specifier" + ) + if fn.constexpr: + raise CxxParseError("typedef function may not be constexpr") + if fn.extern: + raise CxxParseError("typedef function may not be extern") + if fn.static: + raise CxxParseError("typedef function may not be static") + if fn.inline: + raise CxxParseError("typedef function may not be inline") + if fn.has_body: + raise CxxParseError("typedef may not be a function definition") + if fn.template: + raise CxxParseError("typedef function may not have a template") + + fntype = FunctionType( + fn.return_type, + fn.parameters, + fn.vararg, + fn.has_trailing_return, + noexcept=fn.noexcept, + ) + + typedef = Typedef(fntype, pqname.segments[0].name, self._current_access) + self.visitor.on_typedef(state, typedef) + return False + else: + self.visitor.on_function(state, fn) + return fn.has_body or fn.has_trailing_return # # Decorated type parsing @@ -1988,9 +2019,6 @@ class CxxParser: # if ( then it's a function/method if self.lex.token_if("("): - if is_typedef: - raise self._parse_error(None) - if not pqname: raise self._parse_error(None) @@ -2005,6 +2033,7 @@ class CxxParser: constructor, destructor, is_friend, + is_typedef, ) # anything else is a field/variable diff --git a/cxxheaderparser/types.py b/cxxheaderparser/types.py index b978218..becf6ef 100644 --- a/cxxheaderparser/types.py +++ b/cxxheaderparser/types.py @@ -236,6 +236,8 @@ class FunctionType: #: whatever the trailing return type was has_trailing_return: bool = False + noexcept: typing.Optional[Value] = None + @dataclass class Type: @@ -550,13 +552,13 @@ class Typedef: """ - #: The aliased type + #: The aliased type or function type #: #: .. code-block:: c++ #: #: typedef type *pname; #: ~~~~~~ - type: DecoratedType + type: typing.Union[DecoratedType, FunctionType] #: The alias introduced for the specified type #: diff --git a/tests/test_typedef.py b/tests/test_typedef.py index 50a3084..3bbc749 100644 --- a/tests/test_typedef.py +++ b/tests/test_typedef.py @@ -899,3 +899,92 @@ def test_volatile_typedef(): ] ) ) + + +def test_function_typedef(): + content = """ + typedef void fn(int); + typedef auto fntype(int) -> int; + + struct X { + typedef void fn(int); + }; + """ + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[NameSpecifier(name="X")], classkey="struct" + ) + ), + typedefs=[ + Typedef( + type=FunctionType( + return_type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="void")] + ) + ), + parameters=[ + Parameter( + type=Type( + typename=PQName( + segments=[ + FundamentalSpecifier(name="int") + ] + ) + ) + ) + ], + ), + name="fn", + access="public", + ) + ], + ) + ], + typedefs=[ + Typedef( + type=FunctionType( + return_type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="void")] + ) + ), + parameters=[ + Parameter( + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ) + ) + ], + ), + name="fn", + ), + Typedef( + type=FunctionType( + return_type=Type( + typename=PQName(segments=[FundamentalSpecifier(name="int")]) + ), + parameters=[ + Parameter( + type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ) + ) + ], + has_trailing_return=True, + ), + name="fntype", + ), + ], + ) + )