summaryrefslogtreecommitdiff
path: root/sphinx/domains/cpp.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/domains/cpp.py')
-rw-r--r--sphinx/domains/cpp.py125
1 files changed, 83 insertions, 42 deletions
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py
index d7dbc8b48..2534f6239 100644
--- a/sphinx/domains/cpp.py
+++ b/sphinx/domains/cpp.py
@@ -3687,17 +3687,23 @@ class ASTTemplateParamNonType(ASTTemplateParam):
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
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
res = []
res.append("I")
for param in self.params:
res.append(param.get_id(version))
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)
def _stringify(self, transform: StringifyTransform) -> str:
@@ -3705,6 +3711,9 @@ class ASTTemplateParams(ASTBase):
res.append("template<")
res.append(", ".join(transform(a) for a in self.params))
res.append("> ")
+ if self.requiresClause is not None:
+ res.append(transform(self.requiresClause))
+ res.append(" ")
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
@@ -3719,6 +3728,9 @@ class ASTTemplateParams(ASTBase):
first = False
param.describe_signature(signode, mode, env, symbol)
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(
self, parentNode: desc_signature, mode: str, env: "BuildEnvironment",
@@ -3743,6 +3755,11 @@ class ASTTemplateParams(ASTBase):
if lineSpec and not first:
lineNode = makeLine(parentNode)
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
@@ -3861,12 +3878,24 @@ class ASTTemplateDeclarationPrefix(ASTBase):
# templates is None means it's an explicit instantiation of a variable
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
- # this is not part of a normal name mangling system
+ # This is not part of the Itanium ABI mangling system.
res = []
- for t in self.templates:
- res.append(t.get_id(version))
+ 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))
return ''.join(res)
def _stringify(self, transform: StringifyTransform) -> str:
@@ -3889,7 +3918,7 @@ class ASTRequiresClause(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str:
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:
signode += addnodes.desc_sig_keyword('requires', 'requires')
signode += addnodes.desc_sig_space()
@@ -3900,16 +3929,16 @@ class ASTRequiresClause(ASTBase):
################################################################################
class ASTDeclaration(ASTBase):
- def __init__(self, objectType: str, directiveType: str, visibility: str,
- templatePrefix: ASTTemplateDeclarationPrefix,
- requiresClause: ASTRequiresClause, declaration: Any,
- trailingRequiresClause: ASTRequiresClause,
+ def __init__(self, objectType: str, directiveType: Optional[str] = None,
+ visibility: Optional[str] = None,
+ templatePrefix: Optional[ASTTemplateDeclarationPrefix] = None,
+ declaration: Any = None,
+ trailingRequiresClause: Optional[ASTRequiresClause] = None,
semicolon: bool = False) -> None:
self.objectType = objectType
self.directiveType = directiveType
self.visibility = visibility
self.templatePrefix = templatePrefix
- self.requiresClause = requiresClause
self.declaration = declaration
self.trailingRequiresClause = trailingRequiresClause
self.semicolon = semicolon
@@ -3920,11 +3949,10 @@ class ASTDeclaration(ASTBase):
def clone(self) -> "ASTDeclaration":
templatePrefixClone = self.templatePrefix.clone() if self.templatePrefix else None
- requiresClasueClone = self.requiresClause.clone() if self.requiresClause else None
trailingRequiresClasueClone = self.trailingRequiresClause.clone() \
if self.trailingRequiresClause else None
return ASTDeclaration(self.objectType, self.directiveType, self.visibility,
- templatePrefixClone, requiresClasueClone,
+ templatePrefixClone,
self.declaration.clone(), trailingRequiresClasueClone,
self.semicolon)
@@ -3940,7 +3968,7 @@ class ASTDeclaration(ASTBase):
def get_id(self, version: int, prefixed: bool = True) -> str:
if version == 1:
- if self.templatePrefix:
+ if self.templatePrefix or self.trailingRequiresClause:
raise NoOldIdError()
if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
return self.enumeratorScopedSymbol.declaration.get_id(version)
@@ -3952,16 +3980,31 @@ class ASTDeclaration(ASTBase):
res = [_id_prefix[version]]
else:
res = []
- if self.templatePrefix:
- res.append(self.templatePrefix.get_id(version))
- if self.requiresClause or self.trailingRequiresClause:
+ # (See also https://github.com/sphinx-doc/sphinx/pull/10286#issuecomment-1168102147)
+ # The first implementation of requires clauses only supported a single clause after the
+ # 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:
raise NoOldIdError()
res.append('IQ')
- if self.requiresClause and self.trailingRequiresClause:
+ if requiresClauseInLast and self.trailingRequiresClause:
+ # make a conjunction of them
res.append('aa')
- if self.requiresClause:
- res.append(self.requiresClause.expr.get_id(version))
+ if requiresClauseInLast:
+ res.append(requiresClauseInLast.expr.get_id(version))
if self.trailingRequiresClause:
res.append(self.trailingRequiresClause.expr.get_id(version))
res.append('E')
@@ -3978,9 +4021,6 @@ class ASTDeclaration(ASTBase):
res.append(' ')
if self.templatePrefix:
res.append(transform(self.templatePrefix))
- if self.requiresClause:
- res.append(transform(self.requiresClause))
- res.append(' ')
res.append(transform(self.declaration))
if self.trailingRequiresClause:
res.append(' ')
@@ -4005,11 +4045,6 @@ class ASTDeclaration(ASTBase):
self.templatePrefix.describe_signature(signode, mode, env,
symbol=self.symbol,
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
if self.visibility and self.visibility != "public":
mainDeclNode += addnodes.desc_sig_keyword(self.visibility, self.visibility)
@@ -4192,7 +4227,7 @@ class Symbol:
continue
# only add a declaration if we our self are from a declaration
if self.declaration:
- decl = ASTDeclaration('templateParam', None, None, None, None, tp, None)
+ decl = ASTDeclaration(objectType='templateParam', declaration=tp)
else:
decl = None
nne = ASTNestedNameElement(tp.get_identifier(), None)
@@ -4207,7 +4242,7 @@ class Symbol:
if nn is None:
continue
# (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 len(nn.names) == 1
self._add_symbols(nn, [], decl, self.docname, self.line)
@@ -6504,7 +6539,14 @@ class DefinitionParser(BaseParser):
declSpecs = self._parse_decl_specs(outer=outer, typed=False)
decl = self._parse_declarator(named=True, paramMode=outer,
typed=False)
- self.assert_end(allowSemicolon=True)
+ 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)
except DefinitionError as exUntyped:
if outer == 'type':
desc = "If just a name"
@@ -6761,7 +6803,8 @@ class DefinitionParser(BaseParser):
err = eParam
self.skip_ws()
if self.skip_string('>'):
- return ASTTemplateParams(templateParams)
+ requiresClause = self._parse_requires_clause()
+ return ASTTemplateParams(templateParams, requiresClause)
elif self.skip_string(','):
continue
else:
@@ -6883,6 +6926,8 @@ class DefinitionParser(BaseParser):
return ASTTemplateDeclarationPrefix(None)
else:
raise e
+ if objectType == 'concept' and params.requiresClause is not None:
+ self.fail('requires-clause not allowed for concept')
else:
params = self._parse_template_introduction()
if not params:
@@ -6931,7 +6976,7 @@ class DefinitionParser(BaseParser):
newTemplates: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] = []
for _i in range(numExtra):
- newTemplates.append(ASTTemplateParams([]))
+ newTemplates.append(ASTTemplateParams([], requiresClause=None))
if templatePrefix and not isMemberInstantiation:
newTemplates.extend(templatePrefix.templates)
templatePrefix = ASTTemplateDeclarationPrefix(newTemplates)
@@ -6947,7 +6992,6 @@ class DefinitionParser(BaseParser):
raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
visibility = None
templatePrefix = None
- requiresClause = None
trailingRequiresClause = None
declaration: Any = None
@@ -6955,10 +6999,8 @@ class DefinitionParser(BaseParser):
if self.match(_visibility_re):
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)
- if objectType == 'function' and templatePrefix is not None:
- requiresClause = self._parse_requires_clause()
if objectType == 'type':
prevErrors = []
@@ -6984,8 +7026,7 @@ class DefinitionParser(BaseParser):
declaration = self._parse_type_with_init(named=True, outer='member')
elif objectType == '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':
declaration = self._parse_class()
elif objectType == 'union':
@@ -7003,7 +7044,7 @@ class DefinitionParser(BaseParser):
self.skip_ws()
semicolon = self.skip_string(';')
return ASTDeclaration(objectType, directiveType, visibility,
- templatePrefix, requiresClause, declaration,
+ templatePrefix, declaration,
trailingRequiresClause, semicolon)
def parse_namespace_object(self) -> ASTNamespace:
@@ -8048,7 +8089,7 @@ def setup(app: Sphinx) -> Dict[str, Any]:
return {
'version': 'builtin',
- 'env_version': 6,
+ 'env_version': 7,
'parallel_read_safe': True,
'parallel_write_safe': True,
}