diff options
Diffstat (limited to 'sphinx/domains/cpp.py')
-rw-r--r-- | sphinx/domains/cpp.py | 3558 |
1 files changed, 1906 insertions, 1652 deletions
diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index 53c958601..7f7558cb3 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -9,12 +9,12 @@ """ import re -import warnings -from copy import deepcopy -from typing import Any, Callable, Dict, Iterator, List, Match, Pattern, Tuple, Type, Union +from typing import ( + Any, Callable, Dict, Iterator, List, Tuple, Type, TypeVar, Union +) -from docutils import nodes, utils -from docutils.nodes import Element, Node, TextElement +from docutils import nodes +from docutils.nodes import Element, Node, TextElement, system_message from docutils.parsers.rst import directives from sphinx import addnodes @@ -22,23 +22,29 @@ from sphinx.addnodes import desc_signature, pending_xref from sphinx.application import Sphinx from sphinx.builders import Builder from sphinx.config import Config -from sphinx.deprecation import RemovedInSphinx40Warning from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType from sphinx.environment import BuildEnvironment from sphinx.errors import NoUri from sphinx.locale import _, __ -from sphinx.roles import XRefRole +from sphinx.roles import SphinxRole, XRefRole from sphinx.transforms import SphinxTransform from sphinx.transforms.post_transforms import ReferencesResolver from sphinx.util import logging +from sphinx.util.cfamily import ( + NoOldIdError, ASTBaseBase, verify_description_mode, StringifyTransform, + BaseParser, DefinitionError, UnsupportedMultiCharacterCharLiteral, + identifier_re, anon_identifier_re, integer_literal_re, octal_literal_re, + hex_literal_re, binary_literal_re, float_literal_re, + char_literal_re +) from sphinx.util.docfields import Field, GroupedField from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_refnode logger = logging.getLogger(__name__) -StringifyTransform = Callable[[Any], str] +T = TypeVar('T') """ Important note on ids @@ -61,7 +67,7 @@ StringifyTransform = Callable[[Any], str] Each signature is in a desc_signature node, where all children are desc_signature_line nodes. Each of these lines will have the attribute - 'sphinx_cpp_tagname' set to one of the following (prioritized): + 'sphinx_line_type' set to one of the following (prioritized): - 'declarator', if the line contains the name of the declared object. - 'templateParams', if the line starts a template parameter list, - 'templateParams', if the line has template parameters @@ -290,47 +296,6 @@ StringifyTransform = Callable[[Any], str] nested-name """ -_integer_literal_re = re.compile(r'[1-9][0-9]*') -_octal_literal_re = re.compile(r'0[0-7]*') -_hex_literal_re = re.compile(r'0[xX][0-9a-fA-F][0-9a-fA-F]*') -_binary_literal_re = re.compile(r'0[bB][01][01]*') -_integer_suffix_re = re.compile(r'') -_float_literal_re = re.compile(r'''(?x) - [+-]?( - # decimal - ([0-9]+[eE][+-]?[0-9]+) - | ([0-9]*\.[0-9]+([eE][+-]?[0-9]+)?) - | ([0-9]+\.([eE][+-]?[0-9]+)?) - # hex - | (0[xX][0-9a-fA-F]+[pP][+-]?[0-9a-fA-F]+) - | (0[xX][0-9a-fA-F]*\.[0-9a-fA-F]+([pP][+-]?[0-9a-fA-F]+)?) - | (0[xX][0-9a-fA-F]+\.([pP][+-]?[0-9a-fA-F]+)?) - ) -''') -_char_literal_re = re.compile(r'''(?x) - ((?:u8)|u|U|L)? - '( - (?:[^\\']) - | (\\( - (?:['"?\\abfnrtv]) - | (?:[0-7]{1,3}) - | (?:x[0-9a-fA-F]{2}) - | (?:u[0-9a-fA-F]{4}) - | (?:U[0-9a-fA-F]{8}) - )) - )' -''') - -_anon_identifier_re = re.compile(r'(@[a-zA-Z0-9_])[a-zA-Z0-9_]*\b') -_identifier_re = re.compile(r'''(?x) - ( # This 'extends' _anon_identifier_re with the ordinary identifiers, - # make sure they are in sync. - (~?\b[a-zA-Z_]) # ordinary identifiers - | (@[a-zA-Z0-9_]) # our extension for names of anonymous entities - ) - [a-zA-Z0-9_]*\b -''') -_whitespace_re = re.compile(r'(?u)\s+') _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'" r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S) _visibility_re = re.compile(r'\b(public|private|protected)\b') @@ -367,6 +332,8 @@ _keywords = [ _max_id = 4 _id_prefix = [None, '', '_CPPv2', '_CPPv3', '_CPPv4'] +# Ids are used in lookup keys which are used across pickled files, +# so when _max_id changes, make sure to update the ENV_VERSION. # ------------------------------------------------------------------------------ # Id v1 constants @@ -576,27 +543,8 @@ _id_explicit_cast = { } -class NoOldIdError(Exception): - # Used to avoid implementing unneeded id generation for old id schemes. - @property - def description(self) -> str: - warnings.warn('%s.description is deprecated. ' - 'Coerce the instance to a string instead.' % self.__class__.__name__, - RemovedInSphinx40Warning, stacklevel=2) - return str(self) - - -class DefinitionError(Exception): - @property - def description(self) -> str: - warnings.warn('%s.description is deprecated. ' - 'Coerce the instance to a string instead.' % self.__class__.__name__, - RemovedInSphinx40Warning, stacklevel=2) - return str(self) - - class _DuplicateSymbolError(Exception): - def __init__(self, symbol: "Symbol", declaration: Any) -> None: + def __init__(self, symbol: "Symbol", declaration: "ASTDeclaration") -> None: assert symbol assert declaration self.symbol = symbol @@ -606,54 +554,237 @@ class _DuplicateSymbolError(Exception): return "Internal C++ duplicate symbol error:\n%s" % self.symbol.dump(0) -class ASTBase: - def __eq__(self, other: Any) -> bool: - if type(self) is not type(other): - return False - try: - for key, value in self.__dict__.items(): - if value != getattr(other, key): - return False - except AttributeError: - return False - return True +class ASTBase(ASTBaseBase): + pass - __hash__ = None # type: Callable[[], int] - def clone(self) -> Any: - """Clone a definition expression node.""" - return deepcopy(self) +# Names +################################################################################ - def _stringify(self, transform: StringifyTransform) -> str: - raise NotImplementedError(repr(self)) +class ASTIdentifier(ASTBase): + def __init__(self, identifier: str) -> None: + assert identifier is not None + assert len(identifier) != 0 + self.identifier = 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._stringify(lambda ast: str(ast)) + return self.identifier def get_display_string(self) -> str: - return self._stringify(lambda ast: ast.get_display_string()) + 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 mode == 'markType': + targetText = prefix + self.identifier + templateArgs + pnode = addnodes.pending_xref('', refdomain='cpp', + reftype='identifier', + reftarget=targetText, modname=None, + classname=None) + key = symbol.get_lookup_key() + pnode['cpp:parent_key'] = key + if self.is_anon(): + pnode += nodes.strong(text="[anonymous]") + else: + pnode += nodes.Text(self.identifier) + signode += pnode + elif mode == 'lastIsName': + if self.is_anon(): + signode += nodes.strong(text="[anonymous]") + else: + signode += addnodes.desc_name(self.identifier, self.identifier) + elif mode == 'noneIsName': + if self.is_anon(): + signode += nodes.strong(text="[anonymous]") + else: + signode += nodes.Text(self.identifier) + else: + raise Exception('Unknown description mode: %s' % mode) + + +class ASTNestedNameElement(ASTBase): + def __init__(self, identOrOp: Union[ASTIdentifier, "ASTOperator"], + templateArgs: "ASTTemplateArgs") -> 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, mode, 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) - def __repr__(self) -> str: - return '<%s>' % self.__class__.__name__ + res = [] + if len(self.names) > 1 or len(modifiers) > 0: + res.append('N') + res.append(modifiers) + for n in self.names: + res.append(n.get_id(version)) + 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] + t = self.templates[i] + if t: + res.append("template " + transform(n)) + else: + res.append(transform(n)) + return '::'.join(res) -def _verify_description_mode(mode: str) -> None: - if mode not in ('lastIsName', 'noneIsName', 'markType', 'markName', 'param'): - raise Exception("Description mode '%s' is invalid." % mode) + 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': + signode += nodes.Text(str(self)) + elif mode == 'param': + name = str(self) + signode += nodes.emphasis(name, name) + elif mode == 'markType' or mode == 'lastIsName' or mode == '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 = [] # type: 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 relies 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() + for i in range(len(names)): + nne = names[i] + template = self.templates[i] + if not first: + dest += nodes.Text('::') + prefix += '::' + if template: + dest += nodes.Text("template ") + 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_addname('::', '::') + signode += dest + if self.templates[-1]: + signode += nodes.Text("template ") + self.names[-1].describe_signature(signode, mode, env, '', symbol) + else: + raise Exception('Unknown description mode: %s' % mode) ################################################################################ # Attributes ################################################################################ -class ASTCPPAttribute(ASTBase): +class ASTAttribute(ASTBase): + def describe_signature(self, signode: TextElement) -> None: + raise NotImplementedError(repr(self)) + + +class ASTCPPAttribute(ASTAttribute): def __init__(self, arg: str) -> None: self.arg = arg - def _stringify(self, transform): + def _stringify(self, transform: StringifyTransform) -> str: return "[[" + self.arg + "]]" - def describe_signature(self, signode: desc_signature) -> None: + def describe_signature(self, signode: TextElement) -> None: txt = str(self) signode.append(nodes.Text(txt, txt)) @@ -672,8 +803,8 @@ class ASTGnuAttribute(ASTBase): return ''.join(res) -class ASTGnuAttributeList(ASTBase): - def __init__(self, attrs: List[Any]) -> None: +class ASTGnuAttributeList(ASTAttribute): + def __init__(self, attrs: List[ASTGnuAttribute]) -> None: self.attrs = attrs def _stringify(self, transform: StringifyTransform) -> str: @@ -687,12 +818,12 @@ class ASTGnuAttributeList(ASTBase): res.append('))') return ''.join(res) - def describe_signature(self, signode: desc_signature) -> None: + def describe_signature(self, signode: TextElement) -> None: txt = str(self) signode.append(nodes.Text(txt, txt)) -class ASTIdAttribute(ASTBase): +class ASTIdAttribute(ASTAttribute): """For simple attributes defined by the user.""" def __init__(self, id: str) -> None: @@ -701,11 +832,11 @@ class ASTIdAttribute(ASTBase): def _stringify(self, transform: StringifyTransform) -> str: return self.id - def describe_signature(self, signode: desc_signature) -> None: + def describe_signature(self, signode: TextElement) -> None: signode.append(nodes.Text(self.id, self.id)) -class ASTParenAttribute(ASTBase): +class ASTParenAttribute(ASTAttribute): """For paren attributes defined by the user.""" def __init__(self, id: str, arg: str) -> None: @@ -715,28 +846,45 @@ class ASTParenAttribute(ASTBase): def _stringify(self, transform: StringifyTransform) -> str: return self.id + '(' + self.arg + ')' - def describe_signature(self, signode: desc_signature) -> None: + def describe_signature(self, signode: TextElement) -> None: txt = str(self) signode.append(nodes.Text(txt, txt)) ################################################################################ -# Expressions and Literals +# 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 ASTPointerLiteral(ASTBase): +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, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('nullptr')) -class ASTBooleanLiteral(ASTBase): - def __init__(self, value): +class ASTBooleanLiteral(ASTLiteral): + def __init__(self, value: bool) -> None: self.value = value def _stringify(self, transform: StringifyTransform) -> str: @@ -751,11 +899,12 @@ class ASTBooleanLiteral(ASTBase): else: return 'L0E' - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text(str(self))) -class ASTNumberLiteral(ASTBase): +class ASTNumberLiteral(ASTLiteral): def __init__(self, data: str) -> None: self.data = data @@ -765,21 +914,30 @@ class ASTNumberLiteral(ASTBase): def get_id(self, version: int) -> str: return "L%sE" % self.data - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: txt = str(self) signode.append(nodes.Text(txt, txt)) -class UnsupportedMultiCharacterCharLiteral(Exception): - @property - def decoded(self) -> str: - warnings.warn('%s.decoded is deprecated. ' - 'Coerce the instance to a string instead.' % self.__class__.__name__, - RemovedInSphinx40Warning, stacklevel=2) - return str(self) +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) -class ASTCharLiteral(ASTBase): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + txt = str(self) + signode.append(nodes.Text(txt, txt)) + + +class ASTCharLiteral(ASTLiteral): def __init__(self, prefix: str, data: str) -> None: self.prefix = prefix # may be None when no prefix self.data = data @@ -800,56 +958,27 @@ class ASTCharLiteral(ASTBase): def get_id(self, version: int) -> str: return self.type + str(self.value) - def describe_signature(self, signode, mode, env, symbol): - txt = str(self) - signode.append(nodes.Text(txt, txt)) - - -class ASTStringLiteral(ASTBase): - 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, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: txt = str(self) signode.append(nodes.Text(txt, txt)) -class ASTThisLiteral(ASTBase): +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, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text("this")) -class ASTParenExpr(ASTBase): - def __init__(self, expr): - 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, mode, env, symbol): - signode.append(nodes.Text('(', '(')) - self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')', ')')) - - -class ASTFoldExpr(ASTBase): - def __init__(self, leftExpr: Any, op: str, rightExpr: Any) -> None: +class ASTFoldExpr(ASTExpression): + def __init__(self, leftExpr: ASTExpression, + op: str, rightExpr: ASTExpression) -> None: assert leftExpr is not None or rightExpr is not None self.leftExpr = leftExpr self.op = op @@ -892,7 +1021,8 @@ class ASTFoldExpr(ASTBase): res.append(self.rightExpr.get_id(version)) return ''.join(res) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('(')) if self.leftExpr: self.leftExpr.describe_signature(signode, mode, env, symbol) @@ -908,99 +1038,224 @@ class ASTFoldExpr(ASTBase): signode.append(nodes.Text(')')) -class ASTBinOpExpr(ASTBase): - def __init__(self, exprs, ops): - assert len(exprs) > 0 - assert len(exprs) == len(ops) + 1 - self.exprs = exprs - self.ops = ops +class ASTParenExpr(ASTExpression): + def __init__(self, expr: ASTExpression): + self.expr = expr 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) + return '(' + transform(self.expr) + ')' 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 self.expr.get_id(version) + + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + signode.append(nodes.Text('(', '(')) + self.expr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')', ')')) + + +class ASTIdExpression(ASTExpression): + def __init__(self, name: ASTNestedName): + # 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): + 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.append(nodes.Text('[')) + self.expr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(']')) + + +class ASTPostfixMember(ASTPostfixOp): + def __init__(self, name: ASTNestedName): + 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.append(nodes.Text('.')) + self.name.describe_signature(signode, 'noneIsName', env, symbol) + + +class ASTPostfixMemberOfPointer(ASTPostfixOp): + def __init__(self, name: ASTNestedName): + 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.append(nodes.Text('->')) + 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.append(nodes.Text('++')) + + +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.append(nodes.Text('--')) + + +class ASTPostfixCallExpr(ASTPostfixOp): + def __init__(self, lst: Union["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: + res = ['cl', idPrefix] + for e in self.lst.exprs: + res.append(e.get_id(version)) + res.append('E') return ''.join(res) - def describe_signature(self, signode, mode, env, symbol): - self.exprs[0].describe_signature(signode, mode, env, symbol) - for i in range(1, len(self.exprs)): - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.ops[i - 1])) - signode.append(nodes.Text(' ')) - self.exprs[i].describe_signature(signode, mode, env, symbol) + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + self.lst.describe_signature(signode, mode, env, symbol) -class ASTAssignmentExpr(ASTBase): - def __init__(self, exprs, ops): - assert len(exprs) > 0 - assert len(exprs) == len(ops) + 1 - self.exprs = exprs - self.ops = ops +class ASTPostfixExpr(ASTExpression): + def __init__(self, prefix: "ASTType", postFixes: List[ASTPostfixOp]): + self.prefix = prefix + self.postFixes = postFixes 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])) + res = [transform(self.prefix)] + for p in self.postFixes: + res.append(transform(p)) return ''.join(res) def get_id(self, version: int) -> str: - 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) + id = self.prefix.get_id(version) + for p in self.postFixes: + id = p.get_id(id, version) + return id - def describe_signature(self, signode, mode, env, symbol): - self.exprs[0].describe_signature(signode, mode, env, symbol) - for i in range(1, len(self.exprs)): - signode.append(nodes.Text(' ')) - signode.append(nodes.Text(self.ops[i - 1])) - signode.append(nodes.Text(' ')) - self.exprs[i].describe_signature(signode, mode, env, symbol) + 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 ASTCastExpr(ASTBase): - def __init__(self, typ, expr): +class ASTExplicitCast(ASTExpression): + def __init__(self, cast: str, typ: "ASTType", expr: ASTExpression): + assert cast in _id_explicit_cast + self.cast = cast self.typ = typ self.expr = expr def _stringify(self, transform: StringifyTransform) -> str: - res = ['('] + res = [self.cast] + res.append('<') res.append(transform(self.typ)) - res.append(')') + res.append('>(') res.append(transform(self.expr)) + res.append(')') return ''.join(res) def get_id(self, version: int) -> str: - return 'cv' + self.typ.get_id(version) + self.expr.get_id(version) + return (_id_explicit_cast[self.cast] + + self.typ.get_id(version) + + self.expr.get_id(version)) - def describe_signature(self, signode, mode, env, symbol): - signode.append(nodes.Text('(')) + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + signode.append(nodes.Text(self.cast)) + signode.append(nodes.Text('<')) self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) + signode.append(nodes.Text('>')) + signode.append(nodes.Text('(')) self.expr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) -class ASTUnaryOpExpr(ASTBase): - def __init__(self, op, expr): +class ASTTypeId(ASTExpression): + def __init__(self, typeOrExpr: Union["ASTType", ASTExpression], isType: bool): + 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.append(nodes.Text('typeid')) + signode.append(nodes.Text('(')) + self.typeOrExpr.describe_signature(signode, mode, env, symbol) + signode.append(nodes.Text(')')) + + +# Unary expressions +################################################################################ + +class ASTUnaryOpExpr(ASTExpression): + def __init__(self, op: str, expr: ASTExpression): self.op = op self.expr = expr @@ -1010,13 +1265,14 @@ class ASTUnaryOpExpr(ASTBase): def get_id(self, version: int) -> str: return _id_operator_unary_v2[self.op] + self.expr.get_id(version) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text(self.op)) self.expr.describe_signature(signode, mode, env, symbol) -class ASTSizeofParamPack(ASTBase): - def __init__(self, identifier): +class ASTSizeofParamPack(ASTExpression): + def __init__(self, identifier: ASTIdentifier): self.identifier = identifier def _stringify(self, transform: StringifyTransform) -> str: @@ -1025,15 +1281,16 @@ class ASTSizeofParamPack(ASTBase): def get_id(self, version: int) -> str: return 'sZ' + self.identifier.get_id(version) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('sizeof...(')) self.identifier.describe_signature(signode, mode, env, symbol=symbol, prefix="", templateArgs="") signode.append(nodes.Text(')')) -class ASTSizeofType(ASTBase): - def __init__(self, typ): +class ASTSizeofType(ASTExpression): + def __init__(self, typ: "ASTType"): self.typ = typ def _stringify(self, transform: StringifyTransform) -> str: @@ -1042,14 +1299,15 @@ class ASTSizeofType(ASTBase): def get_id(self, version: int) -> str: return 'st' + self.typ.get_id(version) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('sizeof(')) self.typ.describe_signature(signode, mode, env, symbol) signode.append(nodes.Text(')')) -class ASTSizeofExpr(ASTBase): - def __init__(self, expr): +class ASTSizeofExpr(ASTExpression): + def __init__(self, expr: ASTExpression): self.expr = expr def _stringify(self, transform: StringifyTransform) -> str: @@ -1058,13 +1316,14 @@ class ASTSizeofExpr(ASTBase): def get_id(self, version: int) -> str: return 'sz' + self.expr.get_id(version) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('sizeof ')) self.expr.describe_signature(signode, mode, env, symbol) -class ASTAlignofExpr(ASTBase): - def __init__(self, typ): +class ASTAlignofExpr(ASTExpression): + def __init__(self, typ: "ASTType"): self.typ = typ def _stringify(self, transform: StringifyTransform) -> str: @@ -1073,14 +1332,15 @@ class ASTAlignofExpr(ASTBase): def get_id(self, version: int) -> str: return 'at' + self.typ.get_id(version) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('alignof(')) self.typ.describe_signature(signode, mode, env, symbol) signode.append(nodes.Text(')')) -class ASTNoexceptExpr(ASTBase): - def __init__(self, expr): +class ASTNoexceptExpr(ASTExpression): + def __init__(self, expr: ASTExpression): self.expr = expr def _stringify(self, transform: StringifyTransform) -> str: @@ -1089,14 +1349,16 @@ class ASTNoexceptExpr(ASTBase): def get_id(self, version: int) -> str: return 'nx' + self.expr.get_id(version) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('noexcept(')) self.expr.describe_signature(signode, mode, env, symbol) signode.append(nodes.Text(')')) -class ASTNewExpr(ASTBase): - def __init__(self, rooted: bool, isNewTypeId: bool, typ: "ASTType", initList: Any) -> None: +class ASTNewExpr(ASTExpression): + def __init__(self, rooted: bool, isNewTypeId: bool, typ: "ASTType", + initList: Union["ASTParenExprList", "ASTBracedInitList"]) -> None: self.rooted = rooted self.isNewTypeId = isNewTypeId self.typ = typ @@ -1128,7 +1390,8 @@ class ASTNewExpr(ASTBase): res.append('E') return ''.join(res) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: if self.rooted: signode.append(nodes.Text('::')) signode.append(nodes.Text('new ')) @@ -1141,8 +1404,8 @@ class ASTNewExpr(ASTBase): self.initList.describe_signature(signode, mode, env, symbol) -class ASTDeleteExpr(ASTBase): - def __init__(self, rooted, array, expr): +class ASTDeleteExpr(ASTExpression): + def __init__(self, rooted: bool, array: bool, expr: ASTExpression): self.rooted = rooted self.array = array self.expr = expr @@ -1164,7 +1427,8 @@ class ASTDeleteExpr(ASTBase): id = "dl" return id + self.expr.get_id(version) - def describe_signature(self, signode, mode, env, symbol): + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: if self.rooted: signode.append(nodes.Text('::')) signode.append(nodes.Text('delete ')) @@ -1173,653 +1437,127 @@ class ASTDeleteExpr(ASTBase): self.expr.describe_signature(signode, mode, env, symbol) -class ASTExplicitCast(ASTBase): - def __init__(self, cast, typ, expr): - assert cast in _id_explicit_cast - self.cast = cast +# Other expressions +################################################################################ + +class ASTCastExpr(ASTExpression): + def __init__(self, typ: "ASTType", expr: ASTExpression): self.typ = typ self.expr = expr def _stringify(self, transform: StringifyTransform) -> str: - res = [self.cast] - res.append('<') + res = ['('] res.append(transform(self.typ)) - res.append('>(') - res.append(transform(self.expr)) res.append(')') + res.append(transform(self.expr)) 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, mode, env, symbol): - signode.append(nodes.Text(self.cast)) - signode.append(nodes.Text('<')) - self.typ.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text('>')) - signode.append(nodes.Text('(')) - self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(')')) - - -class ASTTypeId(ASTBase): - def __init__(self, typeOrExpr, isType): - 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) + return 'cv' + self.typ.get_id(version) + self.expr.get_id(version) - def describe_signature(self, signode, mode, env, symbol): - signode.append(nodes.Text('typeid')) + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('(')) - self.typeOrExpr.describe_signature(signode, mode, env, symbol) + self.typ.describe_signature(signode, mode, env, symbol) signode.append(nodes.Text(')')) - - -class ASTPostfixCallExpr(ASTBase): - def __init__(self, lst: Union["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: - res = ['cl', idPrefix] - for e in self.lst.exprs: - res.append(e.get_id(version)) - res.append('E') - return ''.join(res) - - def describe_signature(self, signode, mode, env, symbol): - self.lst.describe_signature(signode, mode, env, symbol) - - -class ASTPostfixArray(ASTBase): - def __init__(self, expr): - 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, mode, env, symbol): - signode.append(nodes.Text('[')) self.expr.describe_signature(signode, mode, env, symbol) - signode.append(nodes.Text(']')) - - -class ASTPostfixInc(ASTBase): - def _stringify(self, transform: StringifyTransform) -> str: - return '++' - - def get_id(self, idPrefix: str, version: int) -> str: - return 'pp' + idPrefix - - def describe_signature(self, signode, mode, env, symbol): - signode.append(nodes.Text('++')) - -class ASTPostfixDec(ASTBase): - def _stringify(self, transform: StringifyTransform) -> str: - return '--' - - def get_id(self, idPrefix: str, version: int) -> str: - return 'mm' + idPrefix - def describe_signature(self, signode, mode, env, symbol): - signode.append(nodes.Text('--')) - - -class ASTPostfixMember(ASTBase): - def __init__(self, name): - 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, mode, env, symbol): - signode.append(nodes.Text('.')) - self.name.describe_signature(signode, 'noneIsName', env, symbol) - - -class ASTPostfixMemberOfPointer(ASTBase): - def __init__(self, name): - 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, mode, env, symbol): - signode.append(nodes.Text('->')) - self.name.describe_signature(signode, 'noneIsName', env, symbol) - - -class ASTPostfixExpr(ASTBase): - def __init__(self, prefix, postFixes): - assert len(postFixes) > 0 - self.prefix = prefix - self.postFixes = postFixes +class ASTBinOpExpr(ASTExpression): + def __init__(self, exprs: List[ASTExpression], ops: List[str]): + assert len(exprs) > 0 + assert len(exprs) == len(ops) + 1 + self.exprs = exprs + self.ops = ops def _stringify(self, transform: StringifyTransform) -> str: - res = [transform(self.prefix)] - for p in self.postFixes: - res.append(transform(p)) + 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: - id = self.prefix.get_id(version) - for p in self.postFixes: - id = p.get_id(id, version) - return id - - def describe_signature(self, signode, mode, env, symbol): - self.prefix.describe_signature(signode, mode, env, symbol) - for p in self.postFixes: - p.describe_signature(signode, mode, env, symbol) - - -class ASTPackExpansionExpr(ASTBase): - def __init__(self, expr): - 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, mode, env, symbol): - self.expr.describe_signature(signode, mode, env, symbol) - signode += nodes.Text('...') - - -class ASTFallbackExpr(ASTBase): - def __init__(self, expr): - 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, mode, env, symbol): - signode += nodes.Text(self.expr) - - -################################################################################ -# The Rest -################################################################################ - -class ASTIdentifier(ASTBase): - def __init__(self, identifier: str) -> None: - assert identifier is not None - assert len(identifier) != 0 - self.identifier = 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: Any, mode: str, env: "BuildEnvironment", - prefix: str, templateArgs: str, symbol: "Symbol") -> None: - _verify_description_mode(mode) - if mode == 'markType': - targetText = prefix + self.identifier + templateArgs - pnode = addnodes.pending_xref('', refdomain='cpp', - reftype='identifier', - reftarget=targetText, modname=None, - classname=None) - key = symbol.get_lookup_key() - pnode['cpp:parent_key'] = key - if self.is_anon(): - pnode += nodes.strong(text="[anonymous]") - else: - pnode += nodes.Text(self.identifier) - signode += pnode - elif mode == 'lastIsName': - if self.is_anon(): - signode += nodes.strong(text="[anonymous]") - else: - signode += addnodes.desc_name(self.identifier, self.identifier) - elif mode == 'noneIsName': - if self.is_anon(): - signode += nodes.strong(text="[anonymous]") - else: - signode += nodes.Text(self.identifier) - else: - raise Exception('Unknown description mode: %s' % mode) - - -class ASTTemplateKeyParamPackIdDefault(ASTBase): - 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)) + 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: desc_signature, mode: str, - env: "BuildEnvironment", symbol: "Symbol") -> None: - signode += nodes.Text(self.key) - if self.parameterPack: - if self.identifier: - signode += nodes.Text(' ') - signode += nodes.Text('...') - if self.identifier: - if not self.parameterPack: - signode += nodes.Text(' ') - self.identifier.describe_signature(signode, mode, env, '', '', symbol) - if self.default: - signode += nodes.Text(' = ') - self.default.describe_signature(signode, 'markType', env, symbol) - - -class ASTTemplateParamType(ASTBase): - 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, symbol: "Symbol" = 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: desc_signature, mode: str, - env: "BuildEnvironment", symbol: "Symbol") -> None: - self.data.describe_signature(signode, mode, env, symbol) - - -class ASTTemplateParamConstrainedTypeWithInit(ASTBase): - def __init__(self, type: Any, init: Any) -> 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, symbol: "Symbol" = 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: desc_signature, mode: str, - env: "BuildEnvironment", symbol: "Symbol") -> None: - self.type.describe_signature(signode, mode, env, symbol) - if self.init: - signode += nodes.Text(" = ") - self.init.describe_signature(signode, mode, env, symbol) - - -class ASTTemplateParamTemplateType(ASTBase): - def __init__(self, nestedParams: Any, 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, symbol: "Symbol" = 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: desc_signature, mode: str, - env: "BuildEnvironment", symbol: "Symbol") -> None: - self.nestedParams.describe_signature(signode, 'noneIsName', env, symbol) - signode += nodes.Text(' ') - self.data.describe_signature(signode, mode, env, symbol) - - -class ASTTemplateParamNonType(ASTBase): - def __init__(self, param: Any) -> None: - assert param - self.param = param - - @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 - - 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 - return name.names[0].identOrOp - else: - return None - - def get_id(self, version: int, objectType: str = None, symbol: "Symbol" = 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.param.get_id(version) - - def _stringify(self, transform: StringifyTransform) -> str: - return transform(self.param) - - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - self.param.describe_signature(signode, mode, env, symbol) - + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.ops[i - 1])) + signode.append(nodes.Text(' ')) + self.exprs[i].describe_signature(signode, mode, env, symbol) -class ASTTemplateParams(ASTBase): - def __init__(self, params: Any) -> None: - assert params is not None - self.params = params - self.isNested = False # whether it's a template template param - def get_id(self, version: int) -> str: - assert version >= 2 - res = [] - res.append("I") - for param in self.params: - res.append(param.get_id(version)) - res.append("E") - return ''.join(res) +class ASTAssignmentExpr(ASTExpression): + def __init__(self, exprs: List[ASTExpression], ops: List[str]): + 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("template<") - res.append(", ".join(transform(a) for a in self.params)) - res.append("> ") + 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 describe_signature(self, parentNode: desc_signature, mode: str, - env: "BuildEnvironment", symbol: "Symbol", lineSpec: bool = None - ) -> None: - # 'lineSpec' is defaulted becuase of template template parameters - def makeLine(parentNode=parentNode): - signode = addnodes.desc_signature_line() - parentNode += signode - signode.sphinx_cpp_tagname = 'templateParams' - return signode - if self.isNested: - lineNode = parentNode - else: - lineNode = makeLine() - lineNode += nodes.Text("template<") - first = True - for param in self.params: - if not first: - lineNode += nodes.Text(", ") - first = False - if lineSpec: - lineNode = makeLine() - param.describe_signature(lineNode, mode, env, symbol) - if lineSpec and not first: - lineNode = makeLine() - lineNode += nodes.Text(">") - - -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, symbol: "Symbol" = 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: + def get_id(self, version: int) -> str: res = [] - if self.parameterPack: - res.append('...') - res.append(transform(self.identifier)) + 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: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - if self.parameterPack: - signode += nodes.Text('...') - self.identifier.describe_signature(signode, mode, env, '', '', symbol) - + self.exprs[0].describe_signature(signode, mode, env, symbol) + for i in range(1, len(self.exprs)): + signode.append(nodes.Text(' ')) + signode.append(nodes.Text(self.ops[i - 1])) + signode.append(nodes.Text(' ')) + self.exprs[i].describe_signature(signode, mode, env, symbol) -class ASTTemplateIntroduction(ASTBase): - def __init__(self, concept: Any, params: List[Any]) -> None: - assert len(params) > 0 - self.concept = concept - self.params = params - def get_id(self, version: int) -> str: - assert version >= 2 - # first do the same as a normal template parameter list - res = [] - res.append("I") - for param in self.params: - res.append(param.get_id(version)) - res.append("E") - # let's use X expr E, which is otherwise for constant template args - res.append("X") - res.append(self.concept.get_id(version)) - res.append("I") - for param in self.params: - res.append(param.get_id_as_arg(version)) - res.append("E") - res.append("E") - return ''.join(res) +class ASTFallbackExpr(ASTExpression): + def __init__(self, expr: str): + self.expr = expr 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(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_cpp_tagname = 'templateIntroduction' - self.concept.describe_signature(signode, 'markType', env, symbol) - signode += nodes.Text('{') - first = True - for param in self.params: - if not first: - signode += nodes.Text(', ') - first = False - param.describe_signature(signode, mode, env, symbol) - signode += nodes.Text('}') - - -class ASTTemplateDeclarationPrefix(ASTBase): - def __init__(self, templates: List[Any]) -> None: - # templates is None means it's an explicit instantiation of a variable - self.templates = templates + return self.expr def get_id(self, version: int) -> str: - assert version >= 2 - # this is not part of a normal name mangling system - res = [] - for t in self.templates: - res.append(t.get_id(version)) - return ''.join(res) - - def _stringify(self, transform: StringifyTransform) -> str: - res = [] - for t in self.templates: - res.append(transform(t)) - return ''.join(res) + return str(self.expr) - 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(signode, 'lastIsName', env, symbol, lineSpec) + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + signode += nodes.Text(self.expr) -############################################################################################## +################################################################################ +# Types +################################################################################ +# Things for ASTNestedName +################################################################################ class ASTOperator(ASTBase): - def is_anon(self): + def is_anon(self) -> bool: return False def is_operator(self) -> bool: @@ -1828,10 +1566,10 @@ class ASTOperator(ASTBase): def get_id(self, version: int) -> str: raise NotImplementedError() - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", prefix: str, templateArgs: str, symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) identifier = str(self) if mode == 'lastIsName': signode += addnodes.desc_name(identifier, identifier) @@ -1860,42 +1598,39 @@ class ASTOperatorBuildIn(ASTOperator): return 'operator' + self.op -class ASTOperatorType(ASTOperator): - def __init__(self, type: Any) -> None: - self.type = type +class ASTOperatorLiteral(ASTOperator): + def __init__(self, identifier: ASTIdentifier) -> None: + self.identifier = identifier def get_id(self, version: int) -> str: if version == 1: - return 'castto-%s-operator' % self.type.get_id(version) + raise NoOldIdError() else: - return 'cv' + self.type.get_id(version) + return 'li' + self.identifier.get_id(version) def _stringify(self, transform: StringifyTransform) -> str: - return ''.join(['operator ', transform(self.type)]) - - def get_name_no_template(self) -> str: - return str(self) + return 'operator""' + transform(self.identifier) -class ASTOperatorLiteral(ASTOperator): - def __init__(self, identifier: Any) -> None: - self.identifier = identifier +class ASTOperatorType(ASTOperator): + def __init__(self, type: "ASTType") -> None: + self.type = type def get_id(self, version: int) -> str: if version == 1: - raise NoOldIdError() + return 'castto-%s-operator' % self.type.get_id(version) else: - return 'li' + self.identifier.get_id(version) + return 'cv' + self.type.get_id(version) def _stringify(self, transform: StringifyTransform) -> str: - return 'operator""' + transform(self.identifier) - + return ''.join(['operator ', transform(self.type)]) -############################################################################################## + def get_name_no_template(self) -> str: + return str(self) class ASTTemplateArgConstant(ASTBase): - def __init__(self, value: Any) -> None: + def __init__(self, value: ASTExpression) -> None: self.value = value def _stringify(self, transform: StringifyTransform) -> str: @@ -1908,14 +1643,14 @@ class ASTTemplateArgConstant(ASTBase): return 'X' + str(self) + 'E' return 'X' + self.value.get_id(version) + 'E' - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) self.value.describe_signature(signode, mode, env, symbol) class ASTTemplateArgs(ASTBase): - def __init__(self, args: List[Any]) -> None: + def __init__(self, args: List[Union["ASTType", ASTTemplateArgConstant]]) -> None: assert args is not None self.args = args @@ -1938,9 +1673,9 @@ class ASTTemplateArgs(ASTBase): res = ', '.join(transform(a) for a in self.args) return '<' + res + '>' - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) signode += nodes.Text('<') first = True for a in self.args: @@ -1951,148 +1686,19 @@ class ASTTemplateArgs(ASTBase): signode += nodes.Text('>') -class ASTNestedNameElement(ASTBase): - def __init__(self, identOrOp: Union["ASTIdentifier", "ASTOperator"], - templateArgs: ASTTemplateArgs) -> None: - self.identOrOp = identOrOp - self.templateArgs = templateArgs - - def is_operator(self) -> bool: - return False +# Main part of declarations +################################################################################ +class ASTTrailingTypeSpec(ASTBase): 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: desc_signature, 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, mode, 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) - for n in self.names: - res.append(n.get_id(version)) - 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] - t = self.templates[i] - if t: - res.append("template " + transform(n)) - else: - res.append(transform(n)) - return '::'.join(res) + raise NotImplementedError(repr(self)) - def describe_signature(self, signode: desc_signature, mode: str, + 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': - signode += nodes.Text(str(self)) - elif mode == 'param': - name = str(self) - signode += nodes.emphasis(name, name) - elif mode == 'markType' or mode == 'lastIsName' or mode == '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 = [] # type: 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 relies 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() # type: ignore - for i in range(len(names)): - nne = names[i] - template = self.templates[i] - if not first: - dest += nodes.Text('::') - prefix += '::' - if template: - dest += nodes.Text("template ") - 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_addname('::', '::') - signode += dest - if self.templates[-1]: - signode += nodes.Text("template ") - self.names[-1].describe_signature(signode, mode, env, '', symbol) - else: - raise Exception('Unknown description mode: %s' % mode) + raise NotImplementedError(repr(self)) -class ASTTrailingTypeSpecFundamental(ASTBase): +class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec): def __init__(self, name: str) -> None: self.name = name @@ -2116,40 +1722,12 @@ class ASTTrailingTypeSpecFundamental(ASTBase): 'parser should have rejected it.' % self.name) return _id_fundamental_v2[self.name] - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: signode += nodes.Text(str(self.name)) -class ASTTrailingTypeSpecName(ASTBase): - def __init__(self, prefix: str, nestedName: Any) -> None: - self.prefix = prefix - self.nestedName = nestedName - - @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)) - return ''.join(res) - - def describe_signature(self, signode: desc_signature, mode: str, - env: "BuildEnvironment", symbol: "Symbol") -> None: - if self.prefix: - signode += addnodes.desc_annotation(self.prefix, self.prefix) - signode += nodes.Text(' ') - self.nestedName.describe_signature(signode, mode, env, symbol=symbol) - - -class ASTTrailingTypeSpecDecltypeAuto(ASTBase): +class ASTTrailingTypeSpecDecltypeAuto(ASTTrailingTypeSpec): def _stringify(self, transform: StringifyTransform) -> str: return 'decltype(auto)' @@ -2158,13 +1736,13 @@ class ASTTrailingTypeSpecDecltypeAuto(ASTBase): raise NoOldIdError() return 'Dc' - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text(str(self))) -class ASTTrailingTypeSpecDecltype(ASTBase): - def __init__(self, expr): +class ASTTrailingTypeSpecDecltype(ASTTrailingTypeSpec): + def __init__(self, expr: ASTExpression): self.expr = expr def _stringify(self, transform: StringifyTransform) -> str: @@ -2175,15 +1753,45 @@ class ASTTrailingTypeSpecDecltype(ASTBase): raise NoOldIdError() return 'DT' + self.expr.get_id(version) + "E" - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: signode.append(nodes.Text('decltype(')) self.expr.describe_signature(signode, mode, env, symbol) signode.append(nodes.Text(')')) +class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): + def __init__(self, prefix: str, nestedName: ASTNestedName) -> None: + self.prefix = prefix + self.nestedName = nestedName + + @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)) + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + if self.prefix: + signode += addnodes.desc_annotation(self.prefix, self.prefix) + signode += nodes.Text(' ') + self.nestedName.describe_signature(signode, mode, env, symbol=symbol) + + class ASTFunctionParameter(ASTBase): - def __init__(self, arg: Any, ellipsis: bool = False) -> None: + def __init__(self, arg: Union["ASTTypeWithInit", + "ASTTemplateParamConstrainedTypeWithInit"], + ellipsis: bool = False) -> None: self.arg = arg self.ellipsis = ellipsis @@ -2204,9 +1812,9 @@ class ASTFunctionParameter(ASTBase): else: return transform(self.arg) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) if self.ellipsis: signode += nodes.Text('...') else: @@ -2214,7 +1822,8 @@ class ASTFunctionParameter(ASTBase): class ASTParametersQualifiers(ASTBase): - def __init__(self, args: List[Any], volatile: bool, const: bool, refQual: str, + def __init__(self, args: List[ASTFunctionParameter], + volatile: bool, const: bool, refQual: str, exceptionSpec: str, override: bool, final: bool, initializer: str) -> None: self.args = args self.volatile = volatile @@ -2226,7 +1835,7 @@ class ASTParametersQualifiers(ASTBase): self.initializer = initializer @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: return self.args def get_modifiers_id(self, version: int) -> str: @@ -2284,9 +1893,9 @@ class ASTParametersQualifiers(ASTBase): res.append(self.initializer) return ''.join(res) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) paramlist = addnodes.desc_parameterlist() for arg in self.args: param = addnodes.desc_parameter('', '', noemph=True) @@ -2297,11 +1906,11 @@ class ASTParametersQualifiers(ASTBase): paramlist += param signode += paramlist - def _add_anno(signode, text): + def _add_anno(signode: TextElement, text: str) -> None: signode += nodes.Text(' ') signode += addnodes.desc_annotation(text, text) - def _add_text(signode, text): + def _add_text(signode: TextElement, text: str) -> None: signode += nodes.Text(' ' + text) if self.volatile: @@ -2323,7 +1932,7 @@ class ASTParametersQualifiers(ASTBase): class ASTDeclSpecsSimple(ASTBase): def __init__(self, storage: str, threadLocal: bool, inline: bool, virtual: bool, explicit: bool, constexpr: bool, volatile: bool, const: bool, - friend: bool, attrs: List[Any]) -> None: + friend: bool, attrs: List[ASTAttribute]) -> None: self.storage = storage self.threadLocal = threadLocal self.inline = inline @@ -2372,38 +1981,44 @@ class ASTDeclSpecsSimple(ASTBase): res.append('const') return ' '.join(res) - def describe_signature(self, modifiers: List[Node]) -> None: - def _add(modifiers, text): - if len(modifiers) > 0: - modifiers.append(nodes.Text(' ')) - modifiers.append(addnodes.desc_annotation(text, text)) + def describe_signature(self, signode: TextElement) -> None: + addSpace = False for attr in self.attrs: - if len(modifiers) > 0: - modifiers.append(nodes.Text(' ')) - modifiers.append(attr.describe_signature(modifiers)) + if addSpace: + signode += nodes.Text(' ') + addSpace = True + attr.describe_signature(signode) + + def _add(signode: TextElement, text: str) -> bool: + if addSpace: + signode += nodes.Text(' ') + signode += addnodes.desc_annotation(text, text) + return True + if self.storage: - _add(modifiers, self.storage) + addSpace = _add(signode, self.storage) if self.threadLocal: - _add(modifiers, 'thread_local') + addSpace = _add(signode, 'thread_local') if self.inline: - _add(modifiers, 'inline') + addSpace = _add(signode, 'inline') if self.friend: - _add(modifiers, 'friend') + addSpace = _add(signode, 'friend') if self.virtual: - _add(modifiers, 'virtual') + addSpace = _add(signode, 'virtual') if self.explicit: - _add(modifiers, 'explicit') + addSpace = _add(signode, 'explicit') if self.constexpr: - _add(modifiers, 'constexpr') + addSpace = _add(signode, 'constexpr') if self.volatile: - _add(modifiers, 'volatile') + addSpace = _add(signode, 'volatile') if self.const: - _add(modifiers, 'const') + addSpace = _add(signode, 'const') class ASTDeclSpecs(ASTBase): - def __init__(self, outer: Any, leftSpecs: ASTDeclSpecsSimple, - rightSpecs: ASTDeclSpecsSimple, trailing: Any) -> None: + 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 @@ -2412,10 +2027,6 @@ class ASTDeclSpecs(ASTBase): self.allSpecs = self.leftSpecs.mergeWith(self.rightSpecs) self.trailingTypeSpec = trailing - @property - def name(self) -> ASTNestedName: - return self.trailingTypeSpec.name - def get_id(self, version: int) -> str: if version == 1: res = [] @@ -2450,35 +2061,29 @@ class ASTDeclSpecs(ASTBase): res.append(r) return "".join(res) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) - modifiers = [] # type: List[nodes.Node] - - def _add(modifiers, text): - if len(modifiers) > 0: - modifiers.append(nodes.Text(' ')) - modifiers.append(addnodes.desc_annotation(text, text)) + verify_description_mode(mode) + numChildren = len(signode) + self.leftSpecs.describe_signature(signode) + addSpace = len(signode) != numChildren - self.leftSpecs.describe_signature(modifiers) - - for m in modifiers: - signode += m if self.trailingTypeSpec: - if len(modifiers) > 0: + if addSpace: signode += nodes.Text(' ') self.trailingTypeSpec.describe_signature(signode, mode, env, symbol=symbol) - modifiers = [] - self.rightSpecs.describe_signature(modifiers) - if len(modifiers) > 0: + numChildren = len(signode) + self.rightSpecs.describe_signature(signode) + if len(signode) != numChildren: signode += nodes.Text(' ') - for m in modifiers: - signode += m +# Declarator +################################################################################ + class ASTArray(ASTBase): - def __init__(self, size): + def __init__(self, size: ASTExpression): self.size = size def _stringify(self, transform: StringifyTransform) -> str: @@ -2500,16 +2105,174 @@ class ASTArray(ASTBase): else: return 'A_' - def describe_signature(self, signode, mode, env, symbol): - _verify_description_mode(mode) + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + verify_description_mode(mode) signode.append(nodes.Text("[")) if self.size: self.size.describe_signature(signode, mode, env, symbol) signode.append(nodes.Text("]")) -class ASTDeclaratorPtr(ASTBase): - def __init__(self, next: Any, volatile: bool, const: bool, attrs: Any) -> None: +class ASTDeclarator(ASTBase): + @property + def name(self) -> ASTNestedName: + raise NotImplementedError(repr(self)) + + @property + def isPack(self) -> bool: + raise NotImplementedError(repr(self)) + + @property + def function_params(self) -> List[ASTFunctionParameter]: + 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 + + @property + def isPack(self) -> bool: + return False + + @property + def function_params(self) -> List[ASTFunctionParameter]: + return self.paramQual.function_params + + # 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 = [] + # TOOD: 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)) + for op in self.arrayOps: + res.append(transform(op)) + 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): + self.declId = declId + self.size = size + + @property + def name(self) -> ASTNestedName: + return self.declId + + 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.append(nodes.Text(' : ', ' : ')) + self.size.describe_signature(signode, mode, env, symbol) + + +class ASTDeclaratorPtr(ASTDeclarator): + def __init__(self, next: ASTDeclarator, volatile: bool, const: bool, + attrs: List[ASTAttribute]) -> None: assert next self.next = next self.volatile = volatile @@ -2521,12 +2284,11 @@ class ASTDeclaratorPtr(ASTBase): return self.next.name @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: return self.next.function_params def require_space_after_declSpecs(self) -> bool: - # TODO: if has paramPack, then False ? - return True + return self.next.require_space_after_declSpecs() def _stringify(self, transform: StringifyTransform) -> str: res = ['*'] @@ -2541,7 +2303,7 @@ class ASTDeclaratorPtr(ASTBase): res.append(' ') res.append('const') if self.const or self.volatile or len(self.attrs) > 0: - if self.next.require_space_after_declSpecs: + if self.next.require_space_after_declSpecs(): res.append(' ') res.append(transform(self.next)) return ''.join(res) @@ -2583,16 +2345,16 @@ class ASTDeclaratorPtr(ASTBase): def is_function_type(self) -> bool: return self.next.is_function_type() - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) signode += nodes.Text("*") for a in self.attrs: a.describe_signature(signode) if len(self.attrs) > 0 and (self.volatile or self.const): signode += nodes.Text(' ') - def _add_anno(signode, text): + def _add_anno(signode: TextElement, text: str) -> None: signode += addnodes.desc_annotation(text, text) if self.volatile: _add_anno(signode, 'volatile') @@ -2601,13 +2363,13 @@ class ASTDeclaratorPtr(ASTBase): signode += nodes.Text(' ') _add_anno(signode, 'const') if self.const or self.volatile or len(self.attrs) > 0: - if self.next.require_space_after_declSpecs: + if self.next.require_space_after_declSpecs(): signode += nodes.Text(' ') self.next.describe_signature(signode, mode, env, symbol) -class ASTDeclaratorRef(ASTBase): - def __init__(self, next: Any, attrs: Any) -> None: +class ASTDeclaratorRef(ASTDeclarator): + def __init__(self, next: ASTDeclarator, attrs: List[ASTAttribute]) -> None: assert next self.next = next self.attrs = attrs @@ -2621,7 +2383,7 @@ class ASTDeclaratorRef(ASTBase): return True @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: return self.next.function_params def require_space_after_declSpecs(self) -> bool: @@ -2631,7 +2393,7 @@ class ASTDeclaratorRef(ASTBase): res = ['&'] for a in self.attrs: res.append(transform(a)) - if len(self.attrs) > 0 and self.next.require_space_after_declSpecs: + if len(self.attrs) > 0 and self.next.require_space_after_declSpecs(): res.append(' ') res.append(transform(self.next)) return ''.join(res) @@ -2656,19 +2418,19 @@ class ASTDeclaratorRef(ASTBase): def is_function_type(self) -> bool: return self.next.is_function_type() - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) signode += nodes.Text("&") for a in self.attrs: a.describe_signature(signode) - if len(self.attrs) > 0 and self.next.require_space_after_declSpecs: + if len(self.attrs) > 0 and self.next.require_space_after_declSpecs(): signode += nodes.Text(' ') self.next.describe_signature(signode, mode, env, symbol) -class ASTDeclaratorParamPack(ASTBase): - def __init__(self, next: Any) -> None: +class ASTDeclaratorParamPack(ASTDeclarator): + def __init__(self, next: ASTDeclarator) -> None: assert next self.next = next @@ -2677,7 +2439,7 @@ class ASTDeclaratorParamPack(ASTBase): return self.next.name @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: return self.next.function_params def require_space_after_declSpecs(self) -> bool: @@ -2709,17 +2471,18 @@ class ASTDeclaratorParamPack(ASTBase): def is_function_type(self) -> bool: return self.next.is_function_type() - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) signode += nodes.Text("...") if self.next.name: signode += nodes.Text(' ') self.next.describe_signature(signode, mode, env, symbol) -class ASTDeclaratorMemPtr(ASTBase): - def __init__(self, className: Any, const: bool, volatile: bool, next: Any) -> None: +class ASTDeclaratorMemPtr(ASTDeclarator): + def __init__(self, className: ASTNestedName, + const: bool, volatile: bool, next: ASTDeclarator) -> None: assert className assert next self.className = className @@ -2732,7 +2495,7 @@ class ASTDeclaratorMemPtr(ASTBase): return self.next.name @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: return self.next.function_params def require_space_after_declSpecs(self) -> bool: @@ -2743,9 +2506,11 @@ class ASTDeclaratorMemPtr(ASTBase): res.append(transform(self.className)) res.append('::*') if self.volatile: - res.append(' volatile') + res.append('volatile') if self.const: - res.append(' const') + if self.volatile: + res.append(' ') + res.append('const') if self.next.require_space_after_declSpecs(): res.append(' ') res.append(transform(self.next)) @@ -2786,13 +2551,13 @@ class ASTDeclaratorMemPtr(ASTBase): def is_function_type(self) -> bool: return self.next.is_function_type() - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) self.className.describe_signature(signode, mode, env, symbol) signode += nodes.Text('::*') - def _add_anno(signode, text): + def _add_anno(signode: TextElement, text: str) -> None: signode += addnodes.desc_annotation(text, text) if self.volatile: _add_anno(signode, 'volatile') @@ -2801,13 +2566,12 @@ class ASTDeclaratorMemPtr(ASTBase): signode += nodes.Text(' ') _add_anno(signode, 'const') if self.next.require_space_after_declSpecs(): - if self.volatile or self.const: - signode += nodes.Text(' ') + signode += nodes.Text(' ') self.next.describe_signature(signode, mode, env, symbol) -class ASTDeclaratorParen(ASTBase): - def __init__(self, inner: Any, next: Any) -> None: +class ASTDeclaratorParen(ASTDeclarator): + def __init__(self, inner: ASTDeclarator, next: ASTDeclarator) -> None: assert inner assert next self.inner = inner @@ -2819,7 +2583,7 @@ class ASTDeclaratorParen(ASTBase): return self.inner.name @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: return self.inner.function_params def require_space_after_declSpecs(self) -> bool: @@ -2856,135 +2620,37 @@ class ASTDeclaratorParen(ASTBase): def is_function_type(self) -> bool: return self.inner.is_function_type() - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) signode += nodes.Text('(') self.inner.describe_signature(signode, mode, env, symbol) signode += nodes.Text(')') self.next.describe_signature(signode, "noneIsName", env, symbol) -class ASTDeclaratorNameParamQual(ASTBase): - def __init__(self, declId: Any, arrayOps: List[Any], paramQual: Any) -> None: - self.declId = declId - self.arrayOps = arrayOps - self.paramQual = paramQual - - @property - def name(self) -> ASTNestedName: - return self.declId - - @property - def isPack(self) -> bool: - return False - - @property - def function_params(self) -> Any: - return self.paramQual.function_params - - # 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 = [] - # TOOD: 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 +# Type and initializer stuff +############################################################################################## - def is_function_type(self) -> bool: - return self.paramQual is not None +class ASTPackExpansionExpr(ASTExpression): + def __init__(self, expr: ASTExpression): + self.expr = expr def _stringify(self, transform: StringifyTransform) -> str: - res = [] - if self.declId: - res.append(transform(self.declId)) - for op in self.arrayOps: - res.append(transform(op)) - if self.paramQual: - res.append(transform(self.paramQual)) - return ''.join(res) - - def describe_signature(self, signode: desc_signature, 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(ASTBase): - def __init__(self, declId, size): - self.declId = declId - self.size = size - - @property - def name(self) -> ASTNestedName: - return self.declId - - 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 + return transform(self.expr) + '...' - 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 get_id(self, version: int) -> str: + id = self.expr.get_id(version) + return 'sp' + id - def describe_signature(self, signode: desc_signature, mode: str, + 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.append(nodes.Text(' : ', ' : ')) - self.size.describe_signature(signode, mode, env, symbol) + self.expr.describe_signature(signode, mode, env, symbol) + signode += nodes.Text('...') class ASTParenExprList(ASTBase): - def __init__(self, exprs: List[Any]) -> None: + def __init__(self, exprs: List[ASTExpression]) -> None: self.exprs = exprs def get_id(self, version: int) -> str: @@ -2994,9 +2660,9 @@ class ASTParenExprList(ASTBase): exprs = [transform(e) for e in self.exprs] return '(%s)' % ', '.join(exprs) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) signode.append(nodes.Text('(')) first = True for e in self.exprs: @@ -3009,7 +2675,7 @@ class ASTParenExprList(ASTBase): class ASTBracedInitList(ASTBase): - def __init__(self, exprs: List[Any], trailingComma: bool) -> None: + def __init__(self, exprs: List[ASTExpression], trailingComma: bool) -> None: self.exprs = exprs self.trailingComma = trailingComma @@ -3021,9 +2687,9 @@ class ASTBracedInitList(ASTBase): trailingComma = ',' if self.trailingComma else '' return '{%s%s}' % (', '.join(exprs), trailingComma) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) signode.append(nodes.Text('{')) first = True for e in self.exprs: @@ -3038,7 +2704,8 @@ class ASTBracedInitList(ASTBase): class ASTInitializer(ASTBase): - def __init__(self, value: Any, hasAssign: bool = True) -> None: + def __init__(self, value: Union[ASTExpression, ASTBracedInitList], + hasAssign: bool = True) -> None: self.value = value self.hasAssign = hasAssign @@ -3049,16 +2716,16 @@ class ASTInitializer(ASTBase): else: return val - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) if self.hasAssign: signode.append(nodes.Text(' = ')) self.value.describe_signature(signode, 'markType', env, symbol) class ASTType(ASTBase): - def __init__(self, declSpecs: Any, decl: Any) -> None: + def __init__(self, declSpecs: ASTDeclSpecs, decl: ASTDeclarator) -> None: assert declSpecs assert decl self.declSpecs = declSpecs @@ -3073,7 +2740,7 @@ class ASTType(ASTBase): return self.decl.isPack @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: return self.decl.function_params def get_id(self, version: int, objectType: str = None, @@ -3144,9 +2811,9 @@ class ASTType(ASTBase): else: return 'type' - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + 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): @@ -3158,8 +2825,46 @@ class ASTType(ASTBase): 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, symbol: "Symbol" = 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 += nodes.Text(" = ") + self.init.describe_signature(signode, mode, env, symbol) + + class ASTTypeWithInit(ASTBase): - def __init__(self, type: Any, init: Any) -> None: + def __init__(self, type: ASTType, init: ASTInitializer) -> None: self.type = type self.init = init @@ -3187,16 +2892,16 @@ class ASTTypeWithInit(ASTBase): res.append(transform(self.init)) return ''.join(res) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + 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: Any, type: Any) -> None: + def __init__(self, name: ASTNestedName, type: ASTType) -> None: self.name = name self.type = type @@ -3217,17 +2922,20 @@ class ASTTypeUsing(ASTBase): def get_type_declaration_prefix(self) -> str: return 'using' - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) self.name.describe_signature(signode, mode, env, symbol=symbol) if self.type: signode += nodes.Text(' = ') self.type.describe_signature(signode, 'markType', env, symbol=symbol) +# Other declarations +############################################################################################## + class ASTConcept(ASTBase): - def __init__(self, nestedName: Any, initializer: Any) -> None: + def __init__(self, nestedName: ASTNestedName, initializer: ASTInitializer) -> None: self.nestedName = nestedName self.initializer = initializer @@ -3247,7 +2955,7 @@ class ASTConcept(ASTBase): res += transform(self.initializer) return res - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: self.nestedName.describe_signature(signode, mode, env, symbol) if self.initializer: @@ -3275,9 +2983,9 @@ class ASTBaseClass(ASTBase): res.append('...') return ''.join(res) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) if self.visibility is not None: signode += addnodes.desc_annotation(self.visibility, self.visibility) @@ -3314,9 +3022,9 @@ class ASTClass(ASTBase): res.append(transform(b)) return ''.join(res) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) self.name.describe_signature(signode, mode, env, symbol=symbol) if self.final: signode += nodes.Text(' ') @@ -3330,7 +3038,7 @@ class ASTClass(ASTBase): class ASTUnion(ASTBase): - def __init__(self, name: Any) -> None: + def __init__(self, name: ASTNestedName) -> None: self.name = name def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str: @@ -3341,14 +3049,15 @@ class ASTUnion(ASTBase): def _stringify(self, transform: StringifyTransform) -> str: return transform(self.name) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) self.name.describe_signature(signode, mode, env, symbol=symbol) class ASTEnum(ASTBase): - def __init__(self, name: Any, scoped: str, underlyingType: Any) -> None: + def __init__(self, name: ASTNestedName, scoped: str, + underlyingType: ASTType) -> None: self.name = name self.scoped = scoped self.underlyingType = underlyingType @@ -3369,9 +3078,9 @@ class ASTEnum(ASTBase): res.append(transform(self.underlyingType)) return ''.join(res) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) # self.scoped has been done by the CPPEnumObject self.name.describe_signature(signode, mode, env, symbol=symbol) if self.underlyingType: @@ -3381,7 +3090,7 @@ class ASTEnum(ASTBase): class ASTEnumerator(ASTBase): - def __init__(self, name: Any, init: Any) -> None: + def __init__(self, name: ASTNestedName, init: ASTInitializer) -> None: self.name = name self.init = init @@ -3397,14 +3106,398 @@ class ASTEnumerator(ASTBase): res.append(transform(self.init)) return ''.join(res) - def describe_signature(self, signode: desc_signature, mode: str, + def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", symbol: "Symbol") -> None: - _verify_description_mode(mode) + verify_description_mode(mode) self.name.describe_signature(signode, mode, env, symbol) 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)) + + +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 += nodes.Text(self.key) + if self.parameterPack: + if self.identifier: + signode += nodes.Text(' ') + signode += nodes.Text('...') + if self.identifier: + if not self.parameterPack: + signode += nodes.Text(' ') + self.identifier.describe_signature(signode, mode, env, '', '', symbol) + if self.default: + signode += nodes.Text(' = ') + 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, symbol: "Symbol" = 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, symbol: "Symbol" = 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 += nodes.Text(' ') + self.data.describe_signature(signode, mode, env, symbol) + + +class ASTTemplateParamNonType(ASTTemplateParam): + def __init__(self, + param: Union[ASTTypeWithInit, + ASTTemplateParamConstrainedTypeWithInit]) -> None: + assert param + self.param = param + + @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 + + 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, symbol: "Symbol" = 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.param.get_id(version) + + def _stringify(self, transform: StringifyTransform) -> str: + return transform(self.param) + + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + self.param.describe_signature(signode, mode, env, symbol) + + +class ASTTemplateParams(ASTBase): + def __init__(self, params: List[ASTTemplateParam]) -> None: + assert params is not None + self.params = params + + def get_id(self, version: int) -> str: + assert version >= 2 + res = [] + res.append("I") + for param in self.params: + res.append(param.get_id(version)) + res.append("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("> ") + return ''.join(res) + + def describe_signature(self, signode: TextElement, mode: str, + env: "BuildEnvironment", symbol: "Symbol") -> None: + signode += nodes.Text("template<") + first = True + for param in self.params: + if not first: + signode += nodes.Text(", ") + first = False + param.describe_signature(signode, mode, env, symbol) + signode += nodes.Text(">") + + 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 += nodes.Text("template<") + first = True + for param in self.params: + if not first: + lineNode += nodes.Text(", ") + first = False + if lineSpec: + lineNode = makeLine(parentNode) + param.describe_signature(lineNode, mode, env, symbol) + if lineSpec and not first: + lineNode = makeLine(parentNode) + lineNode += nodes.Text(">") + + +# 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, symbol: "Symbol" = 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 += nodes.Text('...') + 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 + # first do the same as a normal template parameter list + res = [] + res.append("I") + for param in self.params: + res.append(param.get_id(version)) + res.append("E") + # let's use X expr E, which is otherwise for constant template args + res.append("X") + res.append(self.concept.get_id(version)) + res.append("I") + for param in self.params: + res.append(param.get_id_as_arg(version)) + res.append("E") + res.append("E") + return ''.join(res) + + 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 += nodes.Text('{') + first = True + for param in self.params: + if not first: + signode += nodes.Text(', ') + first = False + param.describe_signature(signode, mode, env, symbol) + signode += nodes.Text('}') + + +class ASTTemplateDeclarationPrefix(ASTBase): + def __init__(self, + templates: List[Union[ASTTemplateParams, + ASTTemplateIntroduction]]) -> None: + # templates is None means it's an explicit instantiation of a variable + self.templates = templates + + def get_id(self, version: int) -> str: + assert version >= 2 + # this is not part of a normal name mangling system + res = [] + for t in self.templates: + res.append(t.get_id(version)) + return ''.join(res) + + def _stringify(self, transform: StringifyTransform) -> str: + res = [] + for t in self.templates: + res.append(transform(t)) + return ''.join(res) + + 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 ASTDeclaration(ASTBase): def __init__(self, objectType: str, directiveType: str, visibility: str, templatePrefix: ASTTemplateDeclarationPrefix, declaration: Any) -> None: @@ -3432,7 +3525,7 @@ class ASTDeclaration(ASTBase): return self.declaration.name @property - def function_params(self) -> Any: + def function_params(self) -> List[ASTFunctionParameter]: if self.objectType != 'function': return None return self.declaration.function_params @@ -3471,14 +3564,14 @@ class ASTDeclaration(ASTBase): def describe_signature(self, signode: desc_signature, mode: str, env: "BuildEnvironment", options: Dict) -> None: - _verify_description_mode(mode) + 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_cpp_tagname = 'declarator' + mainDeclNode.sphinx_line_type = 'declarator' mainDeclNode['add_permalink'] = not self.symbol.isRedeclaration if self.templatePrefix: @@ -3547,10 +3640,25 @@ class SymbolLookupResult: self.templateArgs = templateArgs +class LookupKey: + def __init__(self, data: List[Tuple[ASTNestedNameElement, + Union[ASTTemplateParams, + ASTTemplateIntroduction], + str]]) -> None: + self.data = data + + class Symbol: + debug_indent = 0 + debug_indent_string = " " debug_lookup = False debug_show_tree = False + @staticmethod + def debug_print(*args: Any) -> None: + print(Symbol.debug_indent_string * Symbol.debug_indent, end="") + print(*args) + def _assert_invariants(self) -> None: if not self.parent: # parent == None means global scope, so declaration means a parent @@ -3570,9 +3678,12 @@ class Symbol: return super().__setattr__(key, value) def __init__(self, parent: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator], - templateParams: Any, templateArgs: Any, declaration: ASTDeclaration, - docname: str) -> None: + templateParams: Union[ASTTemplateParams, ASTTemplateIntroduction], + templateArgs: Any, declaration: ASTDeclaration, docname: str) -> None: self.parent = parent + # declarations in a single directive are linked together + self.siblingAbove = None # type: Symbol + self.siblingBelow = None # type: Symbol self.identOrOp = identOrOp self.templateParams = templateParams # template<templateParams> self.templateArgs = templateArgs # identifier<templateArgs> @@ -3606,38 +3717,43 @@ class Symbol: # and symbol addition should be done as well self._add_template_and_function_params() - def _add_template_and_function_params(self): + def _add_template_and_function_params(self) -> None: + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("_add_template_and_function_params:") # Note: we may be called from _fill_empty, so the symbols we want # to add may actually already be present (as empty symbols). # add symbols for the template params if self.templateParams: - for p in self.templateParams.params: - if not p.get_identifier(): + for tp in self.templateParams.params: + if not tp.get_identifier(): continue # only add a declaration if we our self are from a declaration if self.declaration: - decl = ASTDeclaration('templateParam', None, None, None, p) + decl = ASTDeclaration('templateParam', None, None, None, tp) else: decl = None - nne = ASTNestedNameElement(p.get_identifier(), None) + nne = ASTNestedNameElement(tp.get_identifier(), None) nn = ASTNestedName([nne], [False], rooted=False) self._add_symbols(nn, [], decl, self.docname) # add symbols for function parameters, if any if self.declaration is not None and self.declaration.function_params is not None: - for p in self.declaration.function_params: - if p.arg is None: + for fp in self.declaration.function_params: + if fp.arg is None: continue - nn = p.arg.name + nn = fp.arg.name if nn is None: continue # (comparing to the template params: we have checked that we are a declaration) - decl = ASTDeclaration('functionParam', None, None, None, p) + decl = ASTDeclaration('functionParam', None, None, None, fp) assert not nn.rooted assert len(nn.names) == 1 self._add_symbols(nn, [], decl, self.docname) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 - def remove(self): + def remove(self) -> None: if self.parent is None: return assert self in self.parent._children @@ -3645,12 +3761,18 @@ class Symbol: self.parent = None def clear_doc(self, docname: str) -> None: - newChildren = [] + newChildren = [] # type: List[Symbol] for sChild in self._children: sChild.clear_doc(docname) if sChild.declaration and sChild.docname == docname: sChild.declaration = None sChild.docname = None + if sChild.siblingAbove is not None: + sChild.siblingAbove.siblingBelow = sChild.siblingBelow + if sChild.siblingBelow is not None: + sChild.siblingBelow.siblingAbove = sChild.siblingAbove + sChild.siblingAbove = None + sChild.siblingBelow = None newChildren.append(sChild) self._children = newChildren @@ -3669,7 +3791,11 @@ class Symbol: yield from c.children_recurse_anon - def get_lookup_key(self) -> List[Tuple[ASTNestedNameElement, Any]]: + def get_lookup_key(self) -> "LookupKey": + # The pickle files for the environment and for each document are distinct. + # The environment has all the symbols, but the documents has xrefs that + # must know their scope. A lookup key is essentially a specification of + # how to find a specific symbol. symbols = [] s = self while s.parent: @@ -3679,14 +3805,23 @@ class Symbol: key = [] for s in symbols: nne = ASTNestedNameElement(s.identOrOp, s.templateArgs) - key.append((nne, s.templateParams)) - return key + if s.declaration is not None: + key.append((nne, s.templateParams, s.declaration.get_newest_id())) + else: + key.append((nne, s.templateParams, None)) + return LookupKey(key) def get_full_nested_name(self) -> ASTNestedName: + symbols = [] + s = self + while s.parent: + symbols.append(s) + s = s.parent + symbols.reverse() names = [] templates = [] - for nne, templateParams in self.get_lookup_key(): - names.append(nne) + for s in symbols: + names.append(ASTNestedNameElement(s.identOrOp, s.templateArgs)) templates.append(False) return ASTNestedName(names, templates, rooted=False) @@ -3695,9 +3830,12 @@ class Symbol: templateShorthand: bool, matchSelf: bool, recurseInAnon: bool, correctPrimaryTemplateArgs: bool ) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_print("_find_first_named_symbol ->") res = self._find_named_symbols(identOrOp, templateParams, templateArgs, templateShorthand, matchSelf, recurseInAnon, - correctPrimaryTemplateArgs) + correctPrimaryTemplateArgs, + searchInSiblings=False) try: return next(res) except StopIteration: @@ -3706,10 +3844,24 @@ class Symbol: def _find_named_symbols(self, identOrOp: Union[ASTIdentifier, ASTOperator], templateParams: Any, templateArgs: ASTTemplateArgs, templateShorthand: bool, matchSelf: bool, - recurseInAnon: bool, correctPrimaryTemplateArgs: bool - ) -> Iterator["Symbol"]: - - def isSpecialization(): + recurseInAnon: bool, correctPrimaryTemplateArgs: bool, + searchInSiblings: bool) -> Iterator["Symbol"]: + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("_find_named_symbols:") + Symbol.debug_indent += 1 + Symbol.debug_print("self:") + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_print("identOrOp: ", identOrOp) + Symbol.debug_print("templateParams: ", templateParams) + Symbol.debug_print("templateArgs: ", templateArgs) + Symbol.debug_print("templateShorthand: ", templateShorthand) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("correctPrimaryTemplateAargs:", correctPrimaryTemplateArgs) + Symbol.debug_print("searchInSiblings: ", searchInSiblings) + + def isSpecialization() -> bool: # the names of the template parameters must be given exactly as args # and params that are packs must in the args be the name expanded if len(templateParams.params) != len(templateArgs.args): @@ -3759,20 +3911,64 @@ class Symbol: if str(s.templateArgs) != str(templateArgs): return False return True - if matchSelf and matches(self): - yield self - children = self.children_recurse_anon if recurseInAnon else self._children - for s in children: + + def candidates(): + s = self + if Symbol.debug_lookup: + Symbol.debug_print("searching in self:") + print(s.to_string(Symbol.debug_indent + 1), end="") + while True: + if matchSelf: + yield s + if recurseInAnon: + yield from s.children_recurse_anon + else: + yield from s._children + + if s.siblingAbove is None: + break + s = s.siblingAbove + if Symbol.debug_lookup: + Symbol.debug_print("searching in sibling:") + print(s.to_string(Symbol.debug_indent + 1), end="") + + for s in candidates(): + if Symbol.debug_lookup: + Symbol.debug_print("candidate:") + print(s.to_string(Symbol.debug_indent + 1), end="") if matches(s): + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("matches") + Symbol.debug_indent -= 3 yield s + if Symbol.debug_lookup: + Symbol.debug_indent += 2 + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 def _symbol_lookup(self, nestedName: ASTNestedName, templateDecls: List[Any], onMissingQualifiedSymbol: Callable[["Symbol", Union[ASTIdentifier, ASTOperator], Any, ASTTemplateArgs], "Symbol"], # NOQA strictTemplateParamArgLists: bool, ancestorLookupType: str, templateShorthand: bool, matchSelf: bool, - recurseInAnon: bool, correctPrimaryTemplateArgs: bool - ) -> SymbolLookupResult: + recurseInAnon: bool, correctPrimaryTemplateArgs: bool, + searchInSiblings: bool) -> SymbolLookupResult: # ancestorLookupType: if not None, specifies the target type of the lookup + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("_symbol_lookup:") + Symbol.debug_indent += 1 + Symbol.debug_print("self:") + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_print("nestedName: ", nestedName) + Symbol.debug_print("templateDecls: ", templateDecls) + Symbol.debug_print("strictTemplateParamArgLists:", strictTemplateParamArgLists) + Symbol.debug_print("ancestorLookupType:", ancestorLookupType) + Symbol.debug_print("templateShorthand: ", templateShorthand) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("correctPrimaryTemplateArgs: ", correctPrimaryTemplateArgs) + Symbol.debug_print("searchInSiblings: ", searchInSiblings) if strictTemplateParamArgLists: # Each template argument list must have a template parameter list. @@ -3796,7 +3992,8 @@ class Symbol: while parentSymbol.parent: if parentSymbol.find_identifier(firstName.identOrOp, matchSelf=matchSelf, - recurseInAnon=recurseInAnon): + recurseInAnon=recurseInAnon, + searchInSiblings=searchInSiblings): # if we are in the scope of a constructor but wants to # reference the class we need to walk one extra up if (len(names) == 1 and ancestorLookupType == 'class' and matchSelf and @@ -3807,6 +4004,10 @@ class Symbol: break parentSymbol = parentSymbol.parent + if Symbol.debug_lookup: + Symbol.debug_print("starting point:") + print(parentSymbol.to_string(Symbol.debug_indent + 1), end="") + # and now the actual lookup iTemplateDecl = 0 for name in names[:-1]: @@ -3840,6 +4041,8 @@ class Symbol: symbol = onMissingQualifiedSymbol(parentSymbol, identOrOp, templateParams, templateArgs) if symbol is None: + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return None # We have now matched part of a nested name, and need to match more # so even if we should matchSelf before, we definitely shouldn't @@ -3847,6 +4050,10 @@ class Symbol: matchSelf = False parentSymbol = symbol + if Symbol.debug_lookup: + Symbol.debug_print("handle last name from:") + print(parentSymbol.to_string(Symbol.debug_indent + 1), end="") + # handle the last name name = names[-1] identOrOp = name.identOrOp @@ -3861,7 +4068,11 @@ class Symbol: symbols = parentSymbol._find_named_symbols( identOrOp, templateParams, templateArgs, templateShorthand=templateShorthand, matchSelf=matchSelf, - recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False) + recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False, + searchInSiblings=searchInSiblings) + if Symbol.debug_lookup: + symbols = list(symbols) # type: ignore + Symbol.debug_indent -= 2 return SymbolLookupResult(symbols, parentSymbol, identOrOp, templateParams, templateArgs) @@ -3871,21 +4082,26 @@ class Symbol: # be an actual declaration. if Symbol.debug_lookup: - print("_add_symbols:") - print(" tdecls:", templateDecls) - print(" nn: ", nestedName) - print(" decl: ", declaration) - print(" doc: ", docname) + Symbol.debug_indent += 1 + Symbol.debug_print("_add_symbols:") + Symbol.debug_indent += 1 + Symbol.debug_print("tdecls:", templateDecls) + Symbol.debug_print("nn: ", nestedName) + Symbol.debug_print("decl: ", declaration) + Symbol.debug_print("doc: ", docname) def onMissingQualifiedSymbol(parentSymbol: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator], templateParams: Any, templateArgs: ASTTemplateArgs ) -> "Symbol": if Symbol.debug_lookup: - print(" _add_symbols, onMissingQualifiedSymbol:") - print(" templateParams:", templateParams) - print(" identOrOp: ", identOrOp) - print(" templateARgs: ", templateArgs) + Symbol.debug_indent += 1 + Symbol.debug_print("_add_symbols, onMissingQualifiedSymbol:") + Symbol.debug_indent += 1 + Symbol.debug_print("templateParams:", templateParams) + Symbol.debug_print("identOrOp: ", identOrOp) + Symbol.debug_print("templateARgs: ", templateArgs) + Symbol.debug_indent -= 2 return Symbol(parent=parentSymbol, identOrOp=identOrOp, templateParams=templateParams, templateArgs=templateArgs, declaration=None, @@ -3898,32 +4114,40 @@ class Symbol: templateShorthand=False, matchSelf=False, recurseInAnon=True, - correctPrimaryTemplateArgs=True) + correctPrimaryTemplateArgs=True, + searchInSiblings=False) assert lookupResult is not None # we create symbols all the way, so that can't happen symbols = list(lookupResult.symbols) if len(symbols) == 0: if Symbol.debug_lookup: - print(" _add_symbols, result, no symbol:") - print(" templateParams:", lookupResult.templateParams) - print(" identOrOp: ", lookupResult.identOrOp) - print(" templateArgs: ", lookupResult.templateArgs) - print(" declaration: ", declaration) - print(" docname: ", docname) + Symbol.debug_print("_add_symbols, result, no symbol:") + Symbol.debug_indent += 1 + Symbol.debug_print("templateParams:", lookupResult.templateParams) + Symbol.debug_print("identOrOp: ", lookupResult.identOrOp) + Symbol.debug_print("templateArgs: ", lookupResult.templateArgs) + Symbol.debug_print("declaration: ", declaration) + Symbol.debug_print("docname: ", docname) + Symbol.debug_indent -= 1 symbol = Symbol(parent=lookupResult.parentSymbol, identOrOp=lookupResult.identOrOp, templateParams=lookupResult.templateParams, templateArgs=lookupResult.templateArgs, declaration=declaration, docname=docname) + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return symbol if Symbol.debug_lookup: - print(" _add_symbols, result, symbols:") - print(" number symbols:", len(symbols)) + Symbol.debug_print("_add_symbols, result, symbols:") + Symbol.debug_indent += 1 + Symbol.debug_print("number symbols:", len(symbols)) + Symbol.debug_indent -= 1 if not declaration: if Symbol.debug_lookup: - print(" no delcaration") + Symbol.debug_print("no delcaration") + Symbol.debug_indent -= 2 # good, just a scope creation # TODO: what if we have more than one symbol? return symbols[0] @@ -3939,9 +4163,9 @@ class Symbol: else: withDecl.append(s) if Symbol.debug_lookup: - print(" #noDecl: ", len(noDecl)) - print(" #withDecl:", len(withDecl)) - print(" #dupDecl: ", len(dupDecl)) + Symbol.debug_print("#noDecl: ", len(noDecl)) + Symbol.debug_print("#withDecl:", len(withDecl)) + Symbol.debug_print("#dupDecl: ", len(dupDecl)) # With partial builds we may start with a large symbol tree stripped of declarations. # Essentially any combination of noDecl, withDecl, and dupDecls seems possible. # TODO: make partial builds fully work. What should happen when the primary symbol gets @@ -3952,7 +4176,7 @@ class Symbol: # otherwise there should be only one symbol with a declaration. def makeCandSymbol(): if Symbol.debug_lookup: - print(" begin: creating candidate symbol") + Symbol.debug_print("begin: creating candidate symbol") symbol = Symbol(parent=lookupResult.parentSymbol, identOrOp=lookupResult.identOrOp, templateParams=lookupResult.templateParams, @@ -3960,7 +4184,7 @@ class Symbol: declaration=declaration, docname=docname) if Symbol.debug_lookup: - print(" end: creating candidate symbol") + Symbol.debug_print("end: creating candidate symbol") return symbol if len(withDecl) == 0: candSymbol = None @@ -3969,7 +4193,10 @@ class Symbol: def handleDuplicateDeclaration(symbol, candSymbol): if Symbol.debug_lookup: - print(" redeclaration") + Symbol.debug_indent += 1 + Symbol.debug_print("redeclaration") + Symbol.debug_indent -= 1 + Symbol.debug_indent -= 2 # Redeclaration of the same symbol. # Let the new one be there, but raise an error to the client # so it can use the real symbol as subscope. @@ -3985,11 +4212,11 @@ class Symbol: # a function, so compare IDs candId = declaration.get_newest_id() if Symbol.debug_lookup: - print(" candId:", candId) + Symbol.debug_print("candId:", candId) for symbol in withDecl: oldId = symbol.declaration.get_newest_id() if Symbol.debug_lookup: - print(" oldId: ", oldId) + Symbol.debug_print("oldId: ", oldId) if candId == oldId: handleDuplicateDeclaration(symbol, candSymbol) # (not reachable) @@ -3997,14 +4224,16 @@ class Symbol: # if there is an empty symbol, fill that one if len(noDecl) == 0: if Symbol.debug_lookup: - print(" no match, no empty, candSybmol is not None?:", candSymbol is not None) # NOQA + Symbol.debug_print("no match, no empty, candSybmol is not None?:", candSymbol is not None) # NOQA + Symbol.debug_indent -= 2 if candSymbol is not None: return candSymbol else: return makeCandSymbol() else: if Symbol.debug_lookup: - print(" no match, but fill an empty declaration, candSybmol is not None?:", candSymbol is not None) # NOQA + Symbol.debug_print("no match, but fill an empty declaration, candSybmol is not None?:", candSymbol is not None) # NOQA + Symbol.debug_indent -= 2 if candSymbol is not None: candSymbol.remove() # assert len(noDecl) == 1 @@ -4021,6 +4250,9 @@ class Symbol: def merge_with(self, other: "Symbol", docnames: List[str], env: "BuildEnvironment") -> None: + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("merge_with:") assert other is not None for otherChild in other._children: ourChild = self._find_first_named_symbol( @@ -4050,17 +4282,28 @@ class Symbol: # just ignore it, right? pass ourChild.merge_with(otherChild, docnames, env) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 def add_name(self, nestedName: ASTNestedName, templatePrefix: ASTTemplateDeclarationPrefix = None) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("add_name:") if templatePrefix: templateDecls = templatePrefix.templates else: templateDecls = [] - return self._add_symbols(nestedName, templateDecls, - declaration=None, docname=None) + res = self._add_symbols(nestedName, templateDecls, + declaration=None, docname=None) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 + return res def add_declaration(self, declaration: ASTDeclaration, docname: str) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("add_declaration:") assert declaration assert docname nestedName = declaration.name @@ -4068,37 +4311,105 @@ class Symbol: templateDecls = declaration.templatePrefix.templates else: templateDecls = [] - return self._add_symbols(nestedName, templateDecls, declaration, docname) + res = self._add_symbols(nestedName, templateDecls, declaration, docname) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 + return res def find_identifier(self, identOrOp: Union[ASTIdentifier, ASTOperator], - matchSelf: bool, recurseInAnon: bool) -> "Symbol": - if matchSelf and self.identOrOp == identOrOp: - return self - children = self.children_recurse_anon if recurseInAnon else self._children - for s in children: - if s.identOrOp == identOrOp: - return s + matchSelf: bool, recurseInAnon: bool, searchInSiblings: bool + ) -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("find_identifier:") + Symbol.debug_indent += 1 + Symbol.debug_print("identOrOp: ", identOrOp) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("searchInSiblings:", searchInSiblings) + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_indent -= 2 + current = self + while current is not None: + if Symbol.debug_lookup: + Symbol.debug_indent += 2 + Symbol.debug_print("trying:") + print(current.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_indent -= 2 + if matchSelf and current.identOrOp == identOrOp: + return current + children = current.children_recurse_anon if recurseInAnon else current._children + for s in children: + if s.identOrOp == identOrOp: + return s + if not searchInSiblings: + break + current = current.siblingAbove return None - def direct_lookup(self, key: List[Tuple[ASTNestedNameElement, Any]]) -> "Symbol": + def direct_lookup(self, key: "LookupKey") -> "Symbol": + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("direct_lookup:") + Symbol.debug_indent += 1 s = self - for name, templateParams in key: - identOrOp = name.identOrOp - templateArgs = name.templateArgs - s = s._find_first_named_symbol(identOrOp, - templateParams, templateArgs, - templateShorthand=False, - matchSelf=False, - recurseInAnon=False, - correctPrimaryTemplateArgs=False) - if not s: + for name, templateParams, id_ in key.data: + if id_ is not None: + res = None + for cand in s._children: + if cand.declaration is None: + continue + if cand.declaration.get_newest_id() == id_: + res = cand + break + s = res + else: + identOrOp = name.identOrOp + templateArgs = name.templateArgs + s = s._find_first_named_symbol(identOrOp, + templateParams, templateArgs, + templateShorthand=False, + matchSelf=False, + recurseInAnon=False, + correctPrimaryTemplateArgs=False) + if Symbol.debug_lookup: + Symbol.debug_print("name: ", name) + Symbol.debug_print("templateParams:", templateParams) + Symbol.debug_print("id: ", id_) + if s is not None: + print(s.to_string(Symbol.debug_indent + 1), end="") + else: + Symbol.debug_print("not found") + if s is None: + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return None + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 return s def find_name(self, nestedName: ASTNestedName, templateDecls: List[Any], typ: str, templateShorthand: bool, matchSelf: bool, - recurseInAnon: bool) -> List["Symbol"]: + recurseInAnon: bool, searchInSiblings: bool) -> Tuple[List["Symbol"], str]: # templateShorthand: missing template parameter lists for templates is ok + # If the first component is None, + # then the second component _may_ be a string explaining why. + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("find_name:") + Symbol.debug_indent += 1 + Symbol.debug_print("self:") + print(self.to_string(Symbol.debug_indent + 1), end="") + Symbol.debug_print("nestedName: ", nestedName) + Symbol.debug_print("templateDecls: ", templateDecls) + Symbol.debug_print("typ: ", typ) + Symbol.debug_print("templateShorthand:", templateShorthand) + Symbol.debug_print("matchSelf: ", matchSelf) + Symbol.debug_print("recurseInAnon: ", recurseInAnon) + Symbol.debug_print("searchInSiblings: ", searchInSiblings) + + class QualifiedSymbolIsTemplateParam(Exception): + pass def onMissingQualifiedSymbol(parentSymbol: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator], @@ -4108,37 +4419,58 @@ class Symbol: # Though, the correctPrimaryTemplateArgs does # that for primary templates. # Is there another case where it would be good? + if parentSymbol.declaration is not None: + if parentSymbol.declaration.objectType == 'templateParam': + raise QualifiedSymbolIsTemplateParam() return None - lookupResult = self._symbol_lookup(nestedName, templateDecls, - onMissingQualifiedSymbol, - strictTemplateParamArgLists=False, - ancestorLookupType=typ, - templateShorthand=templateShorthand, - matchSelf=matchSelf, - recurseInAnon=recurseInAnon, - correctPrimaryTemplateArgs=False) + try: + lookupResult = self._symbol_lookup(nestedName, templateDecls, + onMissingQualifiedSymbol, + strictTemplateParamArgLists=False, + ancestorLookupType=typ, + templateShorthand=templateShorthand, + matchSelf=matchSelf, + recurseInAnon=recurseInAnon, + correctPrimaryTemplateArgs=False, + searchInSiblings=searchInSiblings) + except QualifiedSymbolIsTemplateParam: + return None, "templateParamInQualified" + if lookupResult is None: # if it was a part of the qualification that could not be found - return None + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 + return None, None res = list(lookupResult.symbols) if len(res) != 0: - return res + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 + return res, None + + if lookupResult.parentSymbol.declaration is not None: + if lookupResult.parentSymbol.declaration.objectType == 'templateParam': + return None, "templateParamInQualified" # try without template params and args symbol = lookupResult.parentSymbol._find_first_named_symbol( lookupResult.identOrOp, None, None, templateShorthand=templateShorthand, matchSelf=matchSelf, recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False) + if Symbol.debug_lookup: + Symbol.debug_indent -= 2 if symbol is not None: - return [symbol] + return [symbol], None else: - return None + return None, None def find_declaration(self, declaration: ASTDeclaration, typ: str, templateShorthand: bool, matchSelf: bool, recurseInAnon: bool) -> "Symbol": # templateShorthand: missing template parameter lists for templates is ok + if Symbol.debug_lookup: + Symbol.debug_indent += 1 + Symbol.debug_print("find_declaration:") nestedName = declaration.name if declaration.templatePrefix: templateDecls = declaration.templatePrefix.templates @@ -4158,8 +4490,10 @@ class Symbol: templateShorthand=templateShorthand, matchSelf=matchSelf, recurseInAnon=recurseInAnon, - correctPrimaryTemplateArgs=False) - + correctPrimaryTemplateArgs=False, + searchInSiblings=False) + if Symbol.debug_lookup: + Symbol.debug_indent -= 1 if lookupResult is None: return None @@ -4185,14 +4519,14 @@ class Symbol: return None def to_string(self, indent: int) -> str: - res = ['\t' * indent] + res = [Symbol.debug_indent_string * indent] if not self.parent: res.append('::') else: if self.templateParams: res.append(str(self.templateParams)) res.append('\n') - res.append('\t' * indent) + res.append(Symbol.debug_indent_string * indent) if self.identOrOp: res.append(str(self.identOrOp)) else: @@ -4218,7 +4552,7 @@ class Symbol: return ''.join(res) -class DefinitionParser: +class DefinitionParser(BaseParser): # those without signedness and size modifiers # see https://en.cppreference.com/w/cpp/language/types _simple_fundemental_types = ( @@ -4228,130 +4562,13 @@ class DefinitionParser: _prefix_keys = ('class', 'struct', 'enum', 'union', 'typename') - def __init__(self, definition: Any, warnEnv: Any, config: "Config") -> None: - self.definition = definition.strip() - self.pos = 0 - self.end = len(self.definition) - self.last_match = None # type: Match - self._previous_state = (0, None) # type: Tuple[int, Match] - self.otherErrors = [] # type: List[DefinitionError] - # in our tests the following is set to False to capture bad parsing - self.allowFallbackExpressionParsing = True - - self.warnEnv = warnEnv + def __init__(self, definition: str, *, + location: Union[nodes.Node, Tuple[str, int]], + config: "Config") -> None: + super().__init__(definition, location=location) self.config = config - def _make_multi_error(self, errors: List[Any], header: str) -> DefinitionError: - if len(errors) == 1: - if len(header) > 0: - return DefinitionError(header + '\n' + str(errors[0][0])) - else: - return DefinitionError(str(errors[0][0])) - result = [header, '\n'] - for e in errors: - if len(e[1]) > 0: - ident = ' ' - result.append(e[1]) - result.append(':\n') - for line in str(e[0]).split('\n'): - if len(line) == 0: - continue - result.append(ident) - result.append(line) - result.append('\n') - else: - result.append(str(e[0])) - return DefinitionError(''.join(result)) - - def status(self, msg: str) -> None: - # for debugging - indicator = '-' * self.pos + '^' - print("%s\n%s\n%s" % (msg, self.definition, indicator)) - - def fail(self, msg: str) -> None: - errors = [] - indicator = '-' * self.pos + '^' - exMain = DefinitionError( - 'Invalid definition: %s [error at %d]\n %s\n %s' % - (msg, self.pos, self.definition, indicator)) - errors.append((exMain, "Main error")) - for err in self.otherErrors: - errors.append((err, "Potential other error")) - self.otherErrors = [] - raise self._make_multi_error(errors, '') - - def warn(self, msg: str) -> None: - if self.warnEnv: - self.warnEnv.warn(msg) - else: - print("Warning: %s" % msg) - - def match(self, regex: Pattern) -> bool: - match = regex.match(self.definition, self.pos) - if match is not None: - self._previous_state = (self.pos, self.last_match) - self.pos = match.end() - self.last_match = match - return True - return False - - def backout(self) -> None: - self.pos, self.last_match = self._previous_state - - def skip_string(self, string: str) -> bool: - strlen = len(string) - if self.definition[self.pos:self.pos + strlen] == string: - self.pos += strlen - return True - return False - - def skip_word(self, word: str) -> bool: - return self.match(re.compile(r'\b%s\b' % re.escape(word))) - - def skip_ws(self) -> bool: - return self.match(_whitespace_re) - - def skip_word_and_ws(self, word: str) -> bool: - if self.skip_word(word): - self.skip_ws() - return True - return False - - def skip_string_and_ws(self, string: str) -> bool: - if self.skip_string(string): - self.skip_ws() - return True - return False - - @property - def eof(self) -> bool: - return self.pos >= self.end - - @property - def current_char(self) -> str: - try: - return self.definition[self.pos] - except IndexError: - return 'EOF' - - @property - def matched_text(self) -> str: - if self.last_match is not None: - return self.last_match.group() - else: - return None - - def read_rest(self) -> str: - rv = self.definition[self.pos:] - self.pos = self.end - return rv - - def assert_end(self) -> None: - self.skip_ws() - if not self.eof: - self.fail('Expected end of definition.') - - def _parse_string(self): + def _parse_string(self) -> str: if self.current_char != '"': return None startPos = self.pos @@ -4390,7 +4607,7 @@ class DefinitionParser: % startPos) return self.definition[startPos:self.pos] - def _parse_attribute(self) -> Any: + def _parse_attribute(self) -> ASTAttribute: self.skip_ws() # try C++11 style startPos = self.pos @@ -4414,7 +4631,7 @@ class DefinitionParser: self.fail("Expected '(' after '__attribute__('.") attrs = [] while 1: - if self.match(_identifier_re): + if self.match(identifier_re): name = self.matched_text self.skip_ws() if self.skip_string_and_ws('('): @@ -4449,7 +4666,7 @@ class DefinitionParser: return None - def _parse_literal(self): + def _parse_literal(self) -> ASTLiteral: # -> integer-literal # | character-literal # | floating-literal @@ -4464,8 +4681,8 @@ class DefinitionParser: return ASTBooleanLiteral(True) if self.skip_word('false'): return ASTBooleanLiteral(False) - for regex in [_float_literal_re, _binary_literal_re, _hex_literal_re, - _integer_literal_re, _octal_literal_re]: + for regex in [float_literal_re, binary_literal_re, hex_literal_re, + integer_literal_re, octal_literal_re]: pos = self.pos if self.match(regex): while self.current_char in 'uUlLfF': @@ -4477,7 +4694,7 @@ class DefinitionParser: return ASTStringLiteral(string) # character-literal - if self.match(_char_literal_re): + if self.match(char_literal_re): prefix = self.last_match.group(1) # may be None when no prefix data = self.last_match.group(2) try: @@ -4491,7 +4708,7 @@ class DefinitionParser: # TODO: user-defined lit return None - def _parse_fold_or_paren_expression(self): + def _parse_fold_or_paren_expression(self) -> ASTExpression: # "(" expression ")" # fold-expression # -> ( cast-expression fold-operator ... ) @@ -4550,7 +4767,7 @@ class DefinitionParser: self.fail("Expected ')' to end binary fold expression.") return ASTFoldExpr(leftExpr, op, rightExpr) - def _parse_primary_expression(self): + def _parse_primary_expression(self) -> ASTExpression: # literal # "this" # lambda-expression @@ -4558,7 +4775,7 @@ class DefinitionParser: # fold-expression # id-expression -> we parse this with _parse_nested_name self.skip_ws() - res = self._parse_literal() + res = self._parse_literal() # type: ASTExpression if res is not None: return res self.skip_ws() @@ -4568,10 +4785,13 @@ class DefinitionParser: res = self._parse_fold_or_paren_expression() if res is not None: return res - return self._parse_nested_name() + nn = self._parse_nested_name() + if nn is not None: + return ASTIdExpression(nn) + return None def _parse_initializer_list(self, name: str, open: str, close: str - ) -> Tuple[List[Any], bool]: + ) -> Tuple[List[ASTExpression], bool]: # Parse open and close with the actual initializer-list inbetween # -> initializer-clause '...'[opt] # | initializer-list ',' initializer-clause '...'[opt] @@ -4581,7 +4801,7 @@ class DefinitionParser: if self.skip_string(close): return [], False - exprs = [] + exprs = [] # type: List[ASTExpression] trailingComma = False while True: self.skip_ws() @@ -4631,7 +4851,7 @@ class DefinitionParser: return paren return self._parse_braced_init_list() - def _parse_postfix_expression(self): + def _parse_postfix_expression(self) -> ASTPostfixExpr: # -> primary # | postfix "[" expression "]" # | postfix "[" braced-init-list [opt] "]" @@ -4737,7 +4957,7 @@ class DefinitionParser: raise self._make_multi_error(errors, header) # and now parse postfixes - postFixes = [] # type: List[Any] + postFixes = [] # type: List[ASTPostfixOp] while True: self.skip_ws() if prefixType in ['expr', 'cast', 'typeid']: @@ -4778,12 +4998,9 @@ class DefinitionParser: postFixes.append(ASTPostfixCallExpr(lst)) continue break - if len(postFixes) == 0: - return prefix - else: - return ASTPostfixExpr(prefix, postFixes) + return ASTPostfixExpr(prefix, postFixes) - def _parse_unary_expression(self): + def _parse_unary_expression(self) -> ASTExpression: # -> postfix # | "++" cast # | "--" cast @@ -4806,7 +5023,7 @@ class DefinitionParser: if self.skip_string_and_ws('...'): if not self.skip_string_and_ws('('): self.fail("Expecting '(' after 'sizeof...'.") - if not self.match(_identifier_re): + if not self.match(identifier_re): self.fail("Expecting identifier for 'sizeof...'.") ident = ASTIdentifier(self.matched_text) self.skip_ws() @@ -4874,7 +5091,7 @@ class DefinitionParser: return ASTDeleteExpr(rooted, array, expr) return self._parse_postfix_expression() - def _parse_cast_expression(self): + def _parse_cast_expression(self) -> ASTExpression: # -> unary | "(" type-id ")" cast pos = self.pos self.skip_ws() @@ -4897,7 +5114,7 @@ class DefinitionParser: else: return self._parse_unary_expression() - def _parse_logical_or_expression(self, inTemplate): + def _parse_logical_or_expression(self, inTemplate: bool) -> ASTExpression: # logical-or = logical-and || # logical-and = inclusive-or && # inclusive-or = exclusive-or | @@ -4909,12 +5126,13 @@ class DefinitionParser: # additive = multiplicative +, - # multiplicative = pm *, /, % # pm = cast .*, ->* - def _parse_bin_op_expr(self, opId, inTemplate): + def _parse_bin_op_expr(self: DefinitionParser, + opId: int, inTemplate: bool) -> ASTExpression: if opId + 1 == len(_expression_bin_ops): - def parser(inTemplate): + def parser(inTemplate: bool) -> ASTExpression: return self._parse_cast_expression() else: - def parser(inTemplate): + def parser(inTemplate: bool) -> ASTExpression: return _parse_bin_op_expr(self, opId + 1, inTemplate=inTemplate) exprs = [] ops = [] @@ -4950,7 +5168,7 @@ class DefinitionParser: # -> "?" expression ":" assignment-expression return None - def _parse_assignment_expression(self, inTemplate): + def _parse_assignment_expression(self, inTemplate: bool) -> ASTExpression: # -> conditional-expression # | logical-or-expression assignment-operator initializer-clause # | throw-expression @@ -4982,19 +5200,21 @@ class DefinitionParser: else: return ASTAssignmentExpr(exprs, ops) - def _parse_constant_expression(self, inTemplate): + def _parse_constant_expression(self, inTemplate: bool) -> ASTExpression: # -> conditional-expression orExpr = self._parse_logical_or_expression(inTemplate=inTemplate) # TODO: use _parse_conditional_expression_tail return orExpr - def _parse_expression(self, inTemplate): + def _parse_expression(self, inTemplate: bool) -> ASTExpression: # -> assignment-expression # | expression "," assignment-expresion # TODO: actually parse the second production return self._parse_assignment_expression(inTemplate=inTemplate) - def _parse_expression_fallback(self, end, parser, allow=True): + def _parse_expression_fallback(self, end: List[str], + parser: Callable[[], ASTExpression], + allow: bool = True) -> ASTExpression: # Stupidly "parse" an expression. # 'end' should be a list of characters which ends the expression. @@ -5034,10 +5254,12 @@ class DefinitionParser: value = self.definition[startPos:self.pos].strip() return ASTFallbackExpr(value.strip()) + # ========================================================================== + def _parse_operator(self) -> ASTOperator: self.skip_ws() # adapted from the old code - # thank god, a regular operator definition + # yay, a regular operator definition if self.match(_operator_re): return ASTOperatorBuildIn(self.matched_text) @@ -5056,7 +5278,7 @@ class DefinitionParser: # user-defined literal? if self.skip_string('""'): self.skip_ws() - if not self.match(_identifier_re): + if not self.match(identifier_re): self.fail("Expected user-defined literal suffix.") identifier = ASTIdentifier(self.matched_text) return ASTOperatorLiteral(identifier) @@ -5073,7 +5295,7 @@ class DefinitionParser: if self.skip_string('>'): return ASTTemplateArgs([]) prevErrors = [] - templateArgs = [] # type: List + templateArgs = [] # type: List[Union[ASTType, ASTTemplateArgConstant]] while 1: pos = self.pos parsedComma = False @@ -5092,9 +5314,13 @@ class DefinitionParser: prevErrors.append((e, "If type argument")) self.pos = pos try: + # actually here we shouldn't use the fallback parser (hence allow=False), + # because if actually took the < in an expression, then we _will_ fail, + # which is handled elsewhere. E.g., :cpp:expr:`A <= 0`. def parser(): return self._parse_constant_expression(inTemplate=True) - value = self._parse_expression_fallback([',', '>'], parser) + value = self._parse_expression_fallback( + [',', '>'], parser, allow=False) self.skip_ws() if self.skip_string('>'): parsedEnd = True @@ -5114,7 +5340,7 @@ class DefinitionParser: return ASTTemplateArgs(templateArgs) def _parse_nested_name(self, memberPointer: bool = False) -> ASTNestedName: - names = [] # type: List[Any] + names = [] # type: List[ASTNestedNameElement] templates = [] # type: List[bool] self.skip_ws() @@ -5132,7 +5358,7 @@ class DefinitionParser: if self.skip_word_and_ws('operator'): identOrOp = self._parse_operator() else: - if not self.match(_identifier_re): + if not self.match(identifier_re): if memberPointer and len(names) > 0: templates.pop() break @@ -5161,7 +5387,9 @@ class DefinitionParser: break return ASTNestedName(names, templates, rooted) - def _parse_trailing_type_spec(self) -> Any: + # ========================================================================== + + def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec: # fundemental types self.skip_ws() for t in self._simple_fundemental_types: @@ -5420,7 +5648,7 @@ class DefinitionParser: self.pos = pos declId = None elif named == 'single': - if self.match(_identifier_re): + if self.match(identifier_re): identifier = ASTIdentifier(self.matched_text) nne = ASTNestedNameElement(identifier, None) declId = ASTNestedName([nne], [False], rooted=False) @@ -5464,7 +5692,8 @@ class DefinitionParser: paramQual=paramQual) def _parse_declarator(self, named: Union[bool, str], paramMode: str, - typed: bool = True) -> Any: + typed: bool = True + ) -> ASTDeclarator: # 'typed' here means 'parse return type stuff' if paramMode not in ('type', 'function', 'operatorCast', 'new'): raise Exception( @@ -5706,7 +5935,9 @@ class DefinitionParser: decl = self._parse_declarator(named=named, paramMode=paramMode) return ASTType(declSpecs, decl) - def _parse_type_with_init(self, named: Union[bool, str], outer: str) -> Any: + def _parse_type_with_init( + self, named: Union[bool, str], + outer: str) -> Union[ASTTypeWithInit, ASTTemplateParamConstrainedTypeWithInit]: if outer: assert outer in ('type', 'member', 'function', 'templateParam') type = self._parse_type(outer=outer, named=named) @@ -5820,10 +6051,12 @@ class DefinitionParser: init = ASTInitializer(initVal) return ASTEnumerator(name, init) - def _parse_template_parameter_list(self) -> "ASTTemplateParams": + # ========================================================================== + + def _parse_template_parameter_list(self) -> ASTTemplateParams: # only: '<' parameter-list '>' # we assume that 'template' has just been parsed - templateParams = [] # type: List + templateParams = [] # type: List[ASTTemplateParam] self.skip_ws() if not self.skip_string("<"): self.fail("Expected '<' after 'template'") @@ -5833,7 +6066,6 @@ class DefinitionParser: if self.skip_word('template'): # declare a tenplate template parameter nestedParams = self._parse_template_parameter_list() - nestedParams.isNested = True else: nestedParams = None self.skip_ws() @@ -5850,7 +6082,7 @@ class DefinitionParser: self.skip_ws() parameterPack = self.skip_string('...') self.skip_ws() - if self.match(_identifier_re): + if self.match(identifier_re): identifier = ASTIdentifier(self.matched_text) else: identifier = None @@ -5863,11 +6095,11 @@ class DefinitionParser: parameterPack, default) if nestedParams: # template type - param = ASTTemplateParamTemplateType(nestedParams, data) # type: Any + templateParams.append( + ASTTemplateParamTemplateType(nestedParams, data)) else: # type - param = ASTTemplateParamType(data) - templateParams.append(param) + templateParams.append(ASTTemplateParamType(data)) else: # declare a non-type parameter, or constrained type parameter pos = self.pos @@ -5891,7 +6123,7 @@ class DefinitionParser: prevErrors.append((e, "")) raise self._make_multi_error(prevErrors, header) - def _parse_template_introduction(self) -> "ASTTemplateIntroduction": + def _parse_template_introduction(self) -> ASTTemplateIntroduction: pos = self.pos try: concept = self._parse_nested_name() @@ -5909,7 +6141,7 @@ class DefinitionParser: self.skip_ws() parameterPack = self.skip_string('...') self.skip_ws() - if not self.match(_identifier_re): + if not self.match(identifier_re): self.fail("Expected identifier in template introduction list.") txt_identifier = self.matched_text # make sure there isn't a keyword @@ -5931,14 +6163,15 @@ class DefinitionParser: def _parse_template_declaration_prefix(self, objectType: str ) -> ASTTemplateDeclarationPrefix: - templates = [] # type: List[str] + templates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] while 1: self.skip_ws() # the saved position is only used to provide a better error message + params = None # type: Union[ASTTemplateParams, ASTTemplateIntroduction] pos = self.pos if self.skip_word("template"): try: - params = self._parse_template_parameter_list() # type: Any + params = self._parse_template_parameter_list() except DefinitionError as e: if objectType == 'member' and len(templates) == 0: return ASTTemplateDeclarationPrefix(None) @@ -5990,7 +6223,7 @@ class DefinitionParser: msg += str(nestedName) self.warn(msg) - newTemplates = [] + newTemplates = [] # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]] for i in range(numExtra): newTemplates.append(ASTTemplateParams([])) if templatePrefix and not isMemberInstantiation: @@ -6067,7 +6300,7 @@ class DefinitionParser: res.objectType = 'namespace' # type: ignore return res - def parse_xref_object(self) -> Tuple[Any, bool]: + def parse_xref_object(self) -> Tuple[Union[ASTNamespace, ASTDeclaration], bool]: pos = self.pos try: templatePrefix = self._parse_template_declaration_prefix(objectType="xref") @@ -6097,25 +6330,26 @@ class DefinitionParser: msg = "Error in cross-reference." raise self._make_multi_error(errs, msg) - def parse_expression(self): + def parse_expression(self) -> Union[ASTExpression, ASTType]: pos = self.pos try: expr = self._parse_expression(False) self.skip_ws() self.assert_end() + return expr except DefinitionError as exExpr: self.pos = pos try: - expr = self._parse_type(False) + typ = self._parse_type(False) self.skip_ws() self.assert_end() + return typ except DefinitionError as exType: header = "Error when parsing (type) expression." errs = [] errs.append((exExpr, "If expression")) errs.append((exType, "If type")) raise self._make_multi_error(errs, header) - return expr def _make_phony_error_name() -> ASTNestedName: @@ -6143,9 +6377,6 @@ class CPPObject(ObjectDescription): option_spec = dict(ObjectDescription.option_spec) option_spec['tparam-line-spec'] = directives.flag - def warn(self, msg: Union[str, Exception]) -> None: - self.state_machine.reporter.warning(msg, line=self.lineno) - def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None: assert ast.objectType == 'enumerator' # find the parent, if it exists && is an enum @@ -6176,7 +6407,8 @@ class CPPObject(ObjectDescription): return targetSymbol = parentSymbol.parent - s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True) + s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True, + searchInSiblings=False) if s is not None: # something is already declared with that name return @@ -6187,8 +6419,8 @@ class CPPObject(ObjectDescription): declaration=declClone, docname=self.env.docname) - def add_target_and_index(self, ast: ASTDeclaration, sig: str, signode: desc_signature - ) -> None: + def add_target_and_index(self, ast: ASTDeclaration, sig: str, + signode: TextElement) -> None: # general note: name must be lstrip(':')'ed, to remove "::" ids = [] for i in range(1, _max_id + 1): @@ -6202,8 +6434,9 @@ class CPPObject(ObjectDescription): newestId = ids[0] assert newestId # shouldn't be None if not re.compile(r'^[a-zA-Z0-9_]*$').match(newestId): - self.warn('Index id generation for C++ object "%s" failed, please ' - 'report as bug (id=%s).' % (ast, newestId)) + logger.warning('Index id generation for C++ object "%s" failed, please ' + 'report as bug (id=%s).', ast, newestId, + location=self.get_source_info()) name = ast.symbol.get_full_nested_name().get_display_string().lstrip(':') # Add index entry, but not if it's a declaration inside a concept @@ -6231,10 +6464,6 @@ class CPPObject(ObjectDescription): names = self.env.domaindata['cpp']['names'] if name not in names: names[name] = ast.symbol.docname - signode['names'].append(name) - else: - # print("[CPP] non-unique name:", name) - pass # always add the newest id assert newestId signode['ids'].append(newestId) @@ -6244,7 +6473,6 @@ class CPPObject(ObjectDescription): continue if id not in self.state.document.ids: signode['ids'].append(id) - signode['first'] = (not self.names) # hmm, what is this about? self.state.document.note_explicit_target(signode) @property @@ -6261,10 +6489,11 @@ class CPPObject(ObjectDescription): def parse_definition(self, parser: DefinitionParser) -> ASTDeclaration: return parser.parse_declaration(self.object_type, self.objtype) - def describe_signature(self, signode: desc_signature, ast: Any, options: Dict) -> None: + def describe_signature(self, signode: desc_signature, + ast: ASTDeclaration, options: Dict) -> None: ast.describe_signature(signode, 'lastIsName', self.env, options) - def run(self): + def run(self) -> List[Node]: env = self.state.document.settings.env # from ObjectDescription.run if 'cpp:parent_symbol' not in env.temp_data: root = env.domaindata['cpp']['root_symbol'] @@ -6285,23 +6514,29 @@ class CPPObject(ObjectDescription): parentSymbol = env.temp_data['cpp:parent_symbol'] parentDecl = parentSymbol.declaration if parentDecl is not None and parentDecl.objectType == 'function': - self.warn("C++ declarations inside functions are not supported." + - " Parent function is " + str(parentSymbol.get_full_nested_name())) + logger.warning("C++ declarations inside functions are not supported." + + " Parent function is " + + str(parentSymbol.get_full_nested_name()), + location=self.get_source_info()) name = _make_phony_error_name() symbol = parentSymbol.add_name(name) env.temp_data['cpp:last_symbol'] = symbol return [] + # When multiple declarations are made in the same directive + # they need to know about each other to provide symbol lookup for function parameters. + # We use last_symbol to store the latest added declaration in a directive. + env.temp_data['cpp:last_symbol'] = None return super().run() def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration: parentSymbol = self.env.temp_data['cpp:parent_symbol'] - parser = DefinitionParser(sig, self, self.env.config) + parser = DefinitionParser(sig, location=signode, config=self.env.config) try: ast = self.parse_definition(parser) parser.assert_end() except DefinitionError as e: - self.warn(e) + logger.warning(e, location=signode) # It is easier to assume some phony name than handling the error in # the possibly inner declarations. name = _make_phony_error_name() @@ -6311,12 +6546,19 @@ class CPPObject(ObjectDescription): try: symbol = parentSymbol.add_declaration(ast, docname=self.env.docname) + # append the new declaration to the sibling list + assert symbol.siblingAbove is None + assert symbol.siblingBelow is None + symbol.siblingAbove = self.env.temp_data['cpp:last_symbol'] + if symbol.siblingAbove is not None: + assert symbol.siblingAbove.siblingBelow is None + symbol.siblingAbove.siblingBelow = symbol self.env.temp_data['cpp:last_symbol'] = symbol except _DuplicateSymbolError as e: # Assume we are actually in the old symbol, # instead of the newly created duplicate. self.env.temp_data['cpp:last_symbol'] = e.symbol - self.warn("Duplicate declaration, %s" % sig) + logger.warning("Duplicate declaration, %s", sig, location=signode) if ast.objectType == 'enumerator': self._add_enumerator_to_parent(ast) @@ -6329,10 +6571,10 @@ class CPPObject(ObjectDescription): return ast def before_content(self) -> None: - lastSymbol = self.env.temp_data['cpp:last_symbol'] + lastSymbol = self.env.temp_data['cpp:last_symbol'] # type: Symbol assert lastSymbol self.oldParentSymbol = self.env.temp_data['cpp:parent_symbol'] - self.oldParentKey = self.env.ref_context['cpp:parent_key'] + self.oldParentKey = self.env.ref_context['cpp:parent_key'] # type: LookupKey self.env.temp_data['cpp:parent_symbol'] = lastSymbol self.env.ref_context['cpp:parent_key'] = lastSymbol.get_lookup_key() @@ -6361,7 +6603,7 @@ class CPPClassObject(CPPObject): object_type = 'class' @property - def display_object_type(self): + def display_object_type(self) -> str: # the distinction between class and struct is only cosmetic assert self.objtype in ('class', 'struct') return self.objtype @@ -6391,21 +6633,20 @@ class CPPNamespaceObject(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def warn(self, msg: Union[str, Exception]) -> None: - self.state_machine.reporter.warning(msg, line=self.lineno) - def run(self) -> List[Node]: rootSymbol = self.env.domaindata['cpp']['root_symbol'] if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): symbol = rootSymbol stack = [] # type: List[Symbol] else: - parser = DefinitionParser(self.arguments[0], self, self.config) + parser = DefinitionParser(self.arguments[0], + location=self.get_source_info(), + config=self.config) try: ast = parser.parse_namespace_object() parser.assert_end() except DefinitionError as e: - self.warn(e) + logger.warning(e, location=self.get_source_info()) name = _make_phony_error_name() ast = ASTNamespace(name, None) symbol = rootSymbol.add_name(ast.nestedName, ast.templatePrefix) @@ -6423,18 +6664,17 @@ class CPPNamespacePushObject(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def warn(self, msg: Union[str, Exception]) -> None: - self.state_machine.reporter.warning(msg, line=self.lineno) - def run(self) -> List[Node]: if self.arguments[0].strip() in ('NULL', '0', 'nullptr'): return [] - parser = DefinitionParser(self.arguments[0], self, self.config) + parser = DefinitionParser(self.arguments[0], + location=self.get_source_info(), + config=self.config) try: ast = parser.parse_namespace_object() parser.assert_end() except DefinitionError as e: - self.warn(e) + logger.warning(e, location=self.get_source_info()) name = _make_phony_error_name() ast = ASTNamespace(name, None) oldParent = self.env.temp_data.get('cpp:parent_symbol', None) @@ -6456,13 +6696,11 @@ class CPPNamespacePopObject(SphinxDirective): final_argument_whitespace = True option_spec = {} # type: Dict - def warn(self, msg: Union[str, Exception]) -> None: - self.state_machine.reporter.warning(msg, line=self.lineno) - def run(self) -> List[Node]: stack = self.env.temp_data.get('cpp:namespace_stack', None) if not stack or len(stack) == 0: - self.warn("C++ namespace pop on empty stack. Defaulting to gobal scope.") + logger.warning("C++ namespace pop on empty stack. Defaulting to gobal scope.", + location=self.get_source_info()) stack = [] else: stack.pop() @@ -6477,7 +6715,8 @@ class CPPNamespacePopObject(SphinxDirective): class AliasNode(nodes.Element): - def __init__(self, sig, env=None, parentKey=None): + def __init__(self, sig: str, env: "BuildEnvironment" = None, + parentKey: LookupKey = None) -> None: super().__init__() self.sig = sig if env is not None: @@ -6489,8 +6728,8 @@ class AliasNode(nodes.Element): assert parentKey is not None self.parentKey = parentKey - def copy(self): - return self.__class__(self.sig, env=None, parentKey=self.parentKey) + def copy(self: T) -> T: + return self.__class__(self.sig, env=None, parentKey=self.parentKey) # type: ignore class AliasTransform(SphinxTransform): @@ -6498,31 +6737,27 @@ class AliasTransform(SphinxTransform): def apply(self, **kwargs: Any) -> None: for node in self.document.traverse(AliasNode): - class Warner: - def warn(self, msg): - logger.warning(msg, location=node) - warner = Warner() sig = node.sig parentKey = node.parentKey try: - parser = DefinitionParser(sig, warner, self.env.config) + parser = DefinitionParser(sig, location=node, + config=self.env.config) ast, isShorthand = parser.parse_xref_object() parser.assert_end() except DefinitionError as e: - warner.warn(e) + logger.warning(e, location=node) ast, isShorthand = None, None if ast is None: # could not be parsed, so stop here signode = addnodes.desc_signature(sig, '') - signode['first'] = False signode.clear() signode += addnodes.desc_name(sig, sig) node.replace_self(signode) continue - rootSymbol = self.env.domains['cpp'].data['root_symbol'] - parentSymbol = rootSymbol.direct_lookup(parentKey) + rootSymbol = self.env.domains['cpp'].data['root_symbol'] # type: Symbol + parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol if not parentSymbol: print("Target: ", sig) print("ParentKey: ", parentKey) @@ -6531,19 +6766,25 @@ class AliasTransform(SphinxTransform): symbols = [] # type: List[Symbol] if isShorthand: - ns = ast # type: ASTNamespace + assert isinstance(ast, ASTNamespace) + ns = ast name = ns.nestedName if ns.templatePrefix: templateDecls = ns.templatePrefix.templates else: templateDecls = [] - symbols = parentSymbol.find_name(name, templateDecls, 'any', - templateShorthand=True, - matchSelf=True, recurseInAnon=True) + symbols, failReason = parentSymbol.find_name( + nestedName=name, + templateDecls=templateDecls, + typ='any', + templateShorthand=True, + matchSelf=True, recurseInAnon=True, + searchInSiblings=False) if symbols is None: symbols = [] else: - decl = ast # type: ASTDeclaration + assert isinstance(ast, ASTDeclaration) + decl = ast name = decl.name s = parentSymbol.find_declaration(decl, 'any', templateShorthand=True, @@ -6555,7 +6796,6 @@ class AliasTransform(SphinxTransform): if len(symbols) == 0: signode = addnodes.desc_signature(sig, '') - signode['first'] = False node.append(signode) signode.clear() signode += addnodes.desc_name(sig, sig) @@ -6569,7 +6809,6 @@ class AliasTransform(SphinxTransform): options['tparam-line-spec'] = False for s in symbols: signode = addnodes.desc_signature(sig, '') - signode['first'] = False nodes.append(signode) s.declaration.describe_signature(signode, 'markName', self.env, options) node.replace_self(nodes) @@ -6618,7 +6857,7 @@ class CPPXRefRole(XRefRole): if not has_explicit_title: # major hax: replace anon names via simple string manipulation. # Can this actually fail? - title = _anon_identifier_re.sub("[anonymous]", str(title)) + title = anon_identifier_re.sub("[anonymous]", str(title)) if refnode['reftype'] == 'any': # Assume the removal part of fix_parens for :any: refs. @@ -6640,8 +6879,9 @@ class CPPXRefRole(XRefRole): return title, target -class CPPExprRole: - def __init__(self, asCode): +class CPPExprRole(SphinxRole): + def __init__(self, asCode: bool) -> None: + super().__init__() if asCode: # render the expression as inline code self.class_type = 'cpp-expr' @@ -6651,28 +6891,27 @@ class CPPExprRole: self.class_type = 'cpp-texpr' self.node_type = nodes.inline - def __call__(self, typ, rawtext, text, lineno, inliner, options={}, content=[]): - class Warner: - def warn(self, msg): - inliner.reporter.warning(msg, line=lineno) - text = utils.unescape(text).replace('\n', ' ') - env = inliner.document.settings.env - parser = DefinitionParser(text, Warner(), env.config) + def run(self) -> Tuple[List[Node], List[system_message]]: + text = self.text.replace('\n', ' ') + parser = DefinitionParser(text, + location=self.get_source_info(), + config=self.config) # attempt to mimic XRefRole classes, except that... classes = ['xref', 'cpp', self.class_type] try: ast = parser.parse_expression() except DefinitionError as ex: - Warner().warn('Unparseable C++ expression: %r\n%s' % (text, ex)) + logger.warning('Unparseable C++ expression: %r\n%s', text, ex, + location=self.get_source_info()) # see below return [self.node_type(text, text, classes=classes)], [] - parentSymbol = env.temp_data.get('cpp:parent_symbol', None) + parentSymbol = self.env.temp_data.get('cpp:parent_symbol', None) if parentSymbol is None: - parentSymbol = env.domaindata['cpp']['root_symbol'] + parentSymbol = self.env.domaindata['cpp']['root_symbol'] # ...most if not all of these classes should really apply to the individual references, # not the container node signode = self.node_type(classes=classes) - ast.describe_signature(signode, 'markType', env, parentSymbol) + ast.describe_signature(signode, 'markType', self.env, parentSymbol) return [signode], [] @@ -6796,25 +7035,24 @@ class CPPDomain(Domain): ourNames[name] = docname def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder, - typ: str, target: str, node: pending_xref, contnode: Element, - emitWarnings: bool = True) -> Tuple[Element, str]: - class Warner: - def warn(self, msg): - if emitWarnings: - logger.warning(msg, location=node) - warner = Warner() + typ: str, target: str, node: pending_xref, + contnode: Element) -> Tuple[Element, str]: # add parens again for those that could be functions if typ == 'any' or typ == 'func': target += '()' - parser = DefinitionParser(target, warner, env.config) + parser = DefinitionParser(target, location=node, + config=env.config) try: ast, isShorthand = parser.parse_xref_object() except DefinitionError as e: - def findWarning(e): # as arg to stop flake8 from complaining + # as arg to stop flake8 from complaining + def findWarning(e: Exception) -> Tuple[str, Exception]: if typ != 'any' and typ != 'func': return target, e # hax on top of the paren hax to try to get correct errors - parser2 = DefinitionParser(target[:-2], warner, env.config) + parser2 = DefinitionParser(target[:-2], + location=node, + config=env.config) try: parser2.parse_xref_object() except DefinitionError as e2: @@ -6822,34 +7060,49 @@ class CPPDomain(Domain): # strange, that we don't get the error now, use the original return target, e t, ex = findWarning(e) - warner.warn('Unparseable C++ cross-reference: %r\n%s' % (t, ex)) + logger.warning('Unparseable C++ cross-reference: %r\n%s', t, ex, + location=node) return None, None - parentKey = node.get("cpp:parent_key", None) + parentKey = node.get("cpp:parent_key", None) # type: LookupKey rootSymbol = self.data['root_symbol'] if parentKey: - parentSymbol = rootSymbol.direct_lookup(parentKey) + parentSymbol = rootSymbol.direct_lookup(parentKey) # type: Symbol if not parentSymbol: print("Target: ", target) - print("ParentKey: ", parentKey) + print("ParentKey: ", parentKey.data) print(rootSymbol.dump(1)) assert parentSymbol # should be there else: parentSymbol = rootSymbol if isShorthand: - ns = ast # type: ASTNamespace + assert isinstance(ast, ASTNamespace) + ns = ast name = ns.nestedName if ns.templatePrefix: templateDecls = ns.templatePrefix.templates else: templateDecls = [] - symbols = parentSymbol.find_name(name, templateDecls, typ, - templateShorthand=True, - matchSelf=True, recurseInAnon=True) - # just refer to the arbitrarily first symbol - s = None if symbols is None else symbols[0] + # let's be conservative with the sibling lookup for now + searchInSiblings = (not name.rooted) and len(name.names) == 1 + symbols, failReason = parentSymbol.find_name( + name, templateDecls, typ, + templateShorthand=True, + matchSelf=True, recurseInAnon=True, + searchInSiblings=searchInSiblings) + if symbols is None: + if typ == 'identifier': + if failReason == 'templateParamInQualified': + # this is an xref we created as part of a signature, + # so don't warn for names nested in template parameters + raise NoUri(str(name), typ) + s = None + else: + # just refer to the arbitrarily first symbol + s = symbols[0] else: - decl = ast # type: ASTDeclaration + assert isinstance(ast, ASTDeclaration) + decl = ast name = decl.name s = parentSymbol.find_declaration(decl, typ, templateShorthand=True, @@ -6869,7 +7122,7 @@ class CPPDomain(Domain): typ = 'class' declTyp = s.declaration.objectType - def checkType(): + def checkType() -> bool: if typ == 'any' or typ == 'identifier': return True if declTyp == 'templateParam': @@ -6884,9 +7137,10 @@ class CPPDomain(Domain): print("Type is %s (originally: %s), declType is %s" % (typ, origTyp, declTyp)) assert False if not checkType(): - warner.warn("cpp:%s targets a %s (%s)." - % (origTyp, s.declaration.objectType, - s.get_full_nested_name())) + logger.warning("cpp:%s targets a %s (%s).", + origTyp, s.declaration.objectType, + s.get_full_nested_name(), + location=node) declaration = s.declaration if isShorthand: @@ -6949,9 +7203,9 @@ class CPPDomain(Domain): def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder, target: str, node: pending_xref, contnode: Element ) -> List[Tuple[str, Element]]: - retnode, objtype = self._resolve_xref_inner(env, fromdocname, builder, - 'any', target, node, contnode, - emitWarnings=False) + with logging.suppress_logging(): + retnode, objtype = self._resolve_xref_inner(env, fromdocname, builder, + 'any', target, node, contnode) if retnode: if objtype == 'templateParam': return [('cpp:templateParam', retnode)] @@ -6977,8 +7231,8 @@ class CPPDomain(Domain): target = node.get('reftarget', None) if target is None: return None - parentKey = node.get("cpp:parent_key", None) - if parentKey is None or len(parentKey) <= 0: + parentKey = node.get("cpp:parent_key", None) # type: LookupKey + if parentKey is None or len(parentKey.data) <= 0: return None rootSymbol = self.data['root_symbol'] @@ -6996,7 +7250,7 @@ def setup(app: Sphinx) -> Dict[str, Any]: return { 'version': 'builtin', - 'env_version': 1, + 'env_version': 2, 'parallel_read_safe': True, 'parallel_write_safe': True, } |