Skip to content

Commit 2957e70

Browse files
virtualdauscompgeek
andcommitted
Add support for C++20 abbreviated function templates
Co-authored-by: David Vo <[email protected]>
1 parent e935959 commit 2957e70

File tree

3 files changed

+429
-18
lines changed

3 files changed

+429
-18
lines changed

cxxheaderparser/parser.py

Lines changed: 69 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -606,9 +606,13 @@ def _parse_template_decl(self) -> TemplateDecl:
606606
lex.return_token(ptok)
607607
param = self._parse_template_type_parameter(tok, None)
608608
else:
609-
param = self._parse_parameter(ptok, TemplateNonTypeParam, ">")
609+
param, _ = self._parse_parameter(
610+
ptok, TemplateNonTypeParam, False, ">"
611+
)
610612
else:
611-
param = self._parse_parameter(tok, TemplateNonTypeParam, ">")
613+
param, _ = self._parse_parameter(
614+
tok, TemplateNonTypeParam, concept_ok=False, end=">"
615+
)
612616

613617
params.append(param)
614618

@@ -1644,23 +1648,43 @@ def _parse_pqname(
16441648
#
16451649

16461650
def _parse_parameter(
1647-
self, tok: typing.Optional[LexToken], cls: typing.Type[PT], end: str = ")"
1648-
) -> PT:
1651+
self,
1652+
tok: typing.Optional[LexToken],
1653+
cls: typing.Type[PT],
1654+
concept_ok: bool,
1655+
end: str = ")",
1656+
) -> typing.Tuple[PT, typing.Optional[Type]]:
16491657
"""
16501658
Parses a single parameter (excluding vararg parameters). Also used
16511659
to parse template non-type parameters
1660+
1661+
Returns parameter type, abbreviated template type
16521662
"""
16531663

16541664
param_name = None
16551665
default = None
16561666
param_pack = False
1667+
parsed_type: typing.Optional[Type]
1668+
at_type: typing.Optional[Type] = None
16571669

1658-
# required typename + decorators
1659-
parsed_type, mods = self._parse_type(tok)
1660-
if parsed_type is None:
1661-
raise self._parse_error(None)
1670+
if not tok:
1671+
tok = self.lex.token()
16621672

1663-
mods.validate(var_ok=False, meth_ok=False, msg="parsing parameter")
1673+
# placeholder type, skip typename
1674+
if tok.type == "auto":
1675+
at_type = parsed_type = Type(PQName([AutoSpecifier()]))
1676+
else:
1677+
# required typename + decorators
1678+
parsed_type, mods = self._parse_type(tok)
1679+
if parsed_type is None:
1680+
raise self._parse_error(None)
1681+
1682+
mods.validate(var_ok=False, meth_ok=False, msg="parsing parameter")
1683+
1684+
# Could be a concept
1685+
if concept_ok and self.lex.token_if("auto"):
1686+
at_type = Type(parsed_type.typename)
1687+
parsed_type.typename = PQName([AutoSpecifier()])
16641688

16651689
dtype = self._parse_cv_ptr(parsed_type)
16661690

@@ -1688,32 +1712,50 @@ def _parse_parameter(
16881712
if self.lex.token_if("="):
16891713
default = self._create_value(self._consume_value_until([], ",", end))
16901714

1715+
# abbreviated template pack
1716+
if at_type and self.lex.token_if("ELLIPSIS"):
1717+
param_pack = True
1718+
16911719
param = cls(type=dtype, name=param_name, default=default, param_pack=param_pack)
16921720
self.debug_print("parameter: %s", param)
1693-
return param
1721+
return param, at_type
16941722

1695-
def _parse_parameters(self) -> typing.Tuple[typing.List[Parameter], bool]:
1723+
def _parse_parameters(
1724+
self, concept_ok: bool
1725+
) -> typing.Tuple[typing.List[Parameter], bool, typing.List[TemplateParam]]:
16961726
"""
1697-
Consumes function parameters and returns them, and vararg if found
1727+
Consumes function parameters and returns them, and vararg if found, and
1728+
promotes abbreviated template parameters to actual template parameters
1729+
if concept_ok is True
16981730
"""
16991731

17001732
# starting at a (
17011733

17021734
# special case: zero parameters
17031735
if self.lex.token_if(")"):
1704-
return [], False
1736+
return [], False, []
17051737

17061738
params: typing.List[Parameter] = []
17071739
vararg = False
1740+
at_params: typing.List[TemplateParam] = []
17081741

17091742
while True:
17101743
if self.lex.token_if("ELLIPSIS"):
17111744
vararg = True
17121745
self._next_token_must_be(")")
17131746
break
17141747

1715-
param = self._parse_parameter(None, Parameter)
1748+
param, at_type = self._parse_parameter(None, Parameter, concept_ok)
17161749
params.append(param)
1750+
if at_type:
1751+
at_params.append(
1752+
TemplateNonTypeParam(
1753+
type=at_type,
1754+
param_idx=len(params) - 1,
1755+
param_pack=param.param_pack,
1756+
)
1757+
)
1758+
17171759
tok = self._next_token_must_be(",", ")")
17181760
if tok.value == ")":
17191761
break
@@ -1728,7 +1770,7 @@ def _parse_parameters(self) -> typing.Tuple[typing.List[Parameter], bool]:
17281770
):
17291771
params = []
17301772

1731-
return params, vararg
1773+
return params, vararg, at_params
17321774

17331775
_auto_return_typename = PQName([AutoSpecifier()])
17341776

@@ -1875,7 +1917,16 @@ def _parse_function(
18751917
state.location = location
18761918
is_class_block = isinstance(state, ClassBlockState)
18771919

1878-
params, vararg = self._parse_parameters()
1920+
params, vararg, at_params = self._parse_parameters(True)
1921+
1922+
# Promote abbreviated template parameters
1923+
if at_params:
1924+
if template is None:
1925+
template = TemplateDecl(at_params)
1926+
elif isinstance(template, TemplateDecl):
1927+
template.params.extend(at_params)
1928+
else:
1929+
template[-1].params.extend(at_params)
18791930

18801931
# A method outside of a class has multiple name segments
18811932
multiple_name_segments = len(pqname.segments) > 1
@@ -2048,7 +2099,7 @@ def _parse_cv_ptr_or_fn(
20482099
toks = self._consume_balanced_tokens(gtok)
20492100
self.lex.return_tokens(toks[1:-1])
20502101

2051-
fn_params, vararg = self._parse_parameters()
2102+
fn_params, vararg, _ = self._parse_parameters(False)
20522103

20532104
assert not isinstance(dtype, FunctionType)
20542105
dtype = dtype_fn = FunctionType(dtype, fn_params, vararg)
@@ -2076,7 +2127,7 @@ def _parse_cv_ptr_or_fn(
20762127
assert not isinstance(dtype, FunctionType)
20772128
dtype = self._parse_array_type(aptok, dtype)
20782129
elif aptok.type == "(":
2079-
fn_params, vararg = self._parse_parameters()
2130+
fn_params, vararg, _ = self._parse_parameters(False)
20802131
# the type we already have is the return type of the function pointer
20812132

20822133
assert not isinstance(dtype, FunctionType)

cxxheaderparser/types.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,12 +454,19 @@ class TemplateNonTypeParam:
454454
455455
template <auto T>
456456
~~~~~~
457+
458+
// abbreviated template parameters are converted to this and param_idx is set
459+
void fn(C auto p)
460+
~~~~~~
457461
"""
458462

459463
type: DecoratedType
460464
name: typing.Optional[str] = None
461465
default: typing.Optional[Value] = None
462466

467+
#: If this was promoted, the parameter index that this corresponds with
468+
param_idx: typing.Optional[int] = None
469+
463470
#: Contains a ``...``
464471
param_pack: bool = False
465472

0 commit comments

Comments
 (0)