Merge pull request #2 from robotpy/trailing-return-type
Add support for C++17 trailing return types
This commit is contained in:
commit
1b2e622418
@ -149,6 +149,7 @@ class Lexer:
|
|||||||
"DBL_RBRACKET",
|
"DBL_RBRACKET",
|
||||||
"DBL_COLON",
|
"DBL_COLON",
|
||||||
"DBL_AMP",
|
"DBL_AMP",
|
||||||
|
"ARROW",
|
||||||
"SHIFT_LEFT",
|
"SHIFT_LEFT",
|
||||||
] + list(keywords)
|
] + list(keywords)
|
||||||
|
|
||||||
@ -217,6 +218,7 @@ class Lexer:
|
|||||||
t_DBL_RBRACKET = r"\]\]"
|
t_DBL_RBRACKET = r"\]\]"
|
||||||
t_DBL_COLON = r"::"
|
t_DBL_COLON = r"::"
|
||||||
t_DBL_AMP = r"&&"
|
t_DBL_AMP = r"&&"
|
||||||
|
t_ARROW = r"->"
|
||||||
t_SHIFT_LEFT = r"<<"
|
t_SHIFT_LEFT = r"<<"
|
||||||
# SHIFT_RIGHT introduces ambiguity
|
# SHIFT_RIGHT introduces ambiguity
|
||||||
|
|
||||||
|
@ -1515,6 +1515,31 @@ class CxxParser:
|
|||||||
|
|
||||||
return params, vararg
|
return params, vararg
|
||||||
|
|
||||||
|
_auto_return_typename = PQName([AutoSpecifier()])
|
||||||
|
|
||||||
|
def _parse_trailing_return_type(
|
||||||
|
self, fn: typing.Union[Function, FunctionType]
|
||||||
|
) -> None:
|
||||||
|
# entry is "->"
|
||||||
|
return_type = fn.return_type
|
||||||
|
if not (
|
||||||
|
isinstance(return_type, Type)
|
||||||
|
and not return_type.const
|
||||||
|
and not return_type.volatile
|
||||||
|
and return_type.typename == self._auto_return_typename
|
||||||
|
):
|
||||||
|
raise CxxParseError(
|
||||||
|
f"function with trailing return type must specify return type of 'auto', not {return_type}"
|
||||||
|
)
|
||||||
|
|
||||||
|
parsed_type, mods = self._parse_type(None)
|
||||||
|
mods.validate(var_ok=False, meth_ok=False, msg="parsing trailing return type")
|
||||||
|
|
||||||
|
dtype = self._parse_cv_ptr(parsed_type)
|
||||||
|
|
||||||
|
fn.has_trailing_return = True
|
||||||
|
fn.return_type = dtype
|
||||||
|
|
||||||
def _parse_fn_end(self, fn: Function) -> None:
|
def _parse_fn_end(self, fn: Function) -> None:
|
||||||
"""
|
"""
|
||||||
Consumes the various keywords after the parameters in a function
|
Consumes the various keywords after the parameters in a function
|
||||||
@ -1535,6 +1560,8 @@ class CxxParser:
|
|||||||
if self.lex.token_if("{"):
|
if self.lex.token_if("{"):
|
||||||
self._discard_contents("{", "}")
|
self._discard_contents("{", "}")
|
||||||
fn.has_body = True
|
fn.has_body = True
|
||||||
|
elif self.lex.token_if("ARROW"):
|
||||||
|
self._parse_trailing_return_type(fn)
|
||||||
|
|
||||||
def _parse_method_end(self, method: Method) -> None:
|
def _parse_method_end(self, method: Method) -> None:
|
||||||
"""
|
"""
|
||||||
@ -1577,6 +1604,9 @@ class CxxParser:
|
|||||||
setattr(method, tok_value, True)
|
setattr(method, tok_value, True)
|
||||||
elif tok_value in ("&", "&&"):
|
elif tok_value in ("&", "&&"):
|
||||||
method.ref_qualifier = tok_value
|
method.ref_qualifier = tok_value
|
||||||
|
elif tok_value == "ARROW":
|
||||||
|
self._parse_trailing_return_type(method)
|
||||||
|
break
|
||||||
elif tok_value == "throw":
|
elif tok_value == "throw":
|
||||||
tok = self._next_token_must_be("(")
|
tok = self._next_token_must_be("(")
|
||||||
method.throw = self._create_value(self._consume_balanced_tokens(tok))
|
method.throw = self._create_value(self._consume_balanced_tokens(tok))
|
||||||
@ -1667,7 +1697,7 @@ class CxxParser:
|
|||||||
|
|
||||||
self.visitor.on_class_method(state, method)
|
self.visitor.on_class_method(state, method)
|
||||||
|
|
||||||
return method.has_body
|
return method.has_body or method.has_trailing_return
|
||||||
|
|
||||||
else:
|
else:
|
||||||
fn = Function(
|
fn = Function(
|
||||||
@ -1682,7 +1712,7 @@ class CxxParser:
|
|||||||
self._parse_fn_end(fn)
|
self._parse_fn_end(fn)
|
||||||
self.visitor.on_function(state, fn)
|
self.visitor.on_function(state, fn)
|
||||||
|
|
||||||
return fn.has_body
|
return fn.has_body or fn.has_trailing_return
|
||||||
|
|
||||||
#
|
#
|
||||||
# Decorated type parsing
|
# Decorated type parsing
|
||||||
@ -1736,6 +1766,8 @@ class CxxParser:
|
|||||||
|
|
||||||
fn_params, vararg = self._parse_parameters()
|
fn_params, vararg = self._parse_parameters()
|
||||||
dtype = FunctionType(dtype, fn_params, vararg)
|
dtype = FunctionType(dtype, fn_params, vararg)
|
||||||
|
if self.lex.token_if("ARROW"):
|
||||||
|
self._parse_trailing_return_type(dtype)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Check to see if this is a grouping paren or something else
|
# Check to see if this is a grouping paren or something else
|
||||||
|
@ -230,6 +230,11 @@ class FunctionType:
|
|||||||
#: Set to True if ends with ``...``
|
#: Set to True if ends with ``...``
|
||||||
vararg: bool = False
|
vararg: bool = False
|
||||||
|
|
||||||
|
#: True if function has a trailing return type (``auto foo() -> int``).
|
||||||
|
#: In this case, the 'auto' return type is removed and replaced with
|
||||||
|
#: whatever the trailing return type was
|
||||||
|
has_trailing_return: bool = False
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Type:
|
class Type:
|
||||||
@ -469,6 +474,11 @@ class Function:
|
|||||||
#: If true, the body of the function is present
|
#: If true, the body of the function is present
|
||||||
has_body: bool = False
|
has_body: bool = False
|
||||||
|
|
||||||
|
#: True if function has a trailing return type (``auto foo() -> int``).
|
||||||
|
#: In this case, the 'auto' return type is removed and replaced with
|
||||||
|
#: whatever the trailing return type was
|
||||||
|
has_trailing_return: bool = False
|
||||||
|
|
||||||
template: typing.Optional[TemplateDecl] = None
|
template: typing.Optional[TemplateDecl] = None
|
||||||
|
|
||||||
throw: typing.Optional[Value] = None
|
throw: typing.Optional[Value] = None
|
||||||
|
128
tests/test_fn.py
128
tests/test_fn.py
@ -585,3 +585,131 @@ def test_fn_return_std_function():
|
|||||||
|
|
||||||
assert data1 == expected
|
assert data1 == expected
|
||||||
assert data2 == expected
|
assert data2 == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_fn_return_std_function_trailing():
|
||||||
|
content = """
|
||||||
|
std::function<auto(int)->int> fn();
|
||||||
|
"""
|
||||||
|
data = parse_string(content, cleandoc=True)
|
||||||
|
|
||||||
|
assert data == ParsedData(
|
||||||
|
namespace=NamespaceScope(
|
||||||
|
functions=[
|
||||||
|
Function(
|
||||||
|
return_type=Type(
|
||||||
|
typename=PQName(
|
||||||
|
segments=[
|
||||||
|
NameSpecifier(name="std"),
|
||||||
|
NameSpecifier(
|
||||||
|
name="function",
|
||||||
|
specialization=TemplateSpecialization(
|
||||||
|
args=[
|
||||||
|
TemplateArgument(
|
||||||
|
arg=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=PQName(segments=[NameSpecifier(name="fn")]),
|
||||||
|
parameters=[],
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_fn_trailing_return_simple():
|
||||||
|
content = """
|
||||||
|
auto fn() -> int;
|
||||||
|
"""
|
||||||
|
data = parse_string(content, cleandoc=True)
|
||||||
|
|
||||||
|
assert data == ParsedData(
|
||||||
|
namespace=NamespaceScope(
|
||||||
|
functions=[
|
||||||
|
Function(
|
||||||
|
return_type=Type(
|
||||||
|
typename=PQName(segments=[FundamentalSpecifier(name="int")])
|
||||||
|
),
|
||||||
|
name=PQName(segments=[NameSpecifier(name="fn")]),
|
||||||
|
parameters=[],
|
||||||
|
has_trailing_return=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_fn_trailing_return_std_function():
|
||||||
|
content = """
|
||||||
|
auto fn() -> std::function<int()>;
|
||||||
|
"""
|
||||||
|
data = parse_string(content, cleandoc=True)
|
||||||
|
|
||||||
|
assert data == ParsedData(
|
||||||
|
namespace=NamespaceScope(
|
||||||
|
functions=[
|
||||||
|
Function(
|
||||||
|
return_type=Type(
|
||||||
|
typename=PQName(
|
||||||
|
segments=[
|
||||||
|
NameSpecifier(name="std"),
|
||||||
|
NameSpecifier(
|
||||||
|
name="function",
|
||||||
|
specialization=TemplateSpecialization(
|
||||||
|
args=[
|
||||||
|
TemplateArgument(
|
||||||
|
arg=FunctionType(
|
||||||
|
return_type=Type(
|
||||||
|
typename=PQName(
|
||||||
|
segments=[
|
||||||
|
FundamentalSpecifier(
|
||||||
|
name="int"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
parameters=[],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
name=PQName(segments=[NameSpecifier(name="fn")]),
|
||||||
|
parameters=[],
|
||||||
|
has_trailing_return=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user