Allow typedefs of function definitions

- Fixes #13
This commit is contained in:
Dustin Spicuzza 2021-11-26 00:51:39 -05:00
parent e750418d08
commit 3c5174b52f
3 changed files with 128 additions and 8 deletions

View File

@ -1645,6 +1645,7 @@ class CxxParser:
constructor: bool, constructor: bool,
destructor: bool, destructor: bool,
is_friend: bool, is_friend: bool,
is_typedef: bool,
) -> bool: ) -> bool:
""" """
Assumes the caller has already consumed the return type and name, this consumes the 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() 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)) props.update(dict.fromkeys(mods.meths.keys(), True))
method: Method method: Method
@ -1723,9 +1724,39 @@ class CxxParser:
**props, **props,
) )
self._parse_fn_end(fn) 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 # Decorated type parsing
@ -1988,9 +2019,6 @@ class CxxParser:
# if ( then it's a function/method # if ( then it's a function/method
if self.lex.token_if("("): if self.lex.token_if("("):
if is_typedef:
raise self._parse_error(None)
if not pqname: if not pqname:
raise self._parse_error(None) raise self._parse_error(None)
@ -2005,6 +2033,7 @@ class CxxParser:
constructor, constructor,
destructor, destructor,
is_friend, is_friend,
is_typedef,
) )
# anything else is a field/variable # anything else is a field/variable

View File

@ -236,6 +236,8 @@ class FunctionType:
#: whatever the trailing return type was #: whatever the trailing return type was
has_trailing_return: bool = False has_trailing_return: bool = False
noexcept: typing.Optional[Value] = None
@dataclass @dataclass
class Type: class Type:
@ -550,13 +552,13 @@ class Typedef:
""" """
#: The aliased type #: The aliased type or function type
#: #:
#: .. code-block:: c++ #: .. code-block:: c++
#: #:
#: typedef type *pname; #: typedef type *pname;
#: ~~~~~~ #: ~~~~~~
type: DecoratedType type: typing.Union[DecoratedType, FunctionType]
#: The alias introduced for the specified type #: The alias introduced for the specified type
#: #:

View File

@ -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",
),
],
)
)