Merge branch '5.x'

This commit is contained in:
Adam Turner 2022-07-26 22:31:16 +01:00
commit 6f530ca3ea
6 changed files with 167 additions and 56 deletions

34
CHANGES
View File

@ -36,12 +36,46 @@ Deprecated
Features added Features added
-------------- --------------
- #10286: C++, support requires clauses not just between the template
parameter lists and the declaration.
Bugs fixed Bugs fixed
---------- ----------
Testing Testing
-------- --------
Release 5.1.2 (in development)
==============================
Dependencies
------------
Incompatible changes
--------------------
Deprecated
----------
Features added
--------------
Bugs fixed
----------
Testing
--------
Release 5.1.1 (released Jul 26, 2022)
=====================================
Bugs fixed
----------
* #10701: Fix ValueError in the new ``deque`` based ``sphinx.ext.napolean``
iterator implementation.
* #10702: Restore compatability with third-party builders.
Release 5.1.0 (released Jul 24, 2022) Release 5.1.0 (released Jul 24, 2022)
===================================== =====================================

View File

@ -89,10 +89,11 @@ class Builder:
self.env: BuildEnvironment = env self.env: BuildEnvironment = env
self.env.set_versioning_method(self.versioning_method, self.env.set_versioning_method(self.versioning_method,
self.versioning_compare) self.versioning_compare)
elif env is not Ellipsis: else:
# ... is passed by SphinxComponentRegistry.create_builder to not show two warnings. # ... is passed by SphinxComponentRegistry.create_builder to not show two warnings.
warnings.warn("The 'env' argument to Builder will be required from Sphinx 7.", warnings.warn("The 'env' argument to Builder will be required from Sphinx 7.",
RemovedInSphinx70Warning, stacklevel=2) RemovedInSphinx70Warning, stacklevel=2)
self.env = None
self.events: EventManager = app.events self.events: EventManager = app.events
self.config: Config = app.config self.config: Config = app.config
self.tags: Tags = app.tags self.tags: Tags = app.tags

View File

@ -3687,17 +3687,23 @@ class ASTTemplateParamNonType(ASTTemplateParam):
class ASTTemplateParams(ASTBase): class ASTTemplateParams(ASTBase):
def __init__(self, params: List[ASTTemplateParam]) -> None: def __init__(self, params: List[ASTTemplateParam],
requiresClause: Optional["ASTRequiresClause"]) -> None:
assert params is not None assert params is not None
self.params = params self.params = params
self.requiresClause = requiresClause
def get_id(self, version: int) -> str: def get_id(self, version: int, excludeRequires: bool = False) -> str:
assert version >= 2 assert version >= 2
res = [] res = []
res.append("I") res.append("I")
for param in self.params: for param in self.params:
res.append(param.get_id(version)) res.append(param.get_id(version))
res.append("E") res.append("E")
if not excludeRequires and self.requiresClause:
res.append('IQ')
res.append(self.requiresClause.expr.get_id(version))
res.append('E')
return ''.join(res) return ''.join(res)
def _stringify(self, transform: StringifyTransform) -> str: def _stringify(self, transform: StringifyTransform) -> str:
@ -3705,6 +3711,9 @@ class ASTTemplateParams(ASTBase):
res.append("template<") res.append("template<")
res.append(", ".join(transform(a) for a in self.params)) res.append(", ".join(transform(a) for a in self.params))
res.append("> ") res.append("> ")
if self.requiresClause is not None:
res.append(transform(self.requiresClause))
res.append(" ")
return ''.join(res) return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str, def describe_signature(self, signode: TextElement, mode: str,
@ -3719,6 +3728,9 @@ class ASTTemplateParams(ASTBase):
first = False first = False
param.describe_signature(signode, mode, env, symbol) param.describe_signature(signode, mode, env, symbol)
signode += addnodes.desc_sig_punctuation('>', '>') signode += addnodes.desc_sig_punctuation('>', '>')
if self.requiresClause is not None:
signode += addnodes.desc_sig_space()
self.requiresClause.describe_signature(signode, mode, env, symbol)
def describe_signature_as_introducer( def describe_signature_as_introducer(
self, parentNode: desc_signature, mode: str, env: "BuildEnvironment", self, parentNode: desc_signature, mode: str, env: "BuildEnvironment",
@ -3743,6 +3755,11 @@ class ASTTemplateParams(ASTBase):
if lineSpec and not first: if lineSpec and not first:
lineNode = makeLine(parentNode) lineNode = makeLine(parentNode)
lineNode += addnodes.desc_sig_punctuation('>', '>') lineNode += addnodes.desc_sig_punctuation('>', '>')
if self.requiresClause:
reqNode = addnodes.desc_signature_line()
reqNode.sphinx_line_type = 'requiresClause'
parentNode += reqNode
self.requiresClause.describe_signature(reqNode, 'markType', env, symbol)
# Template introducers # Template introducers
@ -3861,11 +3878,23 @@ class ASTTemplateDeclarationPrefix(ASTBase):
# templates is None means it's an explicit instantiation of a variable # templates is None means it's an explicit instantiation of a variable
self.templates = templates self.templates = templates
def get_id(self, version: int) -> str: def get_requires_clause_in_last(self) -> Optional["ASTRequiresClause"]:
if self.templates is None:
return None
lastList = self.templates[-1]
if not isinstance(lastList, ASTTemplateParams):
return None
return lastList.requiresClause # which may be None
def get_id_except_requires_clause_in_last(self, version: int) -> str:
assert version >= 2 assert version >= 2
# this is not part of a normal name mangling system # This is not part of the Itanium ABI mangling system.
res = [] res = []
for t in self.templates: lastIndex = len(self.templates) - 1
for i, t in enumerate(self.templates):
if isinstance(t, ASTTemplateParams):
res.append(t.get_id(version, excludeRequires=(i == lastIndex)))
else:
res.append(t.get_id(version)) res.append(t.get_id(version))
return ''.join(res) return ''.join(res)
@ -3889,7 +3918,7 @@ class ASTRequiresClause(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str: def _stringify(self, transform: StringifyTransform) -> str:
return 'requires ' + transform(self.expr) return 'requires ' + transform(self.expr)
def describe_signature(self, signode: addnodes.desc_signature_line, mode: str, def describe_signature(self, signode: nodes.TextElement, mode: str,
env: "BuildEnvironment", symbol: "Symbol") -> None: env: "BuildEnvironment", symbol: "Symbol") -> None:
signode += addnodes.desc_sig_keyword('requires', 'requires') signode += addnodes.desc_sig_keyword('requires', 'requires')
signode += addnodes.desc_sig_space() signode += addnodes.desc_sig_space()
@ -3900,16 +3929,16 @@ class ASTRequiresClause(ASTBase):
################################################################################ ################################################################################
class ASTDeclaration(ASTBase): class ASTDeclaration(ASTBase):
def __init__(self, objectType: str, directiveType: str, visibility: str, def __init__(self, objectType: str, directiveType: Optional[str] = None,
templatePrefix: ASTTemplateDeclarationPrefix, visibility: Optional[str] = None,
requiresClause: ASTRequiresClause, declaration: Any, templatePrefix: Optional[ASTTemplateDeclarationPrefix] = None,
trailingRequiresClause: ASTRequiresClause, declaration: Any = None,
trailingRequiresClause: Optional[ASTRequiresClause] = None,
semicolon: bool = False) -> None: semicolon: bool = False) -> None:
self.objectType = objectType self.objectType = objectType
self.directiveType = directiveType self.directiveType = directiveType
self.visibility = visibility self.visibility = visibility
self.templatePrefix = templatePrefix self.templatePrefix = templatePrefix
self.requiresClause = requiresClause
self.declaration = declaration self.declaration = declaration
self.trailingRequiresClause = trailingRequiresClause self.trailingRequiresClause = trailingRequiresClause
self.semicolon = semicolon self.semicolon = semicolon
@ -3920,11 +3949,10 @@ class ASTDeclaration(ASTBase):
def clone(self) -> "ASTDeclaration": def clone(self) -> "ASTDeclaration":
templatePrefixClone = self.templatePrefix.clone() if self.templatePrefix else None templatePrefixClone = self.templatePrefix.clone() if self.templatePrefix else None
requiresClasueClone = self.requiresClause.clone() if self.requiresClause else None
trailingRequiresClasueClone = self.trailingRequiresClause.clone() \ trailingRequiresClasueClone = self.trailingRequiresClause.clone() \
if self.trailingRequiresClause else None if self.trailingRequiresClause else None
return ASTDeclaration(self.objectType, self.directiveType, self.visibility, return ASTDeclaration(self.objectType, self.directiveType, self.visibility,
templatePrefixClone, requiresClasueClone, templatePrefixClone,
self.declaration.clone(), trailingRequiresClasueClone, self.declaration.clone(), trailingRequiresClasueClone,
self.semicolon) self.semicolon)
@ -3940,7 +3968,7 @@ class ASTDeclaration(ASTBase):
def get_id(self, version: int, prefixed: bool = True) -> str: def get_id(self, version: int, prefixed: bool = True) -> str:
if version == 1: if version == 1:
if self.templatePrefix: if self.templatePrefix or self.trailingRequiresClause:
raise NoOldIdError() raise NoOldIdError()
if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
return self.enumeratorScopedSymbol.declaration.get_id(version) return self.enumeratorScopedSymbol.declaration.get_id(version)
@ -3952,16 +3980,31 @@ class ASTDeclaration(ASTBase):
res = [_id_prefix[version]] res = [_id_prefix[version]]
else: else:
res = [] res = []
if self.templatePrefix: # (See also https://github.com/sphinx-doc/sphinx/pull/10286#issuecomment-1168102147)
res.append(self.templatePrefix.get_id(version)) # The first implementation of requires clauses only supported a single clause after the
if self.requiresClause or self.trailingRequiresClause: # template prefix, and no trailing clause. It put the ID after the template parameter
# list, i.e.,
# "I" + template_parameter_list_id + "E" + "IQ" + requires_clause_id + "E"
# but the second implementation associates the requires clause with each list, i.e.,
# "I" + template_parameter_list_id + "IQ" + requires_clause_id + "E" + "E"
# To avoid making a new ID version, we make an exception for the last requires clause
# in the template prefix, and still put it in the end.
# As we now support trailing requires clauses we add that as if it was a conjunction.
if self.templatePrefix is not None:
res.append(self.templatePrefix.get_id_except_requires_clause_in_last(version))
requiresClauseInLast = self.templatePrefix.get_requires_clause_in_last()
else:
requiresClauseInLast = None
if requiresClauseInLast or self.trailingRequiresClause:
if version < 4: if version < 4:
raise NoOldIdError() raise NoOldIdError()
res.append('IQ') res.append('IQ')
if self.requiresClause and self.trailingRequiresClause: if requiresClauseInLast and self.trailingRequiresClause:
# make a conjunction of them
res.append('aa') res.append('aa')
if self.requiresClause: if requiresClauseInLast:
res.append(self.requiresClause.expr.get_id(version)) res.append(requiresClauseInLast.expr.get_id(version))
if self.trailingRequiresClause: if self.trailingRequiresClause:
res.append(self.trailingRequiresClause.expr.get_id(version)) res.append(self.trailingRequiresClause.expr.get_id(version))
res.append('E') res.append('E')
@ -3978,9 +4021,6 @@ class ASTDeclaration(ASTBase):
res.append(' ') res.append(' ')
if self.templatePrefix: if self.templatePrefix:
res.append(transform(self.templatePrefix)) res.append(transform(self.templatePrefix))
if self.requiresClause:
res.append(transform(self.requiresClause))
res.append(' ')
res.append(transform(self.declaration)) res.append(transform(self.declaration))
if self.trailingRequiresClause: if self.trailingRequiresClause:
res.append(' ') res.append(' ')
@ -4005,11 +4045,6 @@ class ASTDeclaration(ASTBase):
self.templatePrefix.describe_signature(signode, mode, env, self.templatePrefix.describe_signature(signode, mode, env,
symbol=self.symbol, symbol=self.symbol,
lineSpec=options.get('tparam-line-spec')) lineSpec=options.get('tparam-line-spec'))
if self.requiresClause:
reqNode = addnodes.desc_signature_line()
reqNode.sphinx_line_type = 'requiresClause'
signode.append(reqNode)
self.requiresClause.describe_signature(reqNode, 'markType', env, self.symbol)
signode += mainDeclNode signode += mainDeclNode
if self.visibility and self.visibility != "public": if self.visibility and self.visibility != "public":
mainDeclNode += addnodes.desc_sig_keyword(self.visibility, self.visibility) mainDeclNode += addnodes.desc_sig_keyword(self.visibility, self.visibility)
@ -4192,7 +4227,7 @@ class Symbol:
continue continue
# only add a declaration if we our self are from a declaration # only add a declaration if we our self are from a declaration
if self.declaration: if self.declaration:
decl = ASTDeclaration('templateParam', None, None, None, None, tp, None) decl = ASTDeclaration(objectType='templateParam', declaration=tp)
else: else:
decl = None decl = None
nne = ASTNestedNameElement(tp.get_identifier(), None) nne = ASTNestedNameElement(tp.get_identifier(), None)
@ -4207,7 +4242,7 @@ class Symbol:
if nn is None: if nn is None:
continue continue
# (comparing to the template params: we have checked that we are a declaration) # (comparing to the template params: we have checked that we are a declaration)
decl = ASTDeclaration('functionParam', None, None, None, None, fp, None) decl = ASTDeclaration(objectType='functionParam', declaration=fp)
assert not nn.rooted assert not nn.rooted
assert len(nn.names) == 1 assert len(nn.names) == 1
self._add_symbols(nn, [], decl, self.docname, self.line) self._add_symbols(nn, [], decl, self.docname, self.line)
@ -6504,6 +6539,13 @@ class DefinitionParser(BaseParser):
declSpecs = self._parse_decl_specs(outer=outer, typed=False) declSpecs = self._parse_decl_specs(outer=outer, typed=False)
decl = self._parse_declarator(named=True, paramMode=outer, decl = self._parse_declarator(named=True, paramMode=outer,
typed=False) typed=False)
mustEnd = True
if outer == 'function':
# Allow trailing requires on functions.
self.skip_ws()
if re.compile(r'requires\b').match(self.definition, self.pos):
mustEnd = False
if mustEnd:
self.assert_end(allowSemicolon=True) self.assert_end(allowSemicolon=True)
except DefinitionError as exUntyped: except DefinitionError as exUntyped:
if outer == 'type': if outer == 'type':
@ -6761,7 +6803,8 @@ class DefinitionParser(BaseParser):
err = eParam err = eParam
self.skip_ws() self.skip_ws()
if self.skip_string('>'): if self.skip_string('>'):
return ASTTemplateParams(templateParams) requiresClause = self._parse_requires_clause()
return ASTTemplateParams(templateParams, requiresClause)
elif self.skip_string(','): elif self.skip_string(','):
continue continue
else: else:
@ -6883,6 +6926,8 @@ class DefinitionParser(BaseParser):
return ASTTemplateDeclarationPrefix(None) return ASTTemplateDeclarationPrefix(None)
else: else:
raise e raise e
if objectType == 'concept' and params.requiresClause is not None:
self.fail('requires-clause not allowed for concept')
else: else:
params = self._parse_template_introduction() params = self._parse_template_introduction()
if not params: if not params:
@ -6931,7 +6976,7 @@ class DefinitionParser(BaseParser):
newTemplates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = [] newTemplates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = []
for _i in range(numExtra): for _i in range(numExtra):
newTemplates.append(ASTTemplateParams([])) newTemplates.append(ASTTemplateParams([], requiresClause=None))
if templatePrefix and not isMemberInstantiation: if templatePrefix and not isMemberInstantiation:
newTemplates.extend(templatePrefix.templates) newTemplates.extend(templatePrefix.templates)
templatePrefix = ASTTemplateDeclarationPrefix(newTemplates) templatePrefix = ASTTemplateDeclarationPrefix(newTemplates)
@ -6947,7 +6992,6 @@ class DefinitionParser(BaseParser):
raise Exception('Internal error, unknown directiveType "%s".' % directiveType) raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
visibility = None visibility = None
templatePrefix = None templatePrefix = None
requiresClause = None
trailingRequiresClause = None trailingRequiresClause = None
declaration: Any = None declaration: Any = None
@ -6955,10 +6999,8 @@ class DefinitionParser(BaseParser):
if self.match(_visibility_re): if self.match(_visibility_re):
visibility = self.matched_text visibility = self.matched_text
if objectType in ('type', 'concept', 'member', 'function', 'class'): if objectType in ('type', 'concept', 'member', 'function', 'class', 'union'):
templatePrefix = self._parse_template_declaration_prefix(objectType) templatePrefix = self._parse_template_declaration_prefix(objectType)
if objectType == 'function' and templatePrefix is not None:
requiresClause = self._parse_requires_clause()
if objectType == 'type': if objectType == 'type':
prevErrors = [] prevErrors = []
@ -6984,7 +7026,6 @@ class DefinitionParser(BaseParser):
declaration = self._parse_type_with_init(named=True, outer='member') declaration = self._parse_type_with_init(named=True, outer='member')
elif objectType == 'function': elif objectType == 'function':
declaration = self._parse_type(named=True, outer='function') declaration = self._parse_type(named=True, outer='function')
if templatePrefix is not None:
trailingRequiresClause = self._parse_requires_clause() trailingRequiresClause = self._parse_requires_clause()
elif objectType == 'class': elif objectType == 'class':
declaration = self._parse_class() declaration = self._parse_class()
@ -7003,7 +7044,7 @@ class DefinitionParser(BaseParser):
self.skip_ws() self.skip_ws()
semicolon = self.skip_string(';') semicolon = self.skip_string(';')
return ASTDeclaration(objectType, directiveType, visibility, return ASTDeclaration(objectType, directiveType, visibility,
templatePrefix, requiresClause, declaration, templatePrefix, declaration,
trailingRequiresClause, semicolon) trailingRequiresClause, semicolon)
def parse_namespace_object(self) -> ASTNamespace: def parse_namespace_object(self) -> ASTNamespace:
@ -8048,7 +8089,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return { return {
'version': 'builtin', 'version': 'builtin',
'env_version': 6, 'env_version': 7,
'parallel_read_safe': True, 'parallel_read_safe': True,
'parallel_write_safe': True, 'parallel_write_safe': True,
} }

View File

@ -44,7 +44,11 @@ _SINGLETONS = ("None", "True", "False", "Ellipsis")
class Deque(collections.deque): class Deque(collections.deque):
"""A subclass of deque with an additional `.Deque.get` method.""" """
A subclass of deque that mimics ``pockets.iterators.modify_iter``.
The `.Deque.get` and `.Deque.next` methods are added.
"""
sentinel = object() sentinel = object()
@ -55,6 +59,12 @@ class Deque(collections.deque):
""" """
return self[n] if n < len(self) else self.sentinel return self[n] if n < len(self) else self.sentinel
def next(self) -> Any:
if self:
return super().popleft()
else:
raise StopIteration
def _convert_type_spec(_type: str, translations: Dict[str, str] = {}) -> str: def _convert_type_spec(_type: str, translations: Dict[str, str] = {}) -> str:
"""Convert type specification to reference in reST.""" """Convert type specification to reference in reST."""
@ -238,7 +248,7 @@ class GoogleDocstring:
line = self._lines.get(0) line = self._lines.get(0)
while(not self._is_section_break() and while(not self._is_section_break() and
(not line or self._is_indented(line, indent))): (not line or self._is_indented(line, indent))):
lines.append(self._lines.popleft()) lines.append(self._lines.next())
line = self._lines.get(0) line = self._lines.get(0)
return lines return lines
@ -247,20 +257,20 @@ class GoogleDocstring:
while (self._lines and while (self._lines and
self._lines.get(0) and self._lines.get(0) and
not self._is_section_header()): not self._is_section_header()):
lines.append(self._lines.popleft()) lines.append(self._lines.next())
return lines return lines
def _consume_empty(self) -> List[str]: def _consume_empty(self) -> List[str]:
lines = [] lines = []
line = self._lines.get(0) line = self._lines.get(0)
while self._lines and not line: while self._lines and not line:
lines.append(self._lines.popleft()) lines.append(self._lines.next())
line = self._lines.get(0) line = self._lines.get(0)
return lines return lines
def _consume_field(self, parse_type: bool = True, prefer_type: bool = False def _consume_field(self, parse_type: bool = True, prefer_type: bool = False
) -> Tuple[str, str, List[str]]: ) -> Tuple[str, str, List[str]]:
line = self._lines.popleft() line = self._lines.next()
before, colon, after = self._partition_field_on_colon(line) before, colon, after = self._partition_field_on_colon(line)
_name, _type, _desc = before, '', after _name, _type, _desc = before, '', after
@ -298,7 +308,7 @@ class GoogleDocstring:
return fields return fields
def _consume_inline_attribute(self) -> Tuple[str, List[str]]: def _consume_inline_attribute(self) -> Tuple[str, List[str]]:
line = self._lines.popleft() line = self._lines.next()
_type, colon, _desc = self._partition_field_on_colon(line) _type, colon, _desc = self._partition_field_on_colon(line)
if not colon or not _desc: if not colon or not _desc:
_type, _desc = _desc, _type _type, _desc = _desc, _type
@ -336,7 +346,7 @@ class GoogleDocstring:
return lines return lines
def _consume_section_header(self) -> str: def _consume_section_header(self) -> str:
section = self._lines.popleft() section = self._lines.next()
stripped_section = section.strip(':') stripped_section = section.strip(':')
if stripped_section.lower() in self._sections: if stripped_section.lower() in self._sections:
section = stripped_section section = stripped_section
@ -345,14 +355,14 @@ class GoogleDocstring:
def _consume_to_end(self) -> List[str]: def _consume_to_end(self) -> List[str]:
lines = [] lines = []
while self._lines: while self._lines:
lines.append(self._lines.popleft()) lines.append(self._lines.next())
return lines return lines
def _consume_to_next_section(self) -> List[str]: def _consume_to_next_section(self) -> List[str]:
self._consume_empty() self._consume_empty()
lines = [] lines = []
while not self._is_section_break(): while not self._is_section_break():
lines.append(self._lines.popleft()) lines.append(self._lines.next())
return lines + self._consume_empty() return lines + self._consume_empty()
def _dedent(self, lines: List[str], full: bool = False) -> List[str]: def _dedent(self, lines: List[str], full: bool = False) -> List[str]:
@ -1155,7 +1165,7 @@ class NumpyDocstring(GoogleDocstring):
def _consume_field(self, parse_type: bool = True, prefer_type: bool = False def _consume_field(self, parse_type: bool = True, prefer_type: bool = False
) -> Tuple[str, str, List[str]]: ) -> Tuple[str, str, List[str]]:
line = self._lines.popleft() line = self._lines.next()
if parse_type: if parse_type:
_name, _, _type = self._partition_field_on_colon(line) _name, _, _type = self._partition_field_on_colon(line)
else: else:
@ -1186,10 +1196,10 @@ class NumpyDocstring(GoogleDocstring):
return self._consume_fields(prefer_type=True) return self._consume_fields(prefer_type=True)
def _consume_section_header(self) -> str: def _consume_section_header(self) -> str:
section = self._lines.popleft() section = self._lines.next()
if not _directive_regex.match(section): if not _directive_regex.match(section):
# Consume the header underline # Consume the header underline
self._lines.popleft() self._lines.next()
return section return section
def _is_section_break(self) -> bool: def _is_section_break(self) -> bool:

View File

@ -161,7 +161,7 @@ class SphinxComponentRegistry:
f"'env'argument. Report this bug to the developers of your custom builder, " f"'env'argument. Report this bug to the developers of your custom builder, "
f"this is likely not a issue with Sphinx. The 'env' argument will be required " f"this is likely not a issue with Sphinx. The 'env' argument will be required "
f"from Sphinx 7.", RemovedInSphinx70Warning, stacklevel=2) f"from Sphinx 7.", RemovedInSphinx70Warning, stacklevel=2)
builder = self.builders[name](app, env=...) # type: ignore[arg-type] builder = self.builders[name](app)
if env is not None: if env is not None:
builder.set_environment(env) builder.set_environment(env)
return builder return builder

View File

@ -891,8 +891,33 @@ def test_domain_cpp_ast_requires_clauses():
{4: 'I0EIQaa1A1BE1fvv'}) {4: 'I0EIQaa1A1BE1fvv'})
check('function', 'template<typename T> requires A || B or C void f()', check('function', 'template<typename T> requires A || B or C void f()',
{4: 'I0EIQoo1Aoo1B1CE1fvv'}) {4: 'I0EIQoo1Aoo1B1CE1fvv'})
check('function', 'void f() requires A || B || C',
{4: 'IQoo1Aoo1B1CE1fv'})
check('function', 'Foo() requires A || B || C',
{4: 'IQoo1Aoo1B1CE3Foov'})
check('function', 'template<typename T> requires A && B || C and D void f()', check('function', 'template<typename T> requires A && B || C and D void f()',
{4: 'I0EIQooaa1A1Baa1C1DE1fvv'}) {4: 'I0EIQooaa1A1Baa1C1DE1fvv'})
check('function',
'template<typename T> requires R<T> ' +
'template<typename U> requires S<T> ' +
'void A<T>::f() requires B',
{4: 'I0EIQ1RI1TEEI0EIQaa1SI1TE1BEN1AI1TE1fEvv'})
check('function',
'template<template<typename T> requires R<T> typename X> ' +
'void f()',
{2: 'II0EIQ1RI1TEE0E1fv', 4: 'II0EIQ1RI1TEE0E1fvv'})
check('type',
'template<typename T> requires IsValid<T> {key}T = true_type',
{4: 'I0EIQ7IsValidI1TEE1T'}, key='using')
check('class',
'template<typename T> requires IsValid<T> {key}T : Base',
{4: 'I0EIQ7IsValidI1TEE1T'}, key='class')
check('union',
'template<typename T> requires IsValid<T> {key}T',
{4: 'I0EIQ7IsValidI1TEE1T'}, key='union')
check('member',
'template<typename T> requires IsValid<T> int Val = 7',
{4: 'I0EIQ7IsValidI1TEE3Val'})
def test_domain_cpp_ast_template_args(): def test_domain_cpp_ast_template_args():