Add support for C++20 abbreviated function templates
Co-authored-by: David Vo <auscompgeek@users.noreply.github.com>
This commit is contained in:
parent
e935959ad3
commit
2957e70823
@ -606,9 +606,13 @@ class CxxParser:
|
||||
lex.return_token(ptok)
|
||||
param = self._parse_template_type_parameter(tok, None)
|
||||
else:
|
||||
param = self._parse_parameter(ptok, TemplateNonTypeParam, ">")
|
||||
param, _ = self._parse_parameter(
|
||||
ptok, TemplateNonTypeParam, False, ">"
|
||||
)
|
||||
else:
|
||||
param = self._parse_parameter(tok, TemplateNonTypeParam, ">")
|
||||
param, _ = self._parse_parameter(
|
||||
tok, TemplateNonTypeParam, concept_ok=False, end=">"
|
||||
)
|
||||
|
||||
params.append(param)
|
||||
|
||||
@ -1644,17 +1648,32 @@ class CxxParser:
|
||||
#
|
||||
|
||||
def _parse_parameter(
|
||||
self, tok: typing.Optional[LexToken], cls: typing.Type[PT], end: str = ")"
|
||||
) -> PT:
|
||||
self,
|
||||
tok: typing.Optional[LexToken],
|
||||
cls: typing.Type[PT],
|
||||
concept_ok: bool,
|
||||
end: str = ")",
|
||||
) -> typing.Tuple[PT, typing.Optional[Type]]:
|
||||
"""
|
||||
Parses a single parameter (excluding vararg parameters). Also used
|
||||
to parse template non-type parameters
|
||||
|
||||
Returns parameter type, abbreviated template type
|
||||
"""
|
||||
|
||||
param_name = None
|
||||
default = None
|
||||
param_pack = False
|
||||
parsed_type: typing.Optional[Type]
|
||||
at_type: typing.Optional[Type] = None
|
||||
|
||||
if not tok:
|
||||
tok = self.lex.token()
|
||||
|
||||
# placeholder type, skip typename
|
||||
if tok.type == "auto":
|
||||
at_type = parsed_type = Type(PQName([AutoSpecifier()]))
|
||||
else:
|
||||
# required typename + decorators
|
||||
parsed_type, mods = self._parse_type(tok)
|
||||
if parsed_type is None:
|
||||
@ -1662,6 +1681,11 @@ class CxxParser:
|
||||
|
||||
mods.validate(var_ok=False, meth_ok=False, msg="parsing parameter")
|
||||
|
||||
# Could be a concept
|
||||
if concept_ok and self.lex.token_if("auto"):
|
||||
at_type = Type(parsed_type.typename)
|
||||
parsed_type.typename = PQName([AutoSpecifier()])
|
||||
|
||||
dtype = self._parse_cv_ptr(parsed_type)
|
||||
|
||||
# optional parameter pack
|
||||
@ -1688,23 +1712,32 @@ class CxxParser:
|
||||
if self.lex.token_if("="):
|
||||
default = self._create_value(self._consume_value_until([], ",", end))
|
||||
|
||||
# abbreviated template pack
|
||||
if at_type and self.lex.token_if("ELLIPSIS"):
|
||||
param_pack = True
|
||||
|
||||
param = cls(type=dtype, name=param_name, default=default, param_pack=param_pack)
|
||||
self.debug_print("parameter: %s", param)
|
||||
return param
|
||||
return param, at_type
|
||||
|
||||
def _parse_parameters(self) -> typing.Tuple[typing.List[Parameter], bool]:
|
||||
def _parse_parameters(
|
||||
self, concept_ok: bool
|
||||
) -> typing.Tuple[typing.List[Parameter], bool, typing.List[TemplateParam]]:
|
||||
"""
|
||||
Consumes function parameters and returns them, and vararg if found
|
||||
Consumes function parameters and returns them, and vararg if found, and
|
||||
promotes abbreviated template parameters to actual template parameters
|
||||
if concept_ok is True
|
||||
"""
|
||||
|
||||
# starting at a (
|
||||
|
||||
# special case: zero parameters
|
||||
if self.lex.token_if(")"):
|
||||
return [], False
|
||||
return [], False, []
|
||||
|
||||
params: typing.List[Parameter] = []
|
||||
vararg = False
|
||||
at_params: typing.List[TemplateParam] = []
|
||||
|
||||
while True:
|
||||
if self.lex.token_if("ELLIPSIS"):
|
||||
@ -1712,8 +1745,17 @@ class CxxParser:
|
||||
self._next_token_must_be(")")
|
||||
break
|
||||
|
||||
param = self._parse_parameter(None, Parameter)
|
||||
param, at_type = self._parse_parameter(None, Parameter, concept_ok)
|
||||
params.append(param)
|
||||
if at_type:
|
||||
at_params.append(
|
||||
TemplateNonTypeParam(
|
||||
type=at_type,
|
||||
param_idx=len(params) - 1,
|
||||
param_pack=param.param_pack,
|
||||
)
|
||||
)
|
||||
|
||||
tok = self._next_token_must_be(",", ")")
|
||||
if tok.value == ")":
|
||||
break
|
||||
@ -1728,7 +1770,7 @@ class CxxParser:
|
||||
):
|
||||
params = []
|
||||
|
||||
return params, vararg
|
||||
return params, vararg, at_params
|
||||
|
||||
_auto_return_typename = PQName([AutoSpecifier()])
|
||||
|
||||
@ -1875,7 +1917,16 @@ class CxxParser:
|
||||
state.location = location
|
||||
is_class_block = isinstance(state, ClassBlockState)
|
||||
|
||||
params, vararg = self._parse_parameters()
|
||||
params, vararg, at_params = self._parse_parameters(True)
|
||||
|
||||
# Promote abbreviated template parameters
|
||||
if at_params:
|
||||
if template is None:
|
||||
template = TemplateDecl(at_params)
|
||||
elif isinstance(template, TemplateDecl):
|
||||
template.params.extend(at_params)
|
||||
else:
|
||||
template[-1].params.extend(at_params)
|
||||
|
||||
# A method outside of a class has multiple name segments
|
||||
multiple_name_segments = len(pqname.segments) > 1
|
||||
@ -2048,7 +2099,7 @@ class CxxParser:
|
||||
toks = self._consume_balanced_tokens(gtok)
|
||||
self.lex.return_tokens(toks[1:-1])
|
||||
|
||||
fn_params, vararg = self._parse_parameters()
|
||||
fn_params, vararg, _ = self._parse_parameters(False)
|
||||
|
||||
assert not isinstance(dtype, FunctionType)
|
||||
dtype = dtype_fn = FunctionType(dtype, fn_params, vararg)
|
||||
@ -2076,7 +2127,7 @@ class CxxParser:
|
||||
assert not isinstance(dtype, FunctionType)
|
||||
dtype = self._parse_array_type(aptok, dtype)
|
||||
elif aptok.type == "(":
|
||||
fn_params, vararg = self._parse_parameters()
|
||||
fn_params, vararg, _ = self._parse_parameters(False)
|
||||
# the type we already have is the return type of the function pointer
|
||||
|
||||
assert not isinstance(dtype, FunctionType)
|
||||
|
@ -454,12 +454,19 @@ class TemplateNonTypeParam:
|
||||
|
||||
template <auto T>
|
||||
~~~~~~
|
||||
|
||||
// abbreviated template parameters are converted to this and param_idx is set
|
||||
void fn(C auto p)
|
||||
~~~~~~
|
||||
"""
|
||||
|
||||
type: DecoratedType
|
||||
name: typing.Optional[str] = None
|
||||
default: typing.Optional[Value] = None
|
||||
|
||||
#: If this was promoted, the parameter index that this corresponds with
|
||||
param_idx: typing.Optional[int] = None
|
||||
|
||||
#: Contains a ``...``
|
||||
param_pack: bool = False
|
||||
|
||||
|
353
tests/test_abv_template.py
Normal file
353
tests/test_abv_template.py
Normal file
@ -0,0 +1,353 @@
|
||||
# Note: testcases generated via `python -m cxxheaderparser.gentest`
|
||||
#
|
||||
# Tests various aspects of abbreviated function templates
|
||||
#
|
||||
|
||||
from cxxheaderparser.simple import NamespaceScope, ParsedData, parse_string
|
||||
from cxxheaderparser.types import (
|
||||
AutoSpecifier,
|
||||
Function,
|
||||
FundamentalSpecifier,
|
||||
NameSpecifier,
|
||||
PQName,
|
||||
Parameter,
|
||||
Pointer,
|
||||
Reference,
|
||||
TemplateDecl,
|
||||
TemplateNonTypeParam,
|
||||
Type,
|
||||
)
|
||||
|
||||
|
||||
def test_abv_template_f1() -> None:
|
||||
content = """
|
||||
void f1(auto); // same as template<class T> void f1(T)
|
||||
void f1p(auto p);
|
||||
"""
|
||||
data = parse_string(content, cleandoc=True)
|
||||
|
||||
assert data == ParsedData(
|
||||
namespace=NamespaceScope(
|
||||
functions=[
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f1")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()]))
|
||||
)
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()])),
|
||||
param_idx=0,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f1p")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()])),
|
||||
name="p",
|
||||
)
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()])),
|
||||
param_idx=0,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_abv_template_f2() -> None:
|
||||
content = """
|
||||
void f2(C1 auto); // same as template<C1 T> void f2(T), if C1 is a concept
|
||||
void f2p(C1 auto p);
|
||||
"""
|
||||
data = parse_string(content, cleandoc=True)
|
||||
|
||||
assert data == ParsedData(
|
||||
namespace=NamespaceScope(
|
||||
functions=[
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f2")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()]))
|
||||
)
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C1")])
|
||||
),
|
||||
param_idx=0,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f2p")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()])),
|
||||
name="p",
|
||||
)
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C1")])
|
||||
),
|
||||
param_idx=0,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_abv_template_f3() -> None:
|
||||
content = """
|
||||
void f3(C2 auto...); // same as template<C2... Ts> void f3(Ts...), if C2 is a
|
||||
// concept
|
||||
void f3p(C2 auto p...);
|
||||
"""
|
||||
data = parse_string(content, cleandoc=True)
|
||||
|
||||
assert data == ParsedData(
|
||||
namespace=NamespaceScope(
|
||||
functions=[
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f3")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()])),
|
||||
param_pack=True,
|
||||
)
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C2")])
|
||||
),
|
||||
param_idx=0,
|
||||
param_pack=True,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f3p")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()])),
|
||||
name="p",
|
||||
param_pack=True,
|
||||
)
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C2")])
|
||||
),
|
||||
param_idx=0,
|
||||
param_pack=True,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_abv_template_f4() -> None:
|
||||
content = """
|
||||
void f4(C2 auto, ...); // same as template<C2 T> void f4(T...), if C2 is a concept
|
||||
void f4p(C2 auto p,...);
|
||||
"""
|
||||
data = parse_string(content, cleandoc=True)
|
||||
|
||||
assert data == ParsedData(
|
||||
namespace=NamespaceScope(
|
||||
functions=[
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f4")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()]))
|
||||
)
|
||||
],
|
||||
vararg=True,
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C2")])
|
||||
),
|
||||
param_idx=0,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f4p")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Type(typename=PQName(segments=[AutoSpecifier()])),
|
||||
name="p",
|
||||
)
|
||||
],
|
||||
vararg=True,
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C2")])
|
||||
),
|
||||
param_idx=0,
|
||||
)
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def test_abv_template_f5() -> None:
|
||||
content = """
|
||||
void f5(const C3 auto *, C4 auto &); // same as template<C3 T, C4 U> void f5(const T*, U&);
|
||||
void f5p(const C3 auto * p1, C4 auto &p2);
|
||||
"""
|
||||
data = parse_string(content, cleandoc=True)
|
||||
|
||||
assert data == ParsedData(
|
||||
namespace=NamespaceScope(
|
||||
functions=[
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f5")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Pointer(
|
||||
ptr_to=Type(
|
||||
typename=PQName(
|
||||
segments=[AutoSpecifier()],
|
||||
),
|
||||
const=True,
|
||||
)
|
||||
)
|
||||
),
|
||||
Parameter(
|
||||
type=Reference(
|
||||
ref_to=Type(typename=PQName(segments=[AutoSpecifier()]))
|
||||
)
|
||||
),
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(
|
||||
segments=[NameSpecifier(name="C3")]
|
||||
),
|
||||
),
|
||||
param_idx=0,
|
||||
),
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C4")])
|
||||
),
|
||||
param_idx=1,
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
Function(
|
||||
return_type=Type(
|
||||
typename=PQName(segments=[FundamentalSpecifier(name="void")])
|
||||
),
|
||||
name=PQName(segments=[NameSpecifier(name="f5p")]),
|
||||
parameters=[
|
||||
Parameter(
|
||||
type=Pointer(
|
||||
ptr_to=Type(
|
||||
typename=PQName(
|
||||
segments=[AutoSpecifier()],
|
||||
),
|
||||
const=True,
|
||||
)
|
||||
),
|
||||
name="p1",
|
||||
),
|
||||
Parameter(
|
||||
type=Reference(
|
||||
ref_to=Type(typename=PQName(segments=[AutoSpecifier()]))
|
||||
),
|
||||
name="p2",
|
||||
),
|
||||
],
|
||||
template=TemplateDecl(
|
||||
params=[
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(
|
||||
segments=[NameSpecifier(name="C3")]
|
||||
),
|
||||
),
|
||||
param_idx=0,
|
||||
),
|
||||
TemplateNonTypeParam(
|
||||
type=Type(
|
||||
typename=PQName(segments=[NameSpecifier(name="C4")])
|
||||
),
|
||||
param_idx=1,
|
||||
),
|
||||
]
|
||||
),
|
||||
),
|
||||
]
|
||||
)
|
||||
)
|
Loading…
x
Reference in New Issue
Block a user