diff options
Diffstat (limited to '')
-rw-r--r-- | sphinx/domains/cpp/_ast.py | 3635 |
1 files changed, 3635 insertions, 0 deletions
diff --git a/sphinx/domains/cpp/_ast.py b/sphinx/domains/cpp/_ast.py new file mode 100644 index 0000000..ad57695 --- /dev/null +++ b/sphinx/domains/cpp/_ast.py @@ -0,0 +1,3635 @@ +from __future__ import annotations + +from typing import TYPE_CHECKING, Any + +from docutils import nodes + +from sphinx import addnodes +from sphinx.domains.cpp._ids import ( + _id_char_from_prefix, + _id_explicit_cast, + _id_fundamental_v1, + _id_fundamental_v2, + _id_operator_unary_v2, + _id_operator_v1, + _id_operator_v2, + _id_prefix, + _id_shorthands_v1, + _max_id, +) +from sphinx.util.cfamily import ( + ASTAttributeList, + ASTBaseBase, + ASTBaseParenExprList, + NoOldIdError, + StringifyTransform, + UnsupportedMultiCharacterCharLiteral, + verify_description_mode, +) + +if TYPE_CHECKING: + + from docutils.nodes import Element, TextElement + + from sphinx.addnodes import desc_signature + from sphinx.domains.cpp._symbol import Symbol + from sphinx.environment import BuildEnvironment + + +class ASTBase(ASTBaseBase): + pass + + +# Names +################################################################################ + +class ASTIdentifier(ASTBase): + def __init__(self, identifier: str) -> None: + assert identifier is not None + assert len(identifier) != 0 + self.identifier = identifier + + # ASTBaseBase already implements this method, + # but specialising it here improves performance + def __eq__(self, other: object) -> bool: + if type(other) is not ASTIdentifier: + return NotImplemented + return self.identifier == other.identifier + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.identifier) + + def is_anon(self) -> bool: + return self.identifier[0] == '@' + + def get_id(self, version: int) -> str: + if self.is_anon() and version < 3: + raise NoOldIdError + if version == 1: + if self.identifier == 'size_t': + return 's' + else: + return self.identifier + if self.identifier == "std": + return 'St' + elif self.identifier[0] == "~": + # a destructor, just use an arbitrary version of dtors + return 'D0' + else: + if self.is_anon(): + return 'Ut%d_%s' % (len(self.identifier) - 1, self.identifier[1:]) + else: + return str(len(self.identifier)) + self.identifier + + # and this is where we finally make a difference between __str__ and the display string + + def __str__(self) -> str: + return self.identifier + + def get_display_string(self) -> str: + return "[anonymous]" if self.is_anon() else self.identifier + + def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment, + prefix: str, templateArgs: str, symbol: Symbol) -> None: + verify_description_mode(mode) + if self.is_anon(): + node = addnodes.desc_sig_name(text="[anonymous]") + else: + node = addnodes.desc_sig_name(self.identifier, self.identifier) + if mode == 'markType': + targetText = prefix + self.identifier + templateArgs + pnode = addnodes.pending_xref('', refdomain='cpp', + reftype='identifier', + reftarget=targetText, modname=None, + classname=None) + pnode['cpp:parent_key'] = symbol.get_lookup_key() + pnode += node + signode += pnode + elif mode == 'lastIsName': + nameNode = addnodes.desc_name() + nameNode += node + signode += nameNode + elif mode == 'noneIsName': + signode += node + elif mode == 'param': + node['classes'].append('sig-param') + signode += node + elif mode == 'udl': + # the target is 'operator""id' instead of just 'id' + assert len(prefix) == 0 + assert len(templateArgs) == 0 + assert not self.is_anon() + targetText = 'operator""' + self.identifier + pnode = addnodes.pending_xref('', refdomain='cpp', + reftype='identifier', + reftarget=targetText, modname=None, + classname=None) + pnode['cpp:parent_key'] = symbol.get_lookup_key() + pnode += node + signode += pnode + else: + raise Exception('Unknown description mode: %s' % mode) + + +class ASTNestedNameElement(ASTBase): + def __init__(self, identOrOp: ASTIdentifier | ASTOperator, + templateArgs: ASTTemplateArgs | None) -> None: + self.identOrOp = identOrOp + self.templateArgs = templateArgs + + def is_operator(self) -> bool: + return False + + def get_id(self, version: int) -> str: + res = self.identOrOp.get_id(version) + if self.templateArgs: + res += self.templateArgs.get_id(version) + return res + + def _stringify(self, transform: StringifyTransform) -> str: + res = transform(self.identOrOp) + if self.templateArgs: + res += transform(self.templateArgs) + return res + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, prefix: str, symbol: Symbol) -> None: + tArgs = str(self.templateArgs) if self.templateArgs is not None else '' + self.identOrOp.describe_signature(signode, mode, env, prefix, tArgs, symbol) + if self.templateArgs is not None: + self.templateArgs.describe_signature(signode, 'markType', env, symbol) + + +class ASTNestedName(ASTBase): + def __init__(self, names: list[ASTNestedNameElement], + templates: list[bool], rooted: bool) -> None: + assert len(names) > 0 + self.names = names + self.templates = templates + assert len(self.names) == len(self.templates) + self.rooted = rooted + + @property + def name(self) -> ASTNestedName: + return self + + def num_templates(self) -> int: + count = 0 + for n in self.names: + if n.is_operator(): + continue + if n.templateArgs: + count += 1 + return count + + def get_id(self, version: int, modifiers: str = '') -> str: + if version == 1: + tt = str(self) + if tt in _id_shorthands_v1: + return _id_shorthands_v1[tt] + else: + return '::'.join(n.get_id(version) for n in self.names) + + res = [] + if len(self.names) > 1 or len(modifiers) > 0: + res.append('N') + res.append(modifiers) + res.extend(n.get_id(version) for n in self.names) + if len(self.names) > 1 or len(modifiers) > 0: + res.append('E') + return ''.join(res) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.rooted: + res.append('') + for i in range(len(self.names)): + n = self.names[i] + if self.templates[i]: + res.append("template " + transform(n)) + else: + res.append(transform(n)) + return '::'.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + # just print the name part, with template args, not template params + if mode == 'noneIsName': + if self.rooted: + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO + signode += nodes.Text('::') + for i in range(len(self.names)): + if i != 0: + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO + signode += nodes.Text('::blah') + n = self.names[i] + if self.templates[i]: + unreachable = "Can this happen?" + raise AssertionError(unreachable) # TODO + signode += nodes.Text("template") + signode += nodes.Text(" ") + n.describe_signature(signode, mode, env, '', symbol) + elif mode == 'param': + assert not self.rooted, str(self) + assert len(self.names) == 1 + assert not self.templates[0] + self.names[0].describe_signature(signode, 'param', env, '', symbol) + elif mode in ('markType', 'lastIsName', 'markName'): + # Each element should be a pending xref targeting the complete + # prefix. however, only the identifier part should be a link, such + # that template args can be a link as well. + # For 'lastIsName' we should also prepend template parameter lists. + templateParams: list[Any] = [] + if mode == 'lastIsName': + assert symbol is not None + if symbol.declaration.templatePrefix is not None: + templateParams = symbol.declaration.templatePrefix.templates + iTemplateParams = 0 + templateParamsPrefix = '' + prefix = '' + first = True + names = self.names[:-1] if mode == 'lastIsName' else self.names + # If lastIsName, then wrap all of the prefix in a desc_addname, + # else append directly to signode. + # NOTE: Breathe previously relied on the prefix being in the desc_addname node, + # so it can remove it in inner declarations. + dest = signode + if mode == 'lastIsName': + dest = addnodes.desc_addname() + if self.rooted: + prefix += '::' + if mode == 'lastIsName' and len(names) == 0: + signode += addnodes.desc_sig_punctuation('::', '::') + else: + dest += addnodes.desc_sig_punctuation('::', '::') + for i in range(len(names)): + nne = names[i] + template = self.templates[i] + if not first: + dest += addnodes.desc_sig_punctuation('::', '::') + prefix += '::' + if template: + dest += addnodes.desc_sig_keyword('template', 'template') + dest += addnodes.desc_sig_space() + first = False + txt_nne = str(nne) + if txt_nne != '': + if nne.templateArgs and iTemplateParams < len(templateParams): + templateParamsPrefix += str(templateParams[iTemplateParams]) + iTemplateParams += 1 + nne.describe_signature(dest, 'markType', + env, templateParamsPrefix + prefix, symbol) + prefix += txt_nne + if mode == 'lastIsName': + if len(self.names) > 1: + dest += addnodes.desc_sig_punctuation('::', '::') + signode += dest + if self.templates[-1]: + signode += addnodes.desc_sig_keyword('template', 'template') + signode += addnodes.desc_sig_space() + self.names[-1].describe_signature(signode, mode, env, '', symbol) + else: + raise Exception('Unknown description mode: %s' % mode) + + +################################################################################ +# Expressions +################################################################################ + +class ASTExpression(ASTBase): + def get_id(self, version: int) -> str: + raise NotImplementedError(repr(self)) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + raise NotImplementedError(repr(self)) + + +# Primary expressions +################################################################################ + +class ASTLiteral(ASTExpression): + pass + + +class ASTPointerLiteral(ASTLiteral): + def _stringify(self, transform: StringifyTransform) -> str: + return 'nullptr' + + def get_id(self, version: int) -> str: + return 'LDnE' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('nullptr', 'nullptr') + + +class ASTBooleanLiteral(ASTLiteral): + def __init__(self, value: bool) -> None: + self.value = value + + def _stringify(self, transform: StringifyTransform) -> str: + if self.value: + return 'true' + else: + return 'false' + + def get_id(self, version: int) -> str: + if self.value: + return 'L1E' + else: + return 'L0E' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword(str(self), str(self)) + + +class ASTNumberLiteral(ASTLiteral): + def __init__(self, data: str) -> None: + self.data = data + + def _stringify(self, transform: StringifyTransform) -> str: + return self.data + + def get_id(self, version: int) -> str: + # TODO: floats should be mangled by writing the hex of the binary representation + return "L%sE" % self.data.replace("'", "") + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_literal_number(self.data, self.data) + + +class ASTStringLiteral(ASTLiteral): + def __init__(self, data: str) -> None: + self.data = data + + def _stringify(self, transform: StringifyTransform) -> str: + return self.data + + def get_id(self, version: int) -> str: + # note: the length is not really correct with escaping + return "LA%d_KcE" % (len(self.data) - 2) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_literal_string(self.data, self.data) + + +class ASTCharLiteral(ASTLiteral): + def __init__(self, prefix: str, data: str) -> None: + self.prefix = prefix # may be None when no prefix + self.data = data + assert prefix in _id_char_from_prefix + self.type = _id_char_from_prefix[prefix] + decoded = data.encode().decode('unicode-escape') + if len(decoded) == 1: + self.value = ord(decoded) + else: + raise UnsupportedMultiCharacterCharLiteral(decoded) + + def _stringify(self, transform: StringifyTransform) -> str: + if self.prefix is None: + return "'" + self.data + "'" + else: + return self.prefix + "'" + self.data + "'" + + def get_id(self, version: int) -> str: + # TODO: the ID should be have L E around it + return self.type + str(self.value) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + if self.prefix is not None: + signode += addnodes.desc_sig_keyword(self.prefix, self.prefix) + txt = "'" + self.data + "'" + signode += addnodes.desc_sig_literal_char(txt, txt) + + +class ASTUserDefinedLiteral(ASTLiteral): + def __init__(self, literal: ASTLiteral, ident: ASTIdentifier) -> None: + self.literal = literal + self.ident = ident + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.literal) + transform(self.ident) + + def get_id(self, version: int) -> str: + # mangle as if it was a function call: ident(literal) + return f'clL_Zli{self.ident.get_id(version)}E{self.literal.get_id(version)}E' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.literal.describe_signature(signode, mode, env, symbol) + self.ident.describe_signature(signode, "udl", env, "", "", symbol) + + +################################################################################ + +class ASTThisLiteral(ASTExpression): + def _stringify(self, transform: StringifyTransform) -> str: + return "this" + + def get_id(self, version: int) -> str: + return "fpT" + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('this', 'this') + + +class ASTFoldExpr(ASTExpression): + def __init__(self, leftExpr: ASTExpression | None, + op: str, rightExpr: ASTExpression | None) -> None: + assert leftExpr is not None or rightExpr is not None + self.leftExpr = leftExpr + self.op = op + self.rightExpr = rightExpr + + def _stringify(self, transform: StringifyTransform) -> str: + res = ['('] + if self.leftExpr: + res.append(transform(self.leftExpr)) + res.append(' ') + res.append(self.op) + res.append(' ') + res.append('...') + if self.rightExpr: + res.append(' ') + res.append(self.op) + res.append(' ') + res.append(transform(self.rightExpr)) + res.append(')') + return ''.join(res) + + def get_id(self, version: int) -> str: + assert version >= 3 + if version == 3: + return str(self) + # https://github.com/itanium-cxx-abi/cxx-abi/pull/67 + res = [] + if self.leftExpr is None: # (... op expr) + res.append('fl') + elif self.rightExpr is None: # (expr op ...) + res.append('fr') + else: # (expr op ... op expr) + # we don't check where the parameter pack is, + # we just always call this a binary left fold + res.append('fL') + res.append(_id_operator_v2[self.op]) + if self.leftExpr: + res.append(self.leftExpr.get_id(version)) + if self.rightExpr: + res.append(self.rightExpr.get_id(version)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_punctuation('(', '(') + if self.leftExpr: + self.leftExpr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator(self.op, self.op) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('...', '...') + if self.rightExpr: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator(self.op, self.op) + signode += addnodes.desc_sig_space() + self.rightExpr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTParenExpr(ASTExpression): + def __init__(self, expr: ASTExpression) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return '(' + transform(self.expr) + ')' + + def get_id(self, version: int) -> str: + return self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_punctuation('(', '(') + self.expr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTIdExpression(ASTExpression): + def __init__(self, name: ASTNestedName) -> None: + # note: this class is basically to cast a nested name as an expression + self.name = name + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.name) + + def get_id(self, version: int) -> str: + return self.name.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.name.describe_signature(signode, mode, env, symbol) + + +# Postfix expressions +################################################################################ + +class ASTPostfixOp(ASTBase): + def get_id(self, idPrefix: str, version: int) -> str: + raise NotImplementedError(repr(self)) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + raise NotImplementedError(repr(self)) + + +class ASTPostfixArray(ASTPostfixOp): + def __init__(self, expr: ASTExpression) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return '[' + transform(self.expr) + ']' + + def get_id(self, idPrefix: str, version: int) -> str: + return 'ix' + idPrefix + self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_punctuation('[', '[') + self.expr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(']', ']') + + +class ASTPostfixMember(ASTPostfixOp): + def __init__(self, name: ASTNestedName) -> None: + self.name = name + + def _stringify(self, transform: StringifyTransform) -> str: + return '.' + transform(self.name) + + def get_id(self, idPrefix: str, version: int) -> str: + return 'dt' + idPrefix + self.name.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_punctuation('.', '.') + self.name.describe_signature(signode, 'noneIsName', env, symbol) + + +class ASTPostfixMemberOfPointer(ASTPostfixOp): + def __init__(self, name: ASTNestedName) -> None: + self.name = name + + def _stringify(self, transform: StringifyTransform) -> str: + return '->' + transform(self.name) + + def get_id(self, idPrefix: str, version: int) -> str: + return 'pt' + idPrefix + self.name.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_operator('->', '->') + self.name.describe_signature(signode, 'noneIsName', env, symbol) + + +class ASTPostfixInc(ASTPostfixOp): + def _stringify(self, transform: StringifyTransform) -> str: + return '++' + + def get_id(self, idPrefix: str, version: int) -> str: + return 'pp' + idPrefix + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_operator('++', '++') + + +class ASTPostfixDec(ASTPostfixOp): + def _stringify(self, transform: StringifyTransform) -> str: + return '--' + + def get_id(self, idPrefix: str, version: int) -> str: + return 'mm' + idPrefix + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_operator('--', '--') + + +class ASTPostfixCallExpr(ASTPostfixOp): + def __init__(self, lst: ASTParenExprList | ASTBracedInitList) -> None: + self.lst = lst + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.lst) + + def get_id(self, idPrefix: str, version: int) -> str: + return ''.join([ + 'cl', + idPrefix, + *(e.get_id(version) for e in self.lst.exprs), + 'E', + ]) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.lst.describe_signature(signode, mode, env, symbol) + + +class ASTPostfixExpr(ASTExpression): + def __init__(self, prefix: ASTType, postFixes: list[ASTPostfixOp]) -> None: + self.prefix = prefix + self.postFixes = postFixes + + def _stringify(self, transform: StringifyTransform) -> str: + return ''.join([transform(self.prefix), *(transform(p) for p in self.postFixes)]) + + def get_id(self, version: int) -> str: + id = self.prefix.get_id(version) + for p in self.postFixes: + id = p.get_id(id, version) + return id + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.prefix.describe_signature(signode, mode, env, symbol) + for p in self.postFixes: + p.describe_signature(signode, mode, env, symbol) + + +class ASTExplicitCast(ASTExpression): + def __init__(self, cast: str, typ: ASTType, expr: ASTExpression) -> None: + assert cast in _id_explicit_cast + self.cast = cast + self.typ = typ + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + res = [self.cast] + res.append('<') + res.append(transform(self.typ)) + res.append('>(') + res.append(transform(self.expr)) + res.append(')') + return ''.join(res) + + def get_id(self, version: int) -> str: + return (_id_explicit_cast[self.cast] + + self.typ.get_id(version) + + self.expr.get_id(version)) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword(self.cast, self.cast) + signode += addnodes.desc_sig_punctuation('<', '<') + self.typ.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation('>', '>') + signode += addnodes.desc_sig_punctuation('(', '(') + self.expr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTTypeId(ASTExpression): + def __init__(self, typeOrExpr: ASTType | ASTExpression, isType: bool) -> None: + self.typeOrExpr = typeOrExpr + self.isType = isType + + def _stringify(self, transform: StringifyTransform) -> str: + return 'typeid(' + transform(self.typeOrExpr) + ')' + + def get_id(self, version: int) -> str: + prefix = 'ti' if self.isType else 'te' + return prefix + self.typeOrExpr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('typeid', 'typeid') + signode += addnodes.desc_sig_punctuation('(', '(') + self.typeOrExpr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +# Unary expressions +################################################################################ + +class ASTUnaryOpExpr(ASTExpression): + def __init__(self, op: str, expr: ASTExpression) -> None: + self.op = op + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + if self.op[0] in 'cn': + return self.op + " " + transform(self.expr) + else: + return self.op + transform(self.expr) + + def get_id(self, version: int) -> str: + return _id_operator_unary_v2[self.op] + self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + if self.op[0] in 'cn': + signode += addnodes.desc_sig_keyword(self.op, self.op) + signode += addnodes.desc_sig_space() + else: + signode += addnodes.desc_sig_operator(self.op, self.op) + self.expr.describe_signature(signode, mode, env, symbol) + + +class ASTSizeofParamPack(ASTExpression): + def __init__(self, identifier: ASTIdentifier) -> None: + self.identifier = identifier + + def _stringify(self, transform: StringifyTransform) -> str: + return "sizeof...(" + transform(self.identifier) + ")" + + def get_id(self, version: int) -> str: + return 'sZ' + self.identifier.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_punctuation('...', '...') + signode += addnodes.desc_sig_punctuation('(', '(') + self.identifier.describe_signature(signode, 'markType', env, + symbol=symbol, prefix="", templateArgs="") + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTSizeofType(ASTExpression): + def __init__(self, typ: ASTType) -> None: + self.typ = typ + + def _stringify(self, transform: StringifyTransform) -> str: + return "sizeof(" + transform(self.typ) + ")" + + def get_id(self, version: int) -> str: + return 'st' + self.typ.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_punctuation('(', '(') + self.typ.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTSizeofExpr(ASTExpression): + def __init__(self, expr: ASTExpression) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return "sizeof " + transform(self.expr) + + def get_id(self, version: int) -> str: + return 'sz' + self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('sizeof', 'sizeof') + signode += addnodes.desc_sig_space() + self.expr.describe_signature(signode, mode, env, symbol) + + +class ASTAlignofExpr(ASTExpression): + def __init__(self, typ: ASTType) -> None: + self.typ = typ + + def _stringify(self, transform: StringifyTransform) -> str: + return "alignof(" + transform(self.typ) + ")" + + def get_id(self, version: int) -> str: + return 'at' + self.typ.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('alignof', 'alignof') + signode += addnodes.desc_sig_punctuation('(', '(') + self.typ.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTNoexceptExpr(ASTExpression): + def __init__(self, expr: ASTExpression) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return 'noexcept(' + transform(self.expr) + ')' + + def get_id(self, version: int) -> str: + return 'nx' + self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('noexcept', 'noexcept') + signode += addnodes.desc_sig_punctuation('(', '(') + self.expr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTNewExpr(ASTExpression): + def __init__(self, rooted: bool, isNewTypeId: bool, typ: ASTType, + initList: ASTParenExprList | ASTBracedInitList) -> None: + self.rooted = rooted + self.isNewTypeId = isNewTypeId + self.typ = typ + self.initList = initList + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.rooted: + res.append('::') + res.append('new ') + # TODO: placement + if self.isNewTypeId: + res.append(transform(self.typ)) + else: + raise AssertionError + if self.initList is not None: + res.append(transform(self.initList)) + return ''.join(res) + + def get_id(self, version: int) -> str: + # the array part will be in the type mangling, so na is not used + res = ['nw'] + # TODO: placement + res.append('_') + res.append(self.typ.get_id(version)) + if self.initList is not None: + res.append(self.initList.get_id(version)) + else: + res.append('E') + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + if self.rooted: + signode += addnodes.desc_sig_punctuation('::', '::') + signode += addnodes.desc_sig_keyword('new', 'new') + signode += addnodes.desc_sig_space() + # TODO: placement + if self.isNewTypeId: + self.typ.describe_signature(signode, mode, env, symbol) + else: + raise AssertionError + if self.initList is not None: + self.initList.describe_signature(signode, mode, env, symbol) + + +class ASTDeleteExpr(ASTExpression): + def __init__(self, rooted: bool, array: bool, expr: ASTExpression) -> None: + self.rooted = rooted + self.array = array + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.rooted: + res.append('::') + res.append('delete ') + if self.array: + res.append('[] ') + res.append(transform(self.expr)) + return ''.join(res) + + def get_id(self, version: int) -> str: + if self.array: + id = "da" + else: + id = "dl" + return id + self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + if self.rooted: + signode += addnodes.desc_sig_punctuation('::', '::') + signode += addnodes.desc_sig_keyword('delete', 'delete') + signode += addnodes.desc_sig_space() + if self.array: + signode += addnodes.desc_sig_punctuation('[]', '[]') + signode += addnodes.desc_sig_space() + self.expr.describe_signature(signode, mode, env, symbol) + + +# Other expressions +################################################################################ + +class ASTCastExpr(ASTExpression): + def __init__(self, typ: ASTType, expr: ASTExpression) -> None: + self.typ = typ + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + res = ['('] + res.append(transform(self.typ)) + res.append(')') + res.append(transform(self.expr)) + return ''.join(res) + + def get_id(self, version: int) -> str: + return 'cv' + self.typ.get_id(version) + self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_punctuation('(', '(') + self.typ.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + self.expr.describe_signature(signode, mode, env, symbol) + + +class ASTBinOpExpr(ASTExpression): + def __init__(self, exprs: list[ASTExpression], ops: list[str]) -> None: + assert len(exprs) > 0 + assert len(exprs) == len(ops) + 1 + self.exprs = exprs + self.ops = ops + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.exprs[0])) + for i in range(1, len(self.exprs)): + res.append(' ') + res.append(self.ops[i - 1]) + res.append(' ') + res.append(transform(self.exprs[i])) + return ''.join(res) + + def get_id(self, version: int) -> str: + assert version >= 2 + res = [] + for i in range(len(self.ops)): + res.append(_id_operator_v2[self.ops[i]]) + res.append(self.exprs[i].get_id(version)) + res.append(self.exprs[-1].get_id(version)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode += addnodes.desc_sig_space() + op = self.ops[i - 1] + if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'): + signode += addnodes.desc_sig_keyword(op, op) + else: + signode += addnodes.desc_sig_operator(op, op) + signode += addnodes.desc_sig_space() + self.exprs[i].describe_signature(signode, mode, env, symbol) + + +class ASTConditionalExpr(ASTExpression): + def __init__(self, ifExpr: ASTExpression, thenExpr: ASTExpression, + elseExpr: ASTExpression) -> None: + self.ifExpr = ifExpr + self.thenExpr = thenExpr + self.elseExpr = elseExpr + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.ifExpr)) + res.append(' ? ') + res.append(transform(self.thenExpr)) + res.append(' : ') + res.append(transform(self.elseExpr)) + return ''.join(res) + + def get_id(self, version: int) -> str: + assert version >= 2 + res = [] + res.append(_id_operator_v2['?']) + res.append(self.ifExpr.get_id(version)) + res.append(self.thenExpr.get_id(version)) + res.append(self.elseExpr.get_id(version)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.ifExpr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator('?', '?') + signode += addnodes.desc_sig_space() + self.thenExpr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator(':', ':') + signode += addnodes.desc_sig_space() + self.elseExpr.describe_signature(signode, mode, env, symbol) + + +class ASTBracedInitList(ASTBase): + def __init__(self, exprs: list[ASTExpression | ASTBracedInitList], + trailingComma: bool) -> None: + self.exprs = exprs + self.trailingComma = trailingComma + + def get_id(self, version: int) -> str: + return "il%sE" % ''.join(e.get_id(version) for e in self.exprs) + + def _stringify(self, transform: StringifyTransform) -> str: + exprs = ', '.join(transform(e) for e in self.exprs) + trailingComma = ',' if self.trailingComma else '' + return f'{{{exprs}{trailingComma}}}' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('{', '{') + first = True + for e in self.exprs: + if not first: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + else: + first = False + e.describe_signature(signode, mode, env, symbol) + if self.trailingComma: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_punctuation('}', '}') + + +class ASTAssignmentExpr(ASTExpression): + def __init__(self, leftExpr: ASTExpression, op: str, + rightExpr: ASTExpression | ASTBracedInitList) -> None: + self.leftExpr = leftExpr + self.op = op + self.rightExpr = rightExpr + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.leftExpr)) + res.append(' ') + res.append(self.op) + res.append(' ') + res.append(transform(self.rightExpr)) + return ''.join(res) + + def get_id(self, version: int) -> str: + # we end up generating the ID from left to right, instead of right to left + res = [] + res.append(_id_operator_v2[self.op]) + res.append(self.leftExpr.get_id(version)) + res.append(self.rightExpr.get_id(version)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.leftExpr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_space() + if ord(self.op[0]) >= ord('a') and ord(self.op[0]) <= ord('z'): + signode += addnodes.desc_sig_keyword(self.op, self.op) + else: + signode += addnodes.desc_sig_operator(self.op, self.op) + signode += addnodes.desc_sig_space() + self.rightExpr.describe_signature(signode, mode, env, symbol) + + +class ASTCommaExpr(ASTExpression): + def __init__(self, exprs: list[ASTExpression]) -> None: + assert len(exprs) > 0 + self.exprs = exprs + + def _stringify(self, transform: StringifyTransform) -> str: + return ', '.join(transform(e) for e in self.exprs) + + def get_id(self, version: int) -> str: + id_ = _id_operator_v2[','] + res = [] + for i in range(len(self.exprs) - 1): + res.append(id_) + res.append(self.exprs[i].get_id(version)) + res.append(self.exprs[-1].get_id(version)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + self.exprs[i].describe_signature(signode, mode, env, symbol) + + +class ASTFallbackExpr(ASTExpression): + def __init__(self, expr: str) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return self.expr + + def get_id(self, version: int) -> str: + return str(self.expr) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += nodes.literal(self.expr, self.expr) + + +################################################################################ +# Types +################################################################################ + +# Things for ASTNestedName +################################################################################ + +class ASTOperator(ASTBase): + def __eq__(self, other: object) -> bool: + raise NotImplementedError(repr(self)) + + def is_anon(self) -> bool: + return False + + def is_operator(self) -> bool: + return True + + def get_id(self, version: int) -> str: + raise NotImplementedError + + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: BuildEnvironment, symbol: Symbol) -> None: + """Render the prefix into signode, and the last part into identnode.""" + raise NotImplementedError + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, prefix: str, templateArgs: str, + symbol: Symbol) -> None: + verify_description_mode(mode) + if mode == 'lastIsName': + mainName = addnodes.desc_name() + self._describe_identifier(mainName, mainName, env, symbol) + signode += mainName + elif mode == 'markType': + targetText = prefix + str(self) + templateArgs + pnode = addnodes.pending_xref('', refdomain='cpp', + reftype='identifier', + reftarget=targetText, modname=None, + classname=None) + pnode['cpp:parent_key'] = symbol.get_lookup_key() + # Render the identifier part, but collapse it into a string + # and make that the a link to this operator. + # E.g., if it is 'operator SomeType', then 'SomeType' becomes + # a link to the operator, not to 'SomeType'. + container = nodes.literal() + self._describe_identifier(signode, container, env, symbol) + txt = container.astext() + pnode += addnodes.desc_name(txt, txt) + signode += pnode + else: + addName = addnodes.desc_addname() + self._describe_identifier(addName, addName, env, symbol) + signode += addName + + +class ASTOperatorBuildIn(ASTOperator): + def __init__(self, op: str) -> None: + self.op = op + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ASTOperatorBuildIn): + return NotImplemented + return self.op == other.op + + def get_id(self, version: int) -> str: + if version == 1: + ids = _id_operator_v1 + if self.op not in ids: + raise NoOldIdError + else: + ids = _id_operator_v2 + if self.op not in ids: + raise Exception('Internal error: Built-in operator "%s" can not ' + 'be mapped to an id.' % self.op) + return ids[self.op] + + def _stringify(self, transform: StringifyTransform) -> str: + if self.op in ('new', 'new[]', 'delete', 'delete[]') or self.op[0] in "abcnox": + return 'operator ' + self.op + else: + return 'operator' + self.op + + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('operator', 'operator') + if self.op in ('new', 'new[]', 'delete', 'delete[]') or self.op[0] in "abcnox": + signode += addnodes.desc_sig_space() + identnode += addnodes.desc_sig_operator(self.op, self.op) + + +class ASTOperatorLiteral(ASTOperator): + def __init__(self, identifier: ASTIdentifier) -> None: + self.identifier = identifier + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ASTOperatorLiteral): + return NotImplemented + return self.identifier == other.identifier + + def get_id(self, version: int) -> str: + if version == 1: + raise NoOldIdError + return 'li' + self.identifier.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + return 'operator""' + transform(self.identifier) + + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('operator', 'operator') + signode += addnodes.desc_sig_literal_string('""', '""') + self.identifier.describe_signature(identnode, 'markType', env, '', '', symbol) + + +class ASTOperatorType(ASTOperator): + def __init__(self, type: ASTType) -> None: + self.type = type + + def __eq__(self, other: object) -> bool: + if not isinstance(other, ASTOperatorType): + return NotImplemented + return self.type == other.type + + def get_id(self, version: int) -> str: + if version == 1: + return 'castto-%s-operator' % self.type.get_id(version) + else: + return 'cv' + self.type.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + return f'operator {transform(self.type)}' + + def get_name_no_template(self) -> str: + return str(self) + + def _describe_identifier(self, signode: TextElement, identnode: TextElement, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('operator', 'operator') + signode += addnodes.desc_sig_space() + self.type.describe_signature(identnode, 'markType', env, symbol) + + +class ASTTemplateArgConstant(ASTBase): + def __init__(self, value: ASTExpression) -> None: + self.value = value + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.value) + + def get_id(self, version: int) -> str: + if version == 1: + return str(self).replace(' ', '-') + if version == 2: + return 'X' + str(self) + 'E' + return 'X' + self.value.get_id(version) + 'E' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.value.describe_signature(signode, mode, env, symbol) + + +class ASTTemplateArgs(ASTBase): + def __init__(self, args: list[ASTType | ASTTemplateArgConstant], + packExpansion: bool) -> None: + assert args is not None + self.args = args + self.packExpansion = packExpansion + + def get_id(self, version: int) -> str: + if version == 1: + res = [] + res.append(':') + res.append('.'.join(a.get_id(version) for a in self.args)) + res.append(':') + return ''.join(res) + + res = [] + res.append('I') + if len(self.args) > 0: + for a in self.args[:-1]: + res.append(a.get_id(version)) + if self.packExpansion: + res.append('J') + res.append(self.args[-1].get_id(version)) + if self.packExpansion: + res.append('E') + res.append('E') + return ''.join(res) + + def _stringify(self, transform: StringifyTransform) -> str: + res = ', '.join(transform(a) for a in self.args) + if self.packExpansion: + res += '...' + return '<' + res + '>' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('<', '<') + first = True + for a in self.args: + if not first: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + first = False + a.describe_signature(signode, 'markType', env, symbol=symbol) + if self.packExpansion: + signode += addnodes.desc_sig_punctuation('...', '...') + signode += addnodes.desc_sig_punctuation('>', '>') + + +# Main part of declarations +################################################################################ + +class ASTTrailingTypeSpec(ASTBase): + def get_id(self, version: int) -> str: + raise NotImplementedError(repr(self)) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + raise NotImplementedError(repr(self)) + + +class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec): + def __init__(self, names: list[str], canonNames: list[str]) -> None: + assert len(names) != 0 + assert len(names) == len(canonNames), (names, canonNames) + self.names = names + # the canonical name list is for ID lookup + self.canonNames = canonNames + + def _stringify(self, transform: StringifyTransform) -> str: + return ' '.join(self.names) + + def get_id(self, version: int) -> str: + if version == 1: + res = [] + for a in self.canonNames: + if a in _id_fundamental_v1: + res.append(_id_fundamental_v1[a]) + else: + res.append(a) + return '-'.join(res) + + txt = ' '.join(self.canonNames) + if txt not in _id_fundamental_v2: + raise Exception( + 'Semi-internal error: Fundamental type "%s" can not be mapped ' + 'to an ID. Is it a true fundamental type? If not so, the ' + 'parser should have rejected it.' % txt) + return _id_fundamental_v2[txt] + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + first = True + for n in self.names: + if not first: + signode += addnodes.desc_sig_space() + else: + first = False + signode += addnodes.desc_sig_keyword_type(n, n) + + +class ASTTrailingTypeSpecDecltypeAuto(ASTTrailingTypeSpec): + def _stringify(self, transform: StringifyTransform) -> str: + return 'decltype(auto)' + + def get_id(self, version: int) -> str: + if version == 1: + raise NoOldIdError + return 'Dc' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('decltype', 'decltype') + signode += addnodes.desc_sig_punctuation('(', '(') + signode += addnodes.desc_sig_keyword('auto', 'auto') + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTTrailingTypeSpecDecltype(ASTTrailingTypeSpec): + def __init__(self, expr: ASTExpression) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return 'decltype(' + transform(self.expr) + ')' + + def get_id(self, version: int) -> str: + if version == 1: + raise NoOldIdError + return 'DT' + self.expr.get_id(version) + "E" + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('decltype', 'decltype') + signode += addnodes.desc_sig_punctuation('(', '(') + self.expr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): + def __init__(self, prefix: str, nestedName: ASTNestedName, + placeholderType: str | None) -> None: + self.prefix = prefix + self.nestedName = nestedName + self.placeholderType = placeholderType + + @property + def name(self) -> ASTNestedName: + return self.nestedName + + def get_id(self, version: int) -> str: + return self.nestedName.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.prefix: + res.append(self.prefix) + res.append(' ') + res.append(transform(self.nestedName)) + if self.placeholderType is not None: + res.append(' ') + res.append(self.placeholderType) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + if self.prefix: + signode += addnodes.desc_sig_keyword(self.prefix, self.prefix) + signode += addnodes.desc_sig_space() + self.nestedName.describe_signature(signode, mode, env, symbol=symbol) + if self.placeholderType is not None: + signode += addnodes.desc_sig_space() + if self.placeholderType == 'auto': + signode += addnodes.desc_sig_keyword('auto', 'auto') + elif self.placeholderType == 'decltype(auto)': + signode += addnodes.desc_sig_keyword('decltype', 'decltype') + signode += addnodes.desc_sig_punctuation('(', '(') + signode += addnodes.desc_sig_keyword('auto', 'auto') + signode += addnodes.desc_sig_punctuation(')', ')') + else: + raise AssertionError(self.placeholderType) + + +class ASTFunctionParameter(ASTBase): + def __init__(self, arg: ASTTypeWithInit | ASTTemplateParamConstrainedTypeWithInit, + ellipsis: bool = False) -> None: + self.arg = arg + self.ellipsis = ellipsis + + def get_id( + self, version: int, objectType: str | None = None, symbol: Symbol | None = None, + ) -> str: + # this is not part of the normal name mangling in C++ + if symbol: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=False) + # else, do the usual + if self.ellipsis: + return 'z' + else: + return self.arg.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + if self.ellipsis: + return '...' + else: + return transform(self.arg) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + if self.ellipsis: + signode += addnodes.desc_sig_punctuation('...', '...') + else: + self.arg.describe_signature(signode, mode, env, symbol=symbol) + + +class ASTNoexceptSpec(ASTBase): + def __init__(self, expr: ASTExpression | None) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + if self.expr: + return 'noexcept(' + transform(self.expr) + ')' + return 'noexcept' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('noexcept', 'noexcept') + if self.expr: + signode += addnodes.desc_sig_punctuation('(', '(') + self.expr.describe_signature(signode, 'markType', env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTParametersQualifiers(ASTBase): + def __init__(self, args: list[ASTFunctionParameter], volatile: bool, const: bool, + refQual: str | None, exceptionSpec: ASTNoexceptSpec, + trailingReturn: ASTType, + override: bool, final: bool, attrs: ASTAttributeList, + initializer: str | None) -> None: + self.args = args + self.volatile = volatile + self.const = const + self.refQual = refQual + self.exceptionSpec = exceptionSpec + self.trailingReturn = trailingReturn + self.override = override + self.final = final + self.attrs = attrs + self.initializer = initializer + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.args + + def get_modifiers_id(self, version: int) -> str: + res = [] + if self.volatile: + res.append('V') + if self.const: + if version == 1: + res.append('C') + else: + res.append('K') + if self.refQual == '&&': + res.append('O') + elif self.refQual == '&': + res.append('R') + return ''.join(res) + + def get_param_id(self, version: int) -> str: + if version == 1: + if len(self.args) == 0: + return '' + else: + return '__' + '.'.join(a.get_id(version) for a in self.args) + if len(self.args) == 0: + return 'v' + else: + return ''.join(a.get_id(version) for a in self.args) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append('(') + first = True + for a in self.args: + if not first: + res.append(', ') + first = False + res.append(str(a)) + res.append(')') + if self.volatile: + res.append(' volatile') + if self.const: + res.append(' const') + if self.refQual: + res.append(' ') + res.append(self.refQual) + if self.exceptionSpec: + res.append(' ') + res.append(transform(self.exceptionSpec)) + if self.trailingReturn: + res.append(' -> ') + res.append(transform(self.trailingReturn)) + if self.final: + res.append(' final') + if self.override: + res.append(' override') + if len(self.attrs) != 0: + res.append(' ') + res.append(transform(self.attrs)) + if self.initializer: + res.append(' = ') + res.append(self.initializer) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + multi_line_parameter_list = False + test_node: Element = signode + while test_node.parent: + if not isinstance(test_node, addnodes.desc_signature): + test_node = test_node.parent + continue + multi_line_parameter_list = test_node.get('multi_line_parameter_list', False) + break + + # only use the desc_parameterlist for the outer list, not for inner lists + if mode == 'lastIsName': + paramlist = addnodes.desc_parameterlist() + paramlist['multi_line_parameter_list'] = multi_line_parameter_list + for arg in self.args: + param = addnodes.desc_parameter('', '', noemph=True) + arg.describe_signature(param, 'param', env, symbol=symbol) + paramlist += param + signode += paramlist + else: + signode += addnodes.desc_sig_punctuation('(', '(') + first = True + for arg in self.args: + if not first: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + first = False + arg.describe_signature(signode, 'markType', env, symbol=symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + def _add_anno(signode: TextElement, text: str) -> None: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_keyword(text, text) + + if self.volatile: + _add_anno(signode, 'volatile') + if self.const: + _add_anno(signode, 'const') + if self.refQual: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(self.refQual, self.refQual) + if self.exceptionSpec: + signode += addnodes.desc_sig_space() + self.exceptionSpec.describe_signature(signode, mode, env, symbol) + if self.trailingReturn: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_operator('->', '->') + signode += addnodes.desc_sig_space() + self.trailingReturn.describe_signature(signode, mode, env, symbol) + if self.final: + _add_anno(signode, 'final') + if self.override: + _add_anno(signode, 'override') + if len(self.attrs) != 0: + signode += addnodes.desc_sig_space() + self.attrs.describe_signature(signode) + if self.initializer: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() + assert self.initializer in ('0', 'delete', 'default') + if self.initializer == '0': + signode += addnodes.desc_sig_literal_number('0', '0') + else: + signode += addnodes.desc_sig_keyword(self.initializer, self.initializer) + + +class ASTExplicitSpec(ASTBase): + def __init__(self, expr: ASTExpression | None) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + res = ['explicit'] + if self.expr is not None: + res.append('(') + res.append(transform(self.expr)) + res.append(')') + return ''.join(res) + + def describe_signature(self, signode: TextElement, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('explicit', 'explicit') + if self.expr is not None: + signode += addnodes.desc_sig_punctuation('(', '(') + self.expr.describe_signature(signode, 'markType', env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTDeclSpecsSimple(ASTBase): + def __init__(self, storage: str, threadLocal: bool, inline: bool, virtual: bool, + explicitSpec: ASTExplicitSpec | None, + consteval: bool, constexpr: bool, constinit: bool, + volatile: bool, const: bool, friend: bool, + attrs: ASTAttributeList) -> None: + self.storage = storage + self.threadLocal = threadLocal + self.inline = inline + self.virtual = virtual + self.explicitSpec = explicitSpec + self.consteval = consteval + self.constexpr = constexpr + self.constinit = constinit + self.volatile = volatile + self.const = const + self.friend = friend + self.attrs = attrs + + def mergeWith(self, other: ASTDeclSpecsSimple) -> ASTDeclSpecsSimple: + if not other: + return self + return ASTDeclSpecsSimple(self.storage or other.storage, + self.threadLocal or other.threadLocal, + self.inline or other.inline, + self.virtual or other.virtual, + self.explicitSpec or other.explicitSpec, + self.consteval or other.consteval, + self.constexpr or other.constexpr, + self.constinit or other.constinit, + self.volatile or other.volatile, + self.const or other.const, + self.friend or other.friend, + self.attrs + other.attrs) + + def _stringify(self, transform: StringifyTransform) -> str: + res: list[str] = [] + if len(self.attrs) != 0: + res.append(transform(self.attrs)) + if self.storage: + res.append(self.storage) + if self.threadLocal: + res.append('thread_local') + if self.inline: + res.append('inline') + if self.friend: + res.append('friend') + if self.virtual: + res.append('virtual') + if self.explicitSpec: + res.append(transform(self.explicitSpec)) + if self.consteval: + res.append('consteval') + if self.constexpr: + res.append('constexpr') + if self.constinit: + res.append('constinit') + if self.volatile: + res.append('volatile') + if self.const: + res.append('const') + return ' '.join(res) + + def describe_signature(self, signode: TextElement, + env: BuildEnvironment, symbol: Symbol) -> None: + self.attrs.describe_signature(signode) + addSpace = len(self.attrs) != 0 + + def _add(signode: TextElement, text: str) -> bool: + if addSpace: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_keyword(text, text) + return True + + if self.storage: + addSpace = _add(signode, self.storage) + if self.threadLocal: + addSpace = _add(signode, 'thread_local') + if self.inline: + addSpace = _add(signode, 'inline') + if self.friend: + addSpace = _add(signode, 'friend') + if self.virtual: + addSpace = _add(signode, 'virtual') + if self.explicitSpec: + if addSpace: + signode += addnodes.desc_sig_space() + self.explicitSpec.describe_signature(signode, env, symbol) + addSpace = True + if self.consteval: + addSpace = _add(signode, 'consteval') + if self.constexpr: + addSpace = _add(signode, 'constexpr') + if self.constinit: + addSpace = _add(signode, 'constinit') + if self.volatile: + addSpace = _add(signode, 'volatile') + if self.const: + addSpace = _add(signode, 'const') + + +class ASTDeclSpecs(ASTBase): + def __init__(self, outer: str, + leftSpecs: ASTDeclSpecsSimple, rightSpecs: ASTDeclSpecsSimple, + trailing: ASTTrailingTypeSpec) -> None: + # leftSpecs and rightSpecs are used for output + # allSpecs are used for id generation + self.outer = outer + self.leftSpecs = leftSpecs + self.rightSpecs = rightSpecs + self.allSpecs = self.leftSpecs.mergeWith(self.rightSpecs) + self.trailingTypeSpec = trailing + + def get_id(self, version: int) -> str: + if version == 1: + res = [] + res.append(self.trailingTypeSpec.get_id(version)) + if self.allSpecs.volatile: + res.append('V') + if self.allSpecs.const: + res.append('C') + return ''.join(res) + res = [] + if self.allSpecs.volatile: + res.append('V') + if self.allSpecs.const: + res.append('K') + if self.trailingTypeSpec is not None: + res.append(self.trailingTypeSpec.get_id(version)) + return ''.join(res) + + def _stringify(self, transform: StringifyTransform) -> str: + res: list[str] = [] + l = transform(self.leftSpecs) + if len(l) > 0: + res.append(l) + if self.trailingTypeSpec: + if len(res) > 0: + res.append(" ") + res.append(transform(self.trailingTypeSpec)) + r = str(self.rightSpecs) + if len(r) > 0: + if len(res) > 0: + res.append(" ") + res.append(r) + return "".join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + numChildren = len(signode) + self.leftSpecs.describe_signature(signode, env, symbol) + addSpace = len(signode) != numChildren + + if self.trailingTypeSpec: + if addSpace: + signode += addnodes.desc_sig_space() + numChildren = len(signode) + self.trailingTypeSpec.describe_signature(signode, mode, env, + symbol=symbol) + addSpace = len(signode) != numChildren + + if len(str(self.rightSpecs)) > 0: + if addSpace: + signode += addnodes.desc_sig_space() + self.rightSpecs.describe_signature(signode, env, symbol) + + +# Declarator +################################################################################ + +class ASTArray(ASTBase): + def __init__(self, size: ASTExpression) -> None: + self.size = size + + def _stringify(self, transform: StringifyTransform) -> str: + if self.size: + return '[' + transform(self.size) + ']' + else: + return '[]' + + def get_id(self, version: int) -> str: + if version == 1: + return 'A' + if version == 2: + if self.size: + return 'A' + str(self.size) + '_' + else: + return 'A_' + if self.size: + return 'A' + self.size.get_id(version) + '_' + else: + return 'A_' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('[', '[') + if self.size: + self.size.describe_signature(signode, 'markType', env, symbol) + signode += addnodes.desc_sig_punctuation(']', ']') + + +class ASTDeclarator(ASTBase): + @property + def name(self) -> ASTNestedName: + raise NotImplementedError(repr(self)) + + @name.setter + def name(self, name: ASTNestedName) -> None: + raise NotImplementedError(repr(self)) + + @property + def isPack(self) -> bool: + raise NotImplementedError(repr(self)) + + @property + def function_params(self) -> list[ASTFunctionParameter]: + raise NotImplementedError(repr(self)) + + @property + def trailingReturn(self) -> ASTType: + raise NotImplementedError(repr(self)) + + def require_space_after_declSpecs(self) -> bool: + raise NotImplementedError(repr(self)) + + def get_modifiers_id(self, version: int) -> str: + raise NotImplementedError(repr(self)) + + def get_param_id(self, version: int) -> str: + raise NotImplementedError(repr(self)) + + def get_ptr_suffix_id(self, version: int) -> str: + raise NotImplementedError(repr(self)) + + def get_type_id(self, version: int, returnTypeId: str) -> str: + raise NotImplementedError(repr(self)) + + def is_function_type(self) -> bool: + raise NotImplementedError(repr(self)) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + raise NotImplementedError(repr(self)) + + +class ASTDeclaratorNameParamQual(ASTDeclarator): + def __init__(self, declId: ASTNestedName, + arrayOps: list[ASTArray], + paramQual: ASTParametersQualifiers) -> None: + self.declId = declId + self.arrayOps = arrayOps + self.paramQual = paramQual + + @property + def name(self) -> ASTNestedName: + return self.declId + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.declId = name + + @property + def isPack(self) -> bool: + return False + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.paramQual.function_params + + @property + def trailingReturn(self) -> ASTType: + return self.paramQual.trailingReturn + + # only the modifiers for a function, e.g., + def get_modifiers_id(self, version: int) -> str: + # cv-qualifiers + if self.paramQual: + return self.paramQual.get_modifiers_id(version) + raise Exception("This should only be called on a function: %s" % self) + + def get_param_id(self, version: int) -> str: # only the parameters (if any) + if self.paramQual: + return self.paramQual.get_param_id(version) + else: + return '' + + def get_ptr_suffix_id(self, version: int) -> str: # only the array specifiers + return ''.join(a.get_id(version) for a in self.arrayOps) + + def get_type_id(self, version: int, returnTypeId: str) -> str: + assert version >= 2 + res = [] + # TODO: can we actually have both array ops and paramQual? + res.append(self.get_ptr_suffix_id(version)) + if self.paramQual: + res.append(self.get_modifiers_id(version)) + res.append('F') + res.append(returnTypeId) + res.append(self.get_param_id(version)) + res.append('E') + else: + res.append(returnTypeId) + return ''.join(res) + + # ------------------------------------------------------------------------ + + def require_space_after_declSpecs(self) -> bool: + return self.declId is not None + + def is_function_type(self) -> bool: + return self.paramQual is not None + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.declId: + res.append(transform(self.declId)) + res.extend(transform(op) for op in self.arrayOps) + if self.paramQual: + res.append(transform(self.paramQual)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + if self.declId: + self.declId.describe_signature(signode, mode, env, symbol) + for op in self.arrayOps: + op.describe_signature(signode, mode, env, symbol) + if self.paramQual: + self.paramQual.describe_signature(signode, mode, env, symbol) + + +class ASTDeclaratorNameBitField(ASTDeclarator): + def __init__(self, declId: ASTNestedName, size: ASTExpression) -> None: + self.declId = declId + self.size = size + + @property + def name(self) -> ASTNestedName: + return self.declId + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.declId = name + + def get_param_id(self, version: int) -> str: # only the parameters (if any) + return '' + + def get_ptr_suffix_id(self, version: int) -> str: # only the array specifiers + return '' + + # ------------------------------------------------------------------------ + + def require_space_after_declSpecs(self) -> bool: + return self.declId is not None + + def is_function_type(self) -> bool: + return False + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.declId: + res.append(transform(self.declId)) + res.append(" : ") + res.append(transform(self.size)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + if self.declId: + self.declId.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(':', ':') + signode += addnodes.desc_sig_space() + self.size.describe_signature(signode, mode, env, symbol) + + +class ASTDeclaratorPtr(ASTDeclarator): + def __init__(self, next: ASTDeclarator, volatile: bool, const: bool, + attrs: ASTAttributeList) -> None: + assert next + self.next = next + self.volatile = volatile + self.const = const + self.attrs = attrs + + @property + def name(self) -> ASTNestedName: + return self.next.name + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.next.name = name + + @property + def isPack(self) -> bool: + return self.next.isPack + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.next.function_params + + @property + def trailingReturn(self) -> ASTType: + return self.next.trailingReturn + + def require_space_after_declSpecs(self) -> bool: + return self.next.require_space_after_declSpecs() + + def _stringify(self, transform: StringifyTransform) -> str: + res = ['*'] + res.append(transform(self.attrs)) + if len(self.attrs) != 0 and (self.volatile or self.const): + res.append(' ') + if self.volatile: + res.append('volatile') + if self.const: + if self.volatile: + res.append(' ') + res.append('const') + if self.const or self.volatile or len(self.attrs) > 0: + if self.next.require_space_after_declSpecs(): + res.append(' ') + res.append(transform(self.next)) + return ''.join(res) + + def get_modifiers_id(self, version: int) -> str: + return self.next.get_modifiers_id(version) + + def get_param_id(self, version: int) -> str: + return self.next.get_param_id(version) + + def get_ptr_suffix_id(self, version: int) -> str: + if version == 1: + res = ['P'] + if self.volatile: + res.append('V') + if self.const: + res.append('C') + res.append(self.next.get_ptr_suffix_id(version)) + return ''.join(res) + + res = [self.next.get_ptr_suffix_id(version)] + res.append('P') + if self.volatile: + res.append('V') + if self.const: + res.append('C') + return ''.join(res) + + def get_type_id(self, version: int, returnTypeId: str) -> str: + # ReturnType *next, so we are part of the return type of 'next + res = ['P'] + if self.volatile: + res.append('V') + if self.const: + res.append('C') + res.append(returnTypeId) + return self.next.get_type_id(version, returnTypeId=''.join(res)) + + def is_function_type(self) -> bool: + return self.next.is_function_type() + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('*', '*') + self.attrs.describe_signature(signode) + if len(self.attrs) != 0 and (self.volatile or self.const): + signode += addnodes.desc_sig_space() + + def _add_anno(signode: TextElement, text: str) -> None: + signode += addnodes.desc_sig_keyword(text, text) + if self.volatile: + _add_anno(signode, 'volatile') + if self.const: + if self.volatile: + signode += addnodes.desc_sig_space() + _add_anno(signode, 'const') + if self.const or self.volatile or len(self.attrs) > 0: + if self.next.require_space_after_declSpecs(): + signode += addnodes.desc_sig_space() + self.next.describe_signature(signode, mode, env, symbol) + + +class ASTDeclaratorRef(ASTDeclarator): + def __init__(self, next: ASTDeclarator, attrs: ASTAttributeList) -> None: + assert next + self.next = next + self.attrs = attrs + + @property + def name(self) -> ASTNestedName: + return self.next.name + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.next.name = name + + @property + def isPack(self) -> bool: + return self.next.isPack + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.next.function_params + + @property + def trailingReturn(self) -> ASTType: + return self.next.trailingReturn + + def require_space_after_declSpecs(self) -> bool: + return self.next.require_space_after_declSpecs() + + def _stringify(self, transform: StringifyTransform) -> str: + res = ['&'] + res.append(transform(self.attrs)) + if len(self.attrs) != 0 and self.next.require_space_after_declSpecs(): + res.append(' ') + res.append(transform(self.next)) + return ''.join(res) + + def get_modifiers_id(self, version: int) -> str: + return self.next.get_modifiers_id(version) + + def get_param_id(self, version: int) -> str: # only the parameters (if any) + return self.next.get_param_id(version) + + def get_ptr_suffix_id(self, version: int) -> str: + if version == 1: + return 'R' + self.next.get_ptr_suffix_id(version) + else: + return self.next.get_ptr_suffix_id(version) + 'R' + + def get_type_id(self, version: int, returnTypeId: str) -> str: + assert version >= 2 + # ReturnType &next, so we are part of the return type of 'next + return self.next.get_type_id(version, returnTypeId='R' + returnTypeId) + + def is_function_type(self) -> bool: + return self.next.is_function_type() + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('&', '&') + self.attrs.describe_signature(signode) + if len(self.attrs) > 0 and self.next.require_space_after_declSpecs(): + signode += addnodes.desc_sig_space() + self.next.describe_signature(signode, mode, env, symbol) + + +class ASTDeclaratorParamPack(ASTDeclarator): + def __init__(self, next: ASTDeclarator) -> None: + assert next + self.next = next + + @property + def name(self) -> ASTNestedName: + return self.next.name + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.next.name = name + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.next.function_params + + @property + def trailingReturn(self) -> ASTType: + return self.next.trailingReturn + + @property + def isPack(self) -> bool: + return True + + def require_space_after_declSpecs(self) -> bool: + return False + + def _stringify(self, transform: StringifyTransform) -> str: + res = transform(self.next) + if self.next.name: + res = ' ' + res + return '...' + res + + def get_modifiers_id(self, version: int) -> str: + return self.next.get_modifiers_id(version) + + def get_param_id(self, version: int) -> str: # only the parameters (if any) + return self.next.get_param_id(version) + + def get_ptr_suffix_id(self, version: int) -> str: + if version == 1: + return 'Dp' + self.next.get_ptr_suffix_id(version) + else: + return self.next.get_ptr_suffix_id(version) + 'Dp' + + def get_type_id(self, version: int, returnTypeId: str) -> str: + assert version >= 2 + # ReturnType... next, so we are part of the return type of 'next + return self.next.get_type_id(version, returnTypeId='Dp' + returnTypeId) + + def is_function_type(self) -> bool: + return self.next.is_function_type() + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('...', '...') + if self.next.name: + signode += addnodes.desc_sig_space() + self.next.describe_signature(signode, mode, env, symbol) + + +class ASTDeclaratorMemPtr(ASTDeclarator): + def __init__(self, className: ASTNestedName, + const: bool, volatile: bool, next: ASTDeclarator) -> None: + assert className + assert next + self.className = className + self.const = const + self.volatile = volatile + self.next = next + + @property + def name(self) -> ASTNestedName: + return self.next.name + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.next.name = name + + @property + def isPack(self) -> bool: + return self.next.isPack + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.next.function_params + + @property + def trailingReturn(self) -> ASTType: + return self.next.trailingReturn + + def require_space_after_declSpecs(self) -> bool: + return True + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.className)) + res.append('::*') + if self.volatile: + res.append('volatile') + if self.const: + if self.volatile: + res.append(' ') + res.append('const') + if self.next.require_space_after_declSpecs(): + res.append(' ') + res.append(transform(self.next)) + return ''.join(res) + + def get_modifiers_id(self, version: int) -> str: + if version == 1: + raise NoOldIdError + return self.next.get_modifiers_id(version) + + def get_param_id(self, version: int) -> str: # only the parameters (if any) + if version == 1: + raise NoOldIdError + return self.next.get_param_id(version) + + def get_ptr_suffix_id(self, version: int) -> str: + if version == 1: + raise NoOldIdError + raise NotImplementedError + return self.next.get_ptr_suffix_id(version) + 'Dp' + + def get_type_id(self, version: int, returnTypeId: str) -> str: + assert version >= 2 + # ReturnType name::* next, so we are part of the return type of next + nextReturnTypeId = '' + if self.volatile: + nextReturnTypeId += 'V' + if self.const: + nextReturnTypeId += 'K' + nextReturnTypeId += 'M' + nextReturnTypeId += self.className.get_id(version) + nextReturnTypeId += returnTypeId + return self.next.get_type_id(version, nextReturnTypeId) + + def is_function_type(self) -> bool: + return self.next.is_function_type() + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.className.describe_signature(signode, 'markType', env, symbol) + signode += addnodes.desc_sig_punctuation('::', '::') + signode += addnodes.desc_sig_punctuation('*', '*') + + def _add_anno(signode: TextElement, text: str) -> None: + signode += addnodes.desc_sig_keyword(text, text) + if self.volatile: + _add_anno(signode, 'volatile') + if self.const: + if self.volatile: + signode += addnodes.desc_sig_space() + _add_anno(signode, 'const') + if self.next.require_space_after_declSpecs(): + signode += addnodes.desc_sig_space() + self.next.describe_signature(signode, mode, env, symbol) + + +class ASTDeclaratorParen(ASTDeclarator): + def __init__(self, inner: ASTDeclarator, next: ASTDeclarator) -> None: + assert inner + assert next + self.inner = inner + self.next = next + # TODO: we assume the name, params, and qualifiers are in inner + + @property + def name(self) -> ASTNestedName: + return self.inner.name + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.inner.name = name + + @property + def isPack(self) -> bool: + return self.inner.isPack or self.next.isPack + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.inner.function_params + + @property + def trailingReturn(self) -> ASTType: + return self.inner.trailingReturn + + def require_space_after_declSpecs(self) -> bool: + return True + + def _stringify(self, transform: StringifyTransform) -> str: + res = ['('] + res.append(transform(self.inner)) + res.append(')') + res.append(transform(self.next)) + return ''.join(res) + + def get_modifiers_id(self, version: int) -> str: + return self.inner.get_modifiers_id(version) + + def get_param_id(self, version: int) -> str: # only the parameters (if any) + return self.inner.get_param_id(version) + + def get_ptr_suffix_id(self, version: int) -> str: + if version == 1: + raise NoOldIdError # TODO: was this implemented before? + return self.next.get_ptr_suffix_id(version) + \ + self.inner.get_ptr_suffix_id(version) + return self.inner.get_ptr_suffix_id(version) + \ + self.next.get_ptr_suffix_id(version) + + def get_type_id(self, version: int, returnTypeId: str) -> str: + assert version >= 2 + # ReturnType (inner)next, so 'inner' returns everything outside + nextId = self.next.get_type_id(version, returnTypeId) + return self.inner.get_type_id(version, returnTypeId=nextId) + + def is_function_type(self) -> bool: + return self.inner.is_function_type() + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('(', '(') + self.inner.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + self.next.describe_signature(signode, "noneIsName", env, symbol) + + +# Type and initializer stuff +############################################################################################## + +class ASTPackExpansionExpr(ASTExpression): + def __init__(self, expr: ASTExpression | ASTBracedInitList) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.expr) + '...' + + def get_id(self, version: int) -> str: + id = self.expr.get_id(version) + return 'sp' + id + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.expr.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation('...', '...') + + +class ASTParenExprList(ASTBaseParenExprList): + def __init__(self, exprs: list[ASTExpression | ASTBracedInitList]) -> None: + self.exprs = exprs + + def get_id(self, version: int) -> str: + return "pi%sE" % ''.join(e.get_id(version) for e in self.exprs) + + def _stringify(self, transform: StringifyTransform) -> str: + exprs = [transform(e) for e in self.exprs] + return '(%s)' % ', '.join(exprs) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + signode += addnodes.desc_sig_punctuation('(', '(') + first = True + for e in self.exprs: + if not first: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + else: + first = False + e.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation(')', ')') + + +class ASTInitializer(ASTBase): + def __init__(self, value: ASTExpression | ASTBracedInitList, + hasAssign: bool = True) -> None: + self.value = value + self.hasAssign = hasAssign + + def _stringify(self, transform: StringifyTransform) -> str: + val = transform(self.value) + if self.hasAssign: + return ' = ' + val + else: + return val + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + if self.hasAssign: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() + self.value.describe_signature(signode, 'markType', env, symbol) + + +class ASTType(ASTBase): + def __init__(self, declSpecs: ASTDeclSpecs, decl: ASTDeclarator) -> None: + assert declSpecs + assert decl + self.declSpecs = declSpecs + self.decl = decl + + @property + def name(self) -> ASTNestedName: + return self.decl.name + + @name.setter + def name(self, name: ASTNestedName) -> None: + self.decl.name = name + + @property + def isPack(self) -> bool: + return self.decl.isPack + + @property + def function_params(self) -> list[ASTFunctionParameter]: + return self.decl.function_params + + @property + def trailingReturn(self) -> ASTType: + return self.decl.trailingReturn + + def get_id(self, version: int, objectType: str | None = None, + symbol: Symbol | None = None) -> str: + if version == 1: + res = [] + if objectType: # needs the name + if objectType == 'function': # also modifiers + res.append(symbol.get_full_nested_name().get_id(version)) + res.append(self.decl.get_param_id(version)) + res.append(self.decl.get_modifiers_id(version)) + if (self.declSpecs.leftSpecs.constexpr or + (self.declSpecs.rightSpecs and + self.declSpecs.rightSpecs.constexpr)): + res.append('CE') + elif objectType == 'type': # just the name + res.append(symbol.get_full_nested_name().get_id(version)) + else: + raise AssertionError(objectType) + else: # only type encoding + if self.decl.is_function_type(): + raise NoOldIdError + res.append(self.declSpecs.get_id(version)) + res.append(self.decl.get_ptr_suffix_id(version)) + res.append(self.decl.get_param_id(version)) + return ''.join(res) + # other versions + res = [] + if objectType: # needs the name + if objectType == 'function': # also modifiers + modifiers = self.decl.get_modifiers_id(version) + res.append(symbol.get_full_nested_name().get_id(version, modifiers)) + if version >= 4: + # with templates we need to mangle the return type in as well + templ = symbol.declaration.templatePrefix + if templ is not None: + typeId = self.decl.get_ptr_suffix_id(version) + if self.trailingReturn: + returnTypeId = self.trailingReturn.get_id(version) + else: + returnTypeId = self.declSpecs.get_id(version) + res.append(typeId) + res.append(returnTypeId) + res.append(self.decl.get_param_id(version)) + elif objectType == 'type': # just the name + res.append(symbol.get_full_nested_name().get_id(version)) + else: + raise AssertionError(objectType) + else: # only type encoding + # the 'returnType' of a non-function type is simply just the last + # type, i.e., for 'int*' it is 'int' + returnTypeId = self.declSpecs.get_id(version) + typeId = self.decl.get_type_id(version, returnTypeId) + res.append(typeId) + return ''.join(res) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + declSpecs = transform(self.declSpecs) + res.append(declSpecs) + if self.decl.require_space_after_declSpecs() and len(declSpecs) > 0: + res.append(' ') + res.append(transform(self.decl)) + return ''.join(res) + + def get_type_declaration_prefix(self) -> str: + if self.declSpecs.trailingTypeSpec: + return 'typedef' + else: + return 'type' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.declSpecs.describe_signature(signode, 'markType', env, symbol) + if (self.decl.require_space_after_declSpecs() and + len(str(self.declSpecs)) > 0): + signode += addnodes.desc_sig_space() + # for parameters that don't really declare new names we get 'markType', + # this should not be propagated, but be 'noneIsName'. + if mode == 'markType': + mode = 'noneIsName' + self.decl.describe_signature(signode, mode, env, symbol) + + +class ASTTemplateParamConstrainedTypeWithInit(ASTBase): + def __init__(self, type: ASTType, init: ASTType) -> None: + assert type + self.type = type + self.init = init + + @property + def name(self) -> ASTNestedName: + return self.type.name + + @property + def isPack(self) -> bool: + return self.type.isPack + + def get_id( + self, version: int, objectType: str | None = None, symbol: Symbol | None = None, + ) -> str: + # this is not part of the normal name mangling in C++ + assert version >= 2 + if symbol: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=False) + else: + return self.type.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = transform(self.type) + if self.init: + res += " = " + res += transform(self.init) + return res + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.type.describe_signature(signode, mode, env, symbol) + if self.init: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() + self.init.describe_signature(signode, mode, env, symbol) + + +class ASTTypeWithInit(ASTBase): + def __init__(self, type: ASTType, init: ASTInitializer) -> None: + self.type = type + self.init = init + + @property + def name(self) -> ASTNestedName: + return self.type.name + + @property + def isPack(self) -> bool: + return self.type.isPack + + def get_id(self, version: int, objectType: str | None = None, + symbol: Symbol | None = None) -> str: + if objectType != 'member': + return self.type.get_id(version, objectType) + if version == 1: + return (symbol.get_full_nested_name().get_id(version) + '__' + + self.type.get_id(version)) + return symbol.get_full_nested_name().get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.type)) + if self.init: + res.append(transform(self.init)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.type.describe_signature(signode, mode, env, symbol) + if self.init: + self.init.describe_signature(signode, mode, env, symbol) + + +class ASTTypeUsing(ASTBase): + def __init__(self, name: ASTNestedName, type: ASTType | None) -> None: + self.name = name + self.type = type + + def get_id(self, version: int, objectType: str | None = None, + symbol: Symbol | None = None) -> str: + if version == 1: + raise NoOldIdError + return symbol.get_full_nested_name().get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.name)) + if self.type: + res.append(' = ') + res.append(transform(self.type)) + return ''.join(res) + + def get_type_declaration_prefix(self) -> str: + return 'using' + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.name.describe_signature(signode, mode, env, symbol=symbol) + if self.type: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() + self.type.describe_signature(signode, 'markType', env, symbol=symbol) + + +# Other declarations +############################################################################################## + +class ASTConcept(ASTBase): + def __init__(self, nestedName: ASTNestedName, initializer: ASTInitializer) -> None: + self.nestedName = nestedName + self.initializer = initializer + + @property + def name(self) -> ASTNestedName: + return self.nestedName + + def get_id(self, version: int, objectType: str | None = None, + symbol: Symbol | None = None) -> str: + if version == 1: + raise NoOldIdError + return symbol.get_full_nested_name().get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = transform(self.nestedName) + if self.initializer: + res += transform(self.initializer) + return res + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.nestedName.describe_signature(signode, mode, env, symbol) + if self.initializer: + self.initializer.describe_signature(signode, mode, env, symbol) + + +class ASTBaseClass(ASTBase): + def __init__(self, name: ASTNestedName, visibility: str, + virtual: bool, pack: bool) -> None: + self.name = name + self.visibility = visibility + self.virtual = virtual + self.pack = pack + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.visibility is not None: + res.append(self.visibility) + res.append(' ') + if self.virtual: + res.append('virtual ') + res.append(transform(self.name)) + if self.pack: + res.append('...') + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + if self.visibility is not None: + signode += addnodes.desc_sig_keyword(self.visibility, + self.visibility) + signode += addnodes.desc_sig_space() + if self.virtual: + signode += addnodes.desc_sig_keyword('virtual', 'virtual') + signode += addnodes.desc_sig_space() + self.name.describe_signature(signode, 'markType', env, symbol=symbol) + if self.pack: + signode += addnodes.desc_sig_punctuation('...', '...') + + +class ASTClass(ASTBase): + def __init__(self, name: ASTNestedName, final: bool, bases: list[ASTBaseClass], + attrs: ASTAttributeList) -> None: + self.name = name + self.final = final + self.bases = bases + self.attrs = attrs + + def get_id(self, version: int, objectType: str, symbol: Symbol) -> str: + return symbol.get_full_nested_name().get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.attrs)) + if len(self.attrs) != 0: + res.append(' ') + res.append(transform(self.name)) + if self.final: + res.append(' final') + if len(self.bases) > 0: + res.append(' : ') + first = True + for b in self.bases: + if not first: + res.append(', ') + first = False + res.append(transform(b)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.attrs.describe_signature(signode) + if len(self.attrs) != 0: + signode += addnodes.desc_sig_space() + self.name.describe_signature(signode, mode, env, symbol=symbol) + if self.final: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_keyword('final', 'final') + if len(self.bases) > 0: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(':', ':') + signode += addnodes.desc_sig_space() + for b in self.bases: + b.describe_signature(signode, mode, env, symbol=symbol) + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + signode.pop() + signode.pop() + + +class ASTUnion(ASTBase): + def __init__(self, name: ASTNestedName, attrs: ASTAttributeList) -> None: + self.name = name + self.attrs = attrs + + def get_id(self, version: int, objectType: str, symbol: Symbol) -> str: + if version == 1: + raise NoOldIdError + return symbol.get_full_nested_name().get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.attrs)) + if len(self.attrs) != 0: + res.append(' ') + res.append(transform(self.name)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.attrs.describe_signature(signode) + if len(self.attrs) != 0: + signode += addnodes.desc_sig_space() + self.name.describe_signature(signode, mode, env, symbol=symbol) + + +class ASTEnum(ASTBase): + def __init__(self, name: ASTNestedName, scoped: str, underlyingType: ASTType, + attrs: ASTAttributeList) -> None: + self.name = name + self.scoped = scoped + self.underlyingType = underlyingType + self.attrs = attrs + + def get_id(self, version: int, objectType: str, symbol: Symbol) -> str: + if version == 1: + raise NoOldIdError + return symbol.get_full_nested_name().get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.scoped: + res.append(self.scoped) + res.append(' ') + res.append(transform(self.attrs)) + if len(self.attrs) != 0: + res.append(' ') + res.append(transform(self.name)) + if self.underlyingType: + res.append(' : ') + res.append(transform(self.underlyingType)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + # self.scoped has been done by the CPPEnumObject + self.attrs.describe_signature(signode) + if len(self.attrs) != 0: + signode += addnodes.desc_sig_space() + self.name.describe_signature(signode, mode, env, symbol=symbol) + if self.underlyingType: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation(':', ':') + signode += addnodes.desc_sig_space() + self.underlyingType.describe_signature(signode, 'noneIsName', + env, symbol=symbol) + + +class ASTEnumerator(ASTBase): + def __init__(self, name: ASTNestedName, init: ASTInitializer | None, + attrs: ASTAttributeList) -> None: + self.name = name + self.init = init + self.attrs = attrs + + def get_id(self, version: int, objectType: str, symbol: Symbol) -> str: + if version == 1: + raise NoOldIdError + return symbol.get_full_nested_name().get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.name)) + if len(self.attrs) != 0: + res.append(' ') + res.append(transform(self.attrs)) + if self.init: + res.append(transform(self.init)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + verify_description_mode(mode) + self.name.describe_signature(signode, mode, env, symbol) + if len(self.attrs) != 0: + signode += addnodes.desc_sig_space() + self.attrs.describe_signature(signode) + if self.init: + self.init.describe_signature(signode, 'markType', env, symbol) + + +################################################################################ +# Templates +################################################################################ + +# Parameters +################################################################################ + +class ASTTemplateParam(ASTBase): + def get_identifier(self) -> ASTIdentifier: + raise NotImplementedError(repr(self)) + + def get_id(self, version: int) -> str: + raise NotImplementedError(repr(self)) + + def describe_signature(self, parentNode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + raise NotImplementedError(repr(self)) + + @property + def isPack(self) -> bool: + raise NotImplementedError(repr(self)) + + @property + def name(self) -> ASTNestedName: + raise NotImplementedError(repr(self)) + + +class ASTTemplateKeyParamPackIdDefault(ASTTemplateParam): + def __init__(self, key: str, identifier: ASTIdentifier, + parameterPack: bool, default: ASTType) -> None: + assert key + if parameterPack: + assert default is None + self.key = key + self.identifier = identifier + self.parameterPack = parameterPack + self.default = default + + def get_identifier(self) -> ASTIdentifier: + return self.identifier + + def get_id(self, version: int) -> str: + assert version >= 2 + # this is not part of the normal name mangling in C++ + res = [] + if self.parameterPack: + res.append('Dp') + else: + res.append('0') # we need to put something + return ''.join(res) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [self.key] + if self.parameterPack: + if self.identifier: + res.append(' ') + res.append('...') + if self.identifier: + if not self.parameterPack: + res.append(' ') + res.append(transform(self.identifier)) + if self.default: + res.append(' = ') + res.append(transform(self.default)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword(self.key, self.key) + if self.parameterPack: + if self.identifier: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('...', '...') + if self.identifier: + if not self.parameterPack: + signode += addnodes.desc_sig_space() + self.identifier.describe_signature(signode, mode, env, '', '', symbol) + if self.default: + signode += addnodes.desc_sig_space() + signode += addnodes.desc_sig_punctuation('=', '=') + signode += addnodes.desc_sig_space() + self.default.describe_signature(signode, 'markType', env, symbol) + + +class ASTTemplateParamType(ASTTemplateParam): + def __init__(self, data: ASTTemplateKeyParamPackIdDefault) -> None: + assert data + self.data = data + + @property + def name(self) -> ASTNestedName: + id = self.get_identifier() + return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False) + + @property + def isPack(self) -> bool: + return self.data.parameterPack + + def get_identifier(self) -> ASTIdentifier: + return self.data.get_identifier() + + def get_id( + self, version: int, objectType: str | None = None, symbol: Symbol | None = None, + ) -> str: + # this is not part of the normal name mangling in C++ + assert version >= 2 + if symbol: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=False) + else: + return self.data.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.data) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.data.describe_signature(signode, mode, env, symbol) + + +class ASTTemplateParamTemplateType(ASTTemplateParam): + def __init__(self, nestedParams: ASTTemplateParams, + data: ASTTemplateKeyParamPackIdDefault) -> None: + assert nestedParams + assert data + self.nestedParams = nestedParams + self.data = data + + @property + def name(self) -> ASTNestedName: + id = self.get_identifier() + return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False) + + @property + def isPack(self) -> bool: + return self.data.parameterPack + + def get_identifier(self) -> ASTIdentifier: + return self.data.get_identifier() + + def get_id( + self, version: int, objectType: str | None = None, symbol: Symbol | None = None, + ) -> str: + assert version >= 2 + # this is not part of the normal name mangling in C++ + if symbol: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=None) + else: + return self.nestedParams.get_id(version) + self.data.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.nestedParams) + transform(self.data) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + self.nestedParams.describe_signature(signode, 'noneIsName', env, symbol) + signode += addnodes.desc_sig_space() + self.data.describe_signature(signode, mode, env, symbol) + + +class ASTTemplateParamNonType(ASTTemplateParam): + def __init__(self, + param: ASTTypeWithInit | ASTTemplateParamConstrainedTypeWithInit, + parameterPack: bool = False) -> None: + assert param + self.param = param + self.parameterPack = parameterPack + + @property + def name(self) -> ASTNestedName: + id = self.get_identifier() + return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False) + + @property + def isPack(self) -> bool: + return self.param.isPack or self.parameterPack + + def get_identifier(self) -> ASTIdentifier: + name = self.param.name + if name: + assert len(name.names) == 1 + assert name.names[0].identOrOp + assert not name.names[0].templateArgs + res = name.names[0].identOrOp + assert isinstance(res, ASTIdentifier) + return res + else: + return None + + def get_id( + self, version: int, objectType: str | None = None, symbol: Symbol | None = None, + ) -> str: + assert version >= 2 + # this is not part of the normal name mangling in C++ + if symbol: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=None) + else: + res = '_' + if self.parameterPack: + res += 'Dp' + return res + self.param.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + 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): + def __init__(self, params: list[ASTTemplateParam], + requiresClause: ASTRequiresClause | None) -> None: + assert params is not None + self.params = params + self.requiresClause = requiresClause + + def get_id(self, version: int, excludeRequires: bool = False) -> str: + assert version >= 2 + res = [] + res.append("I") + res.extend(param.get_id(version) for param in self.params) + res.append("E") + if not excludeRequires and self.requiresClause: + res.extend(['IQ', self.requiresClause.expr.get_id(version), 'E']) + return ''.join(res) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + 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, + env: BuildEnvironment, symbol: Symbol) -> None: + signode += addnodes.desc_sig_keyword('template', 'template') + signode += addnodes.desc_sig_punctuation('<', '<') + first = True + for param in self.params: + if not first: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + 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, + symbol: Symbol, lineSpec: bool) -> None: + def makeLine(parentNode: desc_signature) -> addnodes.desc_signature_line: + signode = addnodes.desc_signature_line() + parentNode += signode + signode.sphinx_line_type = 'templateParams' + return signode + lineNode = makeLine(parentNode) + lineNode += addnodes.desc_sig_keyword('template', 'template') + lineNode += addnodes.desc_sig_punctuation('<', '<') + first = True + for param in self.params: + if not first: + lineNode += addnodes.desc_sig_punctuation(',', ',') + lineNode += addnodes.desc_sig_space() + first = False + if lineSpec: + lineNode = makeLine(parentNode) + param.describe_signature(lineNode, mode, env, symbol) + 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 +################################################################################ + +class ASTTemplateIntroductionParameter(ASTBase): + def __init__(self, identifier: ASTIdentifier, parameterPack: bool) -> None: + self.identifier = identifier + self.parameterPack = parameterPack + + @property + def name(self) -> ASTNestedName: + id = self.get_identifier() + return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False) + + @property + def isPack(self) -> bool: + return self.parameterPack + + def get_identifier(self) -> ASTIdentifier: + return self.identifier + + def get_id( + self, version: int, objectType: str | None = None, symbol: Symbol | None = None, + ) -> str: + assert version >= 2 + # this is not part of the normal name mangling in C++ + if symbol: + # the anchor will be our parent + return symbol.parent.declaration.get_id(version, prefixed=None) + else: + if self.parameterPack: + return 'Dp' + else: + return '0' # we need to put something + + def get_id_as_arg(self, version: int) -> str: + assert version >= 2 + # used for the implicit requires clause + res = self.identifier.get_id(version) + if self.parameterPack: + return 'sp' + res + else: + return res + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.parameterPack: + res.append('...') + res.append(transform(self.identifier)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: BuildEnvironment, symbol: Symbol) -> None: + if self.parameterPack: + signode += addnodes.desc_sig_punctuation('...', '...') + self.identifier.describe_signature(signode, mode, env, '', '', symbol) + + +class ASTTemplateIntroduction(ASTBase): + def __init__(self, concept: ASTNestedName, + params: list[ASTTemplateIntroductionParameter]) -> None: + assert len(params) > 0 + self.concept = concept + self.params = params + + def get_id(self, version: int) -> str: + assert version >= 2 + return ''.join([ + # first do the same as a normal template parameter list + "I", + *(param.get_id(version) for param in self.params), + "E", + # let's use X expr E, which is otherwise for constant template args + "X", + self.concept.get_id(version), + "I", + *(param.get_id_as_arg(version) for param in self.params), + "E", + "E", + ]) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + res.append(transform(self.concept)) + res.append('{') + res.append(', '.join(transform(param) for param in self.params)) + res.append('} ') + return ''.join(res) + + def describe_signature_as_introducer( + self, parentNode: desc_signature, mode: str, + env: BuildEnvironment, symbol: Symbol, lineSpec: bool) -> None: + # Note: 'lineSpec' has no effect on template introductions. + signode = addnodes.desc_signature_line() + parentNode += signode + signode.sphinx_line_type = 'templateIntroduction' + self.concept.describe_signature(signode, 'markType', env, symbol) + signode += addnodes.desc_sig_punctuation('{', '{') + first = True + for param in self.params: + if not first: + signode += addnodes.desc_sig_punctuation(',', ',') + signode += addnodes.desc_sig_space() + first = False + param.describe_signature(signode, mode, env, symbol) + signode += addnodes.desc_sig_punctuation('}', '}') + + +################################################################################ + +class ASTTemplateDeclarationPrefix(ASTBase): + def __init__(self, + templates: list[ASTTemplateParams | ASTTemplateIntroduction] | None) -> None: + # templates is None means it's an explicit instantiation of a variable + self.templates = templates + + def get_requires_clause_in_last(self) -> ASTRequiresClause | None: + 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 the Itanium ABI mangling system. + res = [] + 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: + return ''.join(map(transform, self.templates)) + + def describe_signature(self, signode: desc_signature, mode: str, + env: BuildEnvironment, symbol: Symbol, lineSpec: bool) -> None: + verify_description_mode(mode) + for t in self.templates: + t.describe_signature_as_introducer(signode, 'lastIsName', env, symbol, lineSpec) + + +class ASTRequiresClause(ASTBase): + def __init__(self, expr: ASTExpression) -> None: + self.expr = expr + + def _stringify(self, transform: StringifyTransform) -> str: + return 'requires ' + transform(self.expr) + + 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() + self.expr.describe_signature(signode, mode, env, symbol) + + +################################################################################ +################################################################################ + +class ASTDeclaration(ASTBase): + def __init__(self, objectType: str, directiveType: str | None = None, + visibility: str | None = None, + templatePrefix: ASTTemplateDeclarationPrefix | None = None, + declaration: Any = None, + trailingRequiresClause: ASTRequiresClause | None = None, + semicolon: bool = False) -> None: + self.objectType = objectType + self.directiveType = directiveType + self.visibility = visibility + self.templatePrefix = templatePrefix + self.declaration = declaration + self.trailingRequiresClause = trailingRequiresClause + self.semicolon = semicolon + + self.symbol: Symbol | None = None + # set by CPPObject._add_enumerator_to_parent + self.enumeratorScopedSymbol: Symbol | None = None + + # the cache assumes that by the time get_newest_id is called, no + # further changes will be made to this object + self._newest_id_cache: str | None = None + + def clone(self) -> ASTDeclaration: + templatePrefixClone = self.templatePrefix.clone() if self.templatePrefix else None + trailingRequiresClasueClone = self.trailingRequiresClause.clone() \ + if self.trailingRequiresClause else None + return ASTDeclaration(self.objectType, self.directiveType, self.visibility, + templatePrefixClone, + self.declaration.clone(), trailingRequiresClasueClone, + self.semicolon) + + @property + def name(self) -> ASTNestedName: + return self.declaration.name + + @property + def function_params(self) -> list[ASTFunctionParameter]: + if self.objectType != 'function': + return None + return self.declaration.function_params + + def get_id(self, version: int, prefixed: bool = True) -> str: + if version == 1: + if self.templatePrefix or self.trailingRequiresClause: + raise NoOldIdError + if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: + return self.enumeratorScopedSymbol.declaration.get_id(version) + return self.declaration.get_id(version, self.objectType, self.symbol) + # version >= 2 + if self.objectType == 'enumerator' and self.enumeratorScopedSymbol: + return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed) + if prefixed: + res = [_id_prefix[version]] + else: + res = [] + # (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 requiresClauseInLast and self.trailingRequiresClause: + # make a conjunction of them + res.append('aa') + if requiresClauseInLast: + res.append(requiresClauseInLast.expr.get_id(version)) + if self.trailingRequiresClause: + res.append(self.trailingRequiresClause.expr.get_id(version)) + res.append('E') + res.append(self.declaration.get_id(version, self.objectType, self.symbol)) + return ''.join(res) + + def get_newest_id(self) -> str: + if self._newest_id_cache is None: + self._newest_id_cache = self.get_id(_max_id, True) + return self._newest_id_cache + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.visibility and self.visibility != "public": + res.append(self.visibility) + res.append(' ') + if self.templatePrefix: + res.append(transform(self.templatePrefix)) + res.append(transform(self.declaration)) + if self.trailingRequiresClause: + res.append(' ') + res.append(transform(self.trailingRequiresClause)) + if self.semicolon: + res.append(';') + return ''.join(res) + + def describe_signature(self, signode: desc_signature, mode: str, + env: BuildEnvironment, options: dict[str, bool]) -> None: + verify_description_mode(mode) + assert self.symbol + # The caller of the domain added a desc_signature node. + # Always enable multiline: + signode['is_multiline'] = True + # Put each line in a desc_signature_line node. + mainDeclNode = addnodes.desc_signature_line() + mainDeclNode.sphinx_line_type = 'declarator' + mainDeclNode['add_permalink'] = not self.symbol.isRedeclaration + + if self.templatePrefix: + self.templatePrefix.describe_signature(signode, mode, env, + symbol=self.symbol, + lineSpec=options.get('tparam-line-spec')) + signode += mainDeclNode + if self.visibility and self.visibility != "public": + mainDeclNode += addnodes.desc_sig_keyword(self.visibility, self.visibility) + mainDeclNode += addnodes.desc_sig_space() + if self.objectType == 'type': + prefix = self.declaration.get_type_declaration_prefix() + mainDeclNode += addnodes.desc_sig_keyword(prefix, prefix) + mainDeclNode += addnodes.desc_sig_space() + elif self.objectType == 'concept': + mainDeclNode += addnodes.desc_sig_keyword('concept', 'concept') + mainDeclNode += addnodes.desc_sig_space() + elif self.objectType in {'member', 'function'}: + pass + elif self.objectType == 'class': + assert self.directiveType in ('class', 'struct') + mainDeclNode += addnodes.desc_sig_keyword(self.directiveType, self.directiveType) + mainDeclNode += addnodes.desc_sig_space() + elif self.objectType == 'union': + mainDeclNode += addnodes.desc_sig_keyword('union', 'union') + mainDeclNode += addnodes.desc_sig_space() + elif self.objectType == 'enum': + mainDeclNode += addnodes.desc_sig_keyword('enum', 'enum') + mainDeclNode += addnodes.desc_sig_space() + if self.directiveType == 'enum-class': + mainDeclNode += addnodes.desc_sig_keyword('class', 'class') + mainDeclNode += addnodes.desc_sig_space() + elif self.directiveType == 'enum-struct': + mainDeclNode += addnodes.desc_sig_keyword('struct', 'struct') + mainDeclNode += addnodes.desc_sig_space() + else: + assert self.directiveType == 'enum', self.directiveType + elif self.objectType == 'enumerator': + mainDeclNode += addnodes.desc_sig_keyword('enumerator', 'enumerator') + mainDeclNode += addnodes.desc_sig_space() + else: + raise AssertionError(self.objectType) + self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol) + lastDeclNode = mainDeclNode + if self.trailingRequiresClause: + trailingReqNode = addnodes.desc_signature_line() + trailingReqNode.sphinx_line_type = 'trailingRequiresClause' + signode.append(trailingReqNode) + lastDeclNode = trailingReqNode + self.trailingRequiresClause.describe_signature( + trailingReqNode, 'markType', env, self.symbol) + if self.semicolon: + lastDeclNode += addnodes.desc_sig_punctuation(';', ';') + + +class ASTNamespace(ASTBase): + def __init__(self, nestedName: ASTNestedName, + templatePrefix: ASTTemplateDeclarationPrefix) -> None: + self.nestedName = nestedName + self.templatePrefix = templatePrefix + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + if self.templatePrefix: + res.append(transform(self.templatePrefix)) + res.append(transform(self.nestedName)) + return ''.join(res) |