From aa43a378655a62ecf43199abe5edd0059fb989bc Mon Sep 17 00:00:00 2001 From: Jakob Lykke Andersen Date: Fri, 29 Jul 2022 17:49:01 +0200 Subject: [PATCH] C++, fix parsing of certain non-type template parameters Specifically 'template' --- CHANGES | 1 + sphinx/domains/cpp.py | 26 +++++++++++++++++++------- tests/test_domain_cpp.py | 7 ++++--- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 70ac240f4..f9f689895 100644 --- a/CHANGES +++ b/CHANGES @@ -21,6 +21,7 @@ Bugs fixed - #10257: C++, ensure consistent non-specialization template argument representation. +- #10729: C++, fix parsing of certain non-type template parameter packs. Testing -------- diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 3b11f15e9..ac13978d0 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -3668,9 +3668,11 @@ class ASTTemplateParamTemplateType(ASTTemplateParam): class ASTTemplateParamNonType(ASTTemplateParam): def __init__(self, param: Union[ASTTypeWithInit, - ASTTemplateParamConstrainedTypeWithInit]) -> None: + ASTTemplateParamConstrainedTypeWithInit], + parameterPack: bool = False) -> None: assert param self.param = param + self.parameterPack = parameterPack @property def name(self) -> ASTNestedName: @@ -3679,7 +3681,7 @@ class ASTTemplateParamNonType(ASTTemplateParam): @property def isPack(self) -> bool: - return self.param.isPack + return self.param.isPack or self.parameterPack def get_identifier(self) -> ASTIdentifier: name = self.param.name @@ -3700,14 +3702,22 @@ class ASTTemplateParamNonType(ASTTemplateParam): # the anchor will be our parent return symbol.parent.declaration.get_id(version, prefixed=None) else: - return '_' + self.param.get_id(version) + res = '_' + if self.parameterPack: + res += 'Dp' + return res + self.param.get_id(version) def _stringify(self, transform: StringifyTransform) -> str: - return transform(self.param) + res = transform(self.param) + if self.parameterPack: + res += '...' + return res def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: self.param.describe_signature(signode, mode, env, symbol) + if self.parameterPack: + signode += addnodes.desc_sig_punctuation('...', '...') class ASTTemplateParams(ASTBase): @@ -4155,7 +4165,7 @@ class LookupKey: def _is_specialization(templateParams: Union[ASTTemplateParams, ASTTemplateIntroduction], templateArgs: ASTTemplateArgs) -> bool: - """Checks if `templateArgs` does not exactly match `templateParams`.""" + # Checks if `templateArgs` does not exactly match `templateParams`. # the names of the template parameters must be given exactly as args # and params that are packs must in the args be the name expanded if len(templateParams.params) != len(templateArgs.args): @@ -6775,7 +6785,7 @@ class DefinitionParser(BaseParser): def _parse_template_parameter(self) -> ASTTemplateParam: self.skip_ws() if self.skip_word('template'): - # declare a tenplate template parameter + # declare a template template parameter nestedParams = self._parse_template_parameter_list() else: nestedParams = None @@ -6822,7 +6832,9 @@ class DefinitionParser(BaseParser): # non-type parameter or constrained type parameter self.pos = pos param = self._parse_type_with_init('maybe', 'templateParam') - return ASTTemplateParamNonType(param) + self.skip_ws() + parameterPack = self.skip_string('...') + return ASTTemplateParamNonType(param, parameterPack) except DefinitionError as eNonType: self.pos = pos header = "Error when parsing template parameter." diff --git a/tests/test_domain_cpp.py b/tests/test_domain_cpp.py index 41ea21e3d..54c4d35ec 100644 --- a/tests/test_domain_cpp.py +++ b/tests/test_domain_cpp.py @@ -876,6 +876,9 @@ def test_domain_cpp_ast_templates(): # defaulted constrained type parameters check('type', 'template {key}A', {2: 'I_1CE1A'}, key='using') + # pack expansion after non-type template parameter + check('type', 'template {key}A', {2: 'I_DpM1XFibEE1A'}, key='using') + def test_domain_cpp_ast_placeholder_types(): check('function', 'void f(Sortable auto &v)', {1: 'f__SortableR', 2: '1fR8Sortable'}) @@ -1472,9 +1475,7 @@ def parse_template_parameter(param: str): ('int X::*...', True), ('int (X::*)(bool)', False), ('int (X::*x)(bool)', False), - # TODO: the following two declarations cannot currently be parsed - # ('int (X::*)(bool)...', True), - # ('int (X::*x...)(bool)', True), + ('int (X::*)(bool)...', True), ('template class', False), ('template class...', True), ])