mirror of
https://github.com/sphinx-doc/sphinx.git
synced 2025-02-25 18:55:22 -06:00
C++, fix non-type template parameter parsing
Fixes sphinx-doc/sphinx#7944
This commit is contained in:
parent
4f56fad7b7
commit
a99039b13b
2
CHANGES
2
CHANGES
@ -87,6 +87,8 @@ Bugs fixed
|
||||
* #2050: Symbols sections are appeared twice in the index page
|
||||
* #8017: Fix circular import in sphinx.addnodes
|
||||
* #7986: CSS: make "highlight" selector more robust
|
||||
* #7944: C++, parse non-type template parameters starting with
|
||||
a dependent qualified name.
|
||||
|
||||
Testing
|
||||
--------
|
||||
|
@ -6250,23 +6250,18 @@ class DefinitionParser(BaseParser):
|
||||
|
||||
# ==========================================================================
|
||||
|
||||
def _parse_template_parameter_list(self) -> ASTTemplateParams:
|
||||
# only: '<' parameter-list '>'
|
||||
# we assume that 'template' has just been parsed
|
||||
templateParams = [] # type: List[ASTTemplateParam]
|
||||
self.skip_ws()
|
||||
if not self.skip_string("<"):
|
||||
self.fail("Expected '<' after 'template'")
|
||||
prevErrors = []
|
||||
while 1:
|
||||
self.skip_ws()
|
||||
if self.skip_word('template'):
|
||||
# declare a tenplate template parameter
|
||||
nestedParams = self._parse_template_parameter_list()
|
||||
else:
|
||||
nestedParams = None
|
||||
self.skip_ws()
|
||||
def _parse_template_paramter(self) -> ASTTemplateParam:
|
||||
if self.skip_word('template'):
|
||||
# declare a tenplate template parameter
|
||||
nestedParams = self._parse_template_parameter_list()
|
||||
else:
|
||||
nestedParams = None
|
||||
|
||||
pos = self.pos
|
||||
try:
|
||||
# Unconstrained type parameter or template type parameter
|
||||
key = None
|
||||
self.skip_ws()
|
||||
if self.skip_word_and_ws('typename'):
|
||||
key = 'typename'
|
||||
elif self.skip_word_and_ws('class'):
|
||||
@ -6274,52 +6269,79 @@ class DefinitionParser(BaseParser):
|
||||
elif nestedParams:
|
||||
self.fail("Expected 'typename' or 'class' after "
|
||||
"template template parameter list.")
|
||||
if key:
|
||||
# declare a type or template type parameter
|
||||
self.skip_ws()
|
||||
parameterPack = self.skip_string('...')
|
||||
self.skip_ws()
|
||||
if self.match(identifier_re):
|
||||
identifier = ASTIdentifier(self.matched_text)
|
||||
else:
|
||||
identifier = None
|
||||
self.skip_ws()
|
||||
if not parameterPack and self.skip_string('='):
|
||||
default = self._parse_type(named=False, outer=None)
|
||||
else:
|
||||
default = None
|
||||
data = ASTTemplateKeyParamPackIdDefault(key, identifier,
|
||||
parameterPack, default)
|
||||
if nestedParams:
|
||||
# template type
|
||||
templateParams.append(
|
||||
ASTTemplateParamTemplateType(nestedParams, data))
|
||||
else:
|
||||
# type
|
||||
templateParams.append(ASTTemplateParamType(data))
|
||||
else:
|
||||
# declare a non-type parameter, or constrained type parameter
|
||||
pos = self.pos
|
||||
try:
|
||||
param = self._parse_type_with_init('maybe', 'templateParam')
|
||||
templateParams.append(ASTTemplateParamNonType(param))
|
||||
except DefinitionError as e:
|
||||
msg = "If non-type template parameter or constrained template parameter"
|
||||
prevErrors.append((e, msg))
|
||||
self.pos = pos
|
||||
self.fail("Expected 'typename' or 'class' in tbe "
|
||||
"beginning of template type parameter.")
|
||||
self.skip_ws()
|
||||
parameterPack = self.skip_string('...')
|
||||
self.skip_ws()
|
||||
if self.match(identifier_re):
|
||||
identifier = ASTIdentifier(self.matched_text)
|
||||
else:
|
||||
identifier = None
|
||||
self.skip_ws()
|
||||
if not parameterPack and self.skip_string('='):
|
||||
default = self._parse_type(named=False, outer=None)
|
||||
else:
|
||||
default = None
|
||||
if self.current_char not in ',>':
|
||||
self.fail('Expected "," or ">" after (template) type parameter.')
|
||||
data = ASTTemplateKeyParamPackIdDefault(key, identifier,
|
||||
parameterPack, default)
|
||||
if nestedParams:
|
||||
return ASTTemplateParamTemplateType(nestedParams, data)
|
||||
else:
|
||||
return ASTTemplateParamType(data)
|
||||
except DefinitionError as eType:
|
||||
if nestedParams:
|
||||
raise
|
||||
try:
|
||||
# non-type parameter or constrained type parameter
|
||||
self.pos = pos
|
||||
param = self._parse_type_with_init('maybe', 'templateParam')
|
||||
return ASTTemplateParamNonType(param)
|
||||
except DefinitionError as eNonType:
|
||||
self.pos = pos
|
||||
header = "Error when parsing template parameter."
|
||||
errs = []
|
||||
errs.append(
|
||||
(eType, "If unconstrained type parameter or template type parameter"))
|
||||
errs.append(
|
||||
(eNonType, "If constrained type parameter or non-type parameter"))
|
||||
raise self._make_multi_error(errs, header)
|
||||
|
||||
def _parse_template_parameter_list(self) -> ASTTemplateParams:
|
||||
# only: '<' parameter-list '>'
|
||||
# we assume that 'template' has just been parsed
|
||||
templateParams = [] # type: List[ASTTemplateParam]
|
||||
self.skip_ws()
|
||||
if not self.skip_string("<"):
|
||||
self.fail("Expected '<' after 'template'")
|
||||
while 1:
|
||||
pos = self.pos
|
||||
err = None
|
||||
try:
|
||||
param = self._parse_template_paramter()
|
||||
templateParams.append(param)
|
||||
except DefinitionError as eParam:
|
||||
self.pos = pos
|
||||
err = eParam
|
||||
self.skip_ws()
|
||||
if self.skip_string('>'):
|
||||
return ASTTemplateParams(templateParams)
|
||||
elif self.skip_string(','):
|
||||
prevErrors = []
|
||||
continue
|
||||
else:
|
||||
header = "Error in template parameter list."
|
||||
errs = []
|
||||
if err:
|
||||
errs.append((err, "If parameter"))
|
||||
try:
|
||||
self.fail('Expected "=", ",", or ">".')
|
||||
self.fail('Expected "," or ">".')
|
||||
except DefinitionError as e:
|
||||
prevErrors.append((e, ""))
|
||||
raise self._make_multi_error(prevErrors, header)
|
||||
errs.append((e, "If no parameter"))
|
||||
print(errs)
|
||||
raise self._make_multi_error(errs, header)
|
||||
|
||||
def _parse_template_introduction(self) -> ASTTemplateIntroduction:
|
||||
pos = self.pos
|
||||
|
@ -760,6 +760,7 @@ def test_templates():
|
||||
check('class', "template<typename T = Test> {key}A", {2: "I0E1A"})
|
||||
|
||||
check('class', "template<template<typename> typename T> {key}A", {2: "II0E0E1A"})
|
||||
check('class', "template<template<typename> class T> {key}A", {2: "II0E0E1A"})
|
||||
check('class', "template<template<typename> typename> {key}A", {2: "II0E0E1A"})
|
||||
check('class', "template<template<typename> typename ...T> {key}A", {2: "II0EDpE1A"})
|
||||
check('class', "template<template<typename> typename...> {key}A", {2: "II0EDpE1A"})
|
||||
@ -770,6 +771,16 @@ def test_templates():
|
||||
check('class', "template<int T = 42> {key}A", {2: "I_iE1A"})
|
||||
check('class', "template<int = 42> {key}A", {2: "I_iE1A"})
|
||||
|
||||
check('class', "template<typename A<B>::C> {key}A", {2: "I_N1AI1BE1CEE1A"})
|
||||
check('class', "template<typename A<B>::C = 42> {key}A", {2: "I_N1AI1BE1CEE1A"})
|
||||
# from #7944
|
||||
check('function', "template<typename T, "
|
||||
"typename std::enable_if<!has_overloaded_addressof<T>::value, bool>::type = false"
|
||||
"> constexpr T *static_addressof(T &ref)",
|
||||
{2: "I0_NSt9enable_ifIX!has_overloaded_addressof<T>::valueEbE4typeEE16static_addressofR1T",
|
||||
3: "I0_NSt9enable_ifIXntN24has_overloaded_addressofI1TE5valueEEbE4typeEE16static_addressofR1T",
|
||||
4: "I0_NSt9enable_ifIXntN24has_overloaded_addressofI1TE5valueEEbE4typeEE16static_addressofP1TR1T"})
|
||||
|
||||
check('class', "template<> {key}A<NS::B<>>", {2: "IE1AIN2NS1BIEEE"})
|
||||
|
||||
# from #2058
|
||||
|
Loading…
Reference in New Issue
Block a user