diff --git a/cxxheaderparser/parser.py b/cxxheaderparser/parser.py index fc658cc..54e36c9 100644 --- a/cxxheaderparser/parser.py +++ b/cxxheaderparser/parser.py @@ -608,11 +608,19 @@ def _parse_template_decl(self) -> TemplateDecl: param = self._parse_template_type_parameter(tok, None) else: param, _ = self._parse_parameter( - ptok, TemplateNonTypeParam, False, ">" + ptok, + TemplateNonTypeParam, + concept_ok=False, + deduce_this_ok=False, + end=">", ) else: param, _ = self._parse_parameter( - tok, TemplateNonTypeParam, concept_ok=False, end=">" + tok, + TemplateNonTypeParam, + concept_ok=False, + deduce_this_ok=False, + end=">", ) params.append(param) @@ -1761,6 +1769,7 @@ def _parse_parameter( tok: typing.Optional[LexToken], cls: typing.Type[PT], concept_ok: bool, + deduce_this_ok: bool, end: str = ")", ) -> typing.Tuple[PT, typing.Optional[Type]]: """ @@ -1775,6 +1784,10 @@ def _parse_parameter( param_pack = False parsed_type: typing.Optional[Type] at_type: typing.Optional[Type] = None + extras: typing.Dict[str, typing.Any] = {} + + if deduce_this_ok and self.lex.token_if("this"): + extras["deduces_this"] = True if not tok: tok = self.lex.token() @@ -1825,12 +1838,18 @@ def _parse_parameter( 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) + param = cls( + type=dtype, + name=param_name, + default=default, + param_pack=param_pack, + **extras, + ) self.debug_print("parameter: %s", param) return param, at_type def _parse_parameters( - self, concept_ok: bool + self, concept_ok: bool, deduce_this_ok: bool ) -> typing.Tuple[typing.List[Parameter], bool, typing.List[TemplateParam]]: """ Consumes function parameters and returns them, and vararg if found, and @@ -1854,7 +1873,13 @@ def _parse_parameters( self._next_token_must_be(")") break - param, at_type = self._parse_parameter(None, Parameter, concept_ok) + # Deduce-this only applicable for first function parameter + param, at_type = self._parse_parameter( + None, + Parameter, + concept_ok, + deduce_this_ok=deduce_this_ok and len(params) == 0, + ) params.append(param) if at_type: at_params.append( @@ -2047,7 +2072,9 @@ def _parse_function( state.location = location is_class_block = isinstance(state, ClassBlockState) - params, vararg, at_params = self._parse_parameters(True) + params, vararg, at_params = self._parse_parameters( + True, deduce_this_ok=is_class_block + ) # Promote abbreviated template parameters if at_params: @@ -2243,7 +2270,7 @@ def _parse_cv_ptr_or_fn( toks = self._consume_balanced_tokens(gtok) self.lex.return_tokens(toks[1:-1]) - fn_params, vararg, _ = self._parse_parameters(False) + fn_params, vararg, _ = self._parse_parameters(False, False) assert not isinstance(dtype, FunctionType) dtype = dtype_fn = FunctionType(dtype, fn_params, vararg) @@ -2273,7 +2300,7 @@ def _parse_cv_ptr_or_fn( assert not isinstance(dtype, FunctionType) dtype = self._parse_array_type(aptok, dtype) elif aptok.type == "(": - fn_params, vararg, _ = self._parse_parameters(False) + fn_params, vararg, _ = self._parse_parameters(False, False) # the type we already have is the return type of the function pointer assert not isinstance(dtype, FunctionType) diff --git a/cxxheaderparser/types.py b/cxxheaderparser/types.py index ea768b6..0eff405 100644 --- a/cxxheaderparser/types.py +++ b/cxxheaderparser/types.py @@ -665,6 +665,9 @@ class Parameter: default: typing.Optional[Value] = None param_pack: bool = False + #: Indicates that this parameter is the parameter used for cxx23 deducing-this + deduces_this: bool = False + def format(self) -> str: default = f" = {self.default.format()}" if self.default else "" pp = "... " if self.param_pack else "" diff --git a/tests/test_class.py b/tests/test_class.py index 9ddb96c..67291cd 100644 --- a/tests/test_class.py +++ b/tests/test_class.py @@ -883,6 +883,60 @@ class StoneClass { ) +def test_class_fn_deduce_this() -> None: + content = """ + class StoneClass { + template + int doSelf(this Self&& self); + }; + """ + data = parse_string(content, cleandoc=True) + + assert data == ParsedData( + namespace=NamespaceScope( + classes=[ + ClassScope( + class_decl=ClassDecl( + typename=PQName( + segments=[NameSpecifier(name="StoneClass")], + classkey="class", + ) + ), + methods=[ + Method( + return_type=Type( + typename=PQName( + segments=[FundamentalSpecifier(name="int")] + ) + ), + name=PQName(segments=[NameSpecifier(name="doSelf")]), + parameters=[ + Parameter( + type=MoveReference( + moveref_to=Type( + typename=PQName( + segments=[NameSpecifier(name="Self")] + ) + ) + ), + name="self", + deduces_this=True, + ) + ], + template=TemplateDecl( + params=[ + TemplateTypeParam(typekey="typename", name="Self") + ] + ), + access="private", + ), + ], + ) + ] + ) + ) + + def test_class_fn_return_global_ns() -> None: content = """ struct Avacado {