1"""
2    sphinx.domains.cpp
3    ~~~~~~~~~~~~~~~~~~
4
5    The C++ language domain.
6
7    :copyright: Copyright 2007-2021 by the Sphinx team, see AUTHORS.
8    :license: BSD, see LICENSE for details.
9"""
10
11import re
12from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Type,
13                    TypeVar, Union, cast)
14
15from docutils import nodes
16from docutils.nodes import Element, Node, TextElement, system_message
17from docutils.parsers.rst import directives
18
19from sphinx import addnodes
20from sphinx.addnodes import desc_signature, pending_xref
21from sphinx.application import Sphinx
22from sphinx.builders import Builder
23from sphinx.directives import ObjectDescription
24from sphinx.domains import Domain, ObjType
25from sphinx.environment import BuildEnvironment
26from sphinx.errors import NoUri
27from sphinx.locale import _, __
28from sphinx.roles import SphinxRole, XRefRole
29from sphinx.transforms import SphinxTransform
30from sphinx.transforms.post_transforms import ReferencesResolver
31from sphinx.util import logging
32from sphinx.util.cfamily import (ASTAttribute, ASTBaseBase, ASTBaseParenExprList, BaseParser,
33                                 DefinitionError, NoOldIdError, StringifyTransform,
34                                 UnsupportedMultiCharacterCharLiteral, anon_identifier_re,
35                                 binary_literal_re, char_literal_re, float_literal_re,
36                                 float_literal_suffix_re, hex_literal_re, identifier_re,
37                                 integer_literal_re, integers_literal_suffix_re,
38                                 octal_literal_re, verify_description_mode)
39from sphinx.util.docfields import Field, GroupedField
40from sphinx.util.docutils import SphinxDirective
41from sphinx.util.nodes import make_refnode
42
43logger = logging.getLogger(__name__)
44T = TypeVar('T')
45
46"""
47    Important note on ids
48    ----------------------------------------------------------------------------
49
50    Multiple id generation schemes are used due to backwards compatibility.
51    - v1: 1.2.3 <= version < 1.3
52          The style used before the rewrite.
53          It is not the actual old code, but a replication of the behaviour.
54    - v2: 1.3 <= version < now
55          Standardised mangling scheme from
56          https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
57          though not completely implemented.
58    All versions are generated and attached to elements. The newest is used for
59    the index. All of the versions should work as permalinks.
60
61
62    Signature Nodes and Tagnames
63    ----------------------------------------------------------------------------
64
65    Each signature is in a desc_signature node, where all children are
66    desc_signature_line nodes. Each of these lines will have the attribute
67    'sphinx_line_type' set to one of the following (prioritized):
68    - 'declarator', if the line contains the name of the declared object.
69    - 'templateParams', if the line starts a template parameter list,
70    - 'templateParams', if the line has template parameters
71      Note: such lines might get a new tag in the future.
72    - 'templateIntroduction, if the line is on the form 'conceptName{...}'
73    No other desc_signature nodes should exist (so far).
74
75
76    Grammar
77    ----------------------------------------------------------------------------
78
79    See https://www.nongnu.org/hcb/ for the grammar,
80    and https://github.com/cplusplus/draft/blob/master/source/grammar.tex,
81    and https://github.com/cplusplus/concepts-ts
82    for the newest grammar.
83
84    common grammar things:
85        template-declaration ->
86            "template" "<" template-parameter-list ">" declaration
87        template-parameter-list ->
88              template-parameter
89            | template-parameter-list "," template-parameter
90        template-parameter ->
91              type-parameter
92            | parameter-declaration # i.e., same as a function argument
93
94        type-parameter ->
95              "class"    "..."[opt] identifier[opt]
96            | "class"               identifier[opt] "=" type-id
97            | "typename" "..."[opt] identifier[opt]
98            | "typename"            identifier[opt] "=" type-id
99            | "template" "<" template-parameter-list ">"
100                "class"  "..."[opt] identifier[opt]
101            | "template" "<" template-parameter-list ">"
102                "class"             identifier[opt] "=" id-expression
103            # also, from C++17 we can have "typename" in template templates
104        templateDeclPrefix ->
105            "template" "<" template-parameter-list ">"
106
107        simple-declaration ->
108            attribute-specifier-seq[opt] decl-specifier-seq[opt]
109                init-declarator-list[opt] ;
110        # Make the semicolon optional.
111        # For now: drop the attributes (TODO).
112        # Use at most 1 init-declarator.
113        -> decl-specifier-seq init-declarator
114        -> decl-specifier-seq declarator initializer
115
116        decl-specifier ->
117              storage-class-specifier ->
118                 (  "static" (only for member_object and function_object)
119                  | "extern" (only for member_object and function_object)
120                  | "register"
121                 )
122                 thread_local[opt] (only for member_object)
123                                   (it can also appear before the others)
124
125            | type-specifier -> trailing-type-specifier
126            | function-specifier -> "inline" | "virtual" | "explicit" (only
127              for function_object)
128            | "friend" (only for function_object)
129            | "constexpr" (only for member_object and function_object)
130        trailing-type-specifier ->
131              simple-type-specifier
132            | elaborated-type-specifier
133            | typename-specifier
134            | cv-qualifier -> "const" | "volatile"
135        stricter grammar for decl-specifier-seq (with everything, each object
136        uses a subset):
137            visibility storage-class-specifier function-specifier "friend"
138            "constexpr" "volatile" "const" trailing-type-specifier
139            # where trailing-type-specifier can no be cv-qualifier
140        # Inside e.g., template parameters a strict subset is used
141        # (see type-specifier-seq)
142        trailing-type-specifier ->
143              simple-type-specifier ->
144                ::[opt] nested-name-specifier[opt] type-name
145              | ::[opt] nested-name-specifier "template" simple-template-id
146              | "char" | "bool" | ect.
147              | decltype-specifier
148            | elaborated-type-specifier ->
149                class-key attribute-specifier-seq[opt] ::[opt]
150                nested-name-specifier[opt] identifier
151              | class-key ::[opt] nested-name-specifier[opt] template[opt]
152                simple-template-id
153              | "enum" ::[opt] nested-name-specifier[opt] identifier
154            | typename-specifier ->
155                "typename" ::[opt] nested-name-specifier identifier
156              | "typename" ::[opt] nested-name-specifier template[opt]
157                simple-template-id
158        class-key -> "class" | "struct" | "union"
159        type-name ->* identifier | simple-template-id
160        # ignoring attributes and decltype, and then some left-factoring
161        trailing-type-specifier ->
162            rest-of-trailing
163            ("class" | "struct" | "union" | "typename") rest-of-trailing
164            build-in -> "char" | "bool" | ect.
165            decltype-specifier
166        rest-of-trailing -> (with some simplification)
167            "::"[opt] list-of-elements-separated-by-::
168        element ->
169            "template"[opt] identifier ("<" template-argument-list ">")[opt]
170        template-argument-list ->
171              template-argument "..."[opt]
172            | template-argument-list "," template-argument "..."[opt]
173        template-argument ->
174              constant-expression
175            | type-specifier-seq abstract-declarator
176            | id-expression
177
178
179        declarator ->
180              ptr-declarator
181            | noptr-declarator parameters-and-qualifiers trailing-return-type
182              (TODO: for now we don't support trailing-eturn-type)
183        ptr-declarator ->
184              noptr-declarator
185            | ptr-operator ptr-declarator
186        noptr-declarator ->
187              declarator-id attribute-specifier-seq[opt] ->
188                    "..."[opt] id-expression
189                  | rest-of-trailing
190            | noptr-declarator parameters-and-qualifiers
191            | noptr-declarator "[" constant-expression[opt] "]"
192              attribute-specifier-seq[opt]
193            | "(" ptr-declarator ")"
194        ptr-operator ->
195              "*"  attribute-specifier-seq[opt] cv-qualifier-seq[opt]
196            | "&   attribute-specifier-seq[opt]
197            | "&&" attribute-specifier-seq[opt]
198            | "::"[opt] nested-name-specifier "*" attribute-specifier-seq[opt]
199                cv-qualifier-seq[opt]
200        # function_object must use a parameters-and-qualifiers, the others may
201        # use it (e.g., function poitners)
202        parameters-and-qualifiers ->
203            "(" parameter-clause ")" attribute-specifier-seq[opt]
204            cv-qualifier-seq[opt] ref-qualifier[opt]
205            exception-specification[opt]
206        ref-qualifier -> "&" | "&&"
207        exception-specification ->
208            "noexcept" ("(" constant-expression ")")[opt]
209            "throw" ("(" type-id-list ")")[opt]
210        # TODO: we don't implement attributes
211        # member functions can have initializers, but we fold them into here
212        memberFunctionInit -> "=" "0"
213        # (note: only "0" is allowed as the value, according to the standard,
214        # right?)
215
216        enum-head ->
217            enum-key attribute-specifier-seq[opt] nested-name-specifier[opt]
218                identifier enum-base[opt]
219        enum-key -> "enum" | "enum struct" | "enum class"
220        enum-base ->
221            ":" type
222        enumerator-definition ->
223              identifier
224            | identifier "=" constant-expression
225
226    We additionally add the possibility for specifying the visibility as the
227    first thing.
228
229    concept_object:
230        goal:
231            just a declaration of the name (for now)
232
233        grammar: only a single template parameter list, and the nested name
234            may not have any template argument lists
235
236            "template" "<" template-parameter-list ">"
237            nested-name-specifier
238
239    type_object:
240        goal:
241            either a single type (e.g., "MyClass:Something_T" or a typedef-like
242            thing (e.g. "Something Something_T" or "int I_arr[]"
243        grammar, single type: based on a type in a function parameter, but
244        without a name:
245               parameter-declaration
246            -> attribute-specifier-seq[opt] decl-specifier-seq
247               abstract-declarator[opt]
248            # Drop the attributes
249            -> decl-specifier-seq abstract-declarator[opt]
250        grammar, typedef-like: no initilizer
251            decl-specifier-seq declarator
252        Can start with a templateDeclPrefix.
253
254    member_object:
255        goal: as a type_object which must have a declarator, and optionally
256        with a initializer
257        grammar:
258            decl-specifier-seq declarator initializer
259        Can start with a templateDeclPrefix.
260
261    function_object:
262        goal: a function declaration, TODO: what about templates? for now: skip
263        grammar: no initializer
264           decl-specifier-seq declarator
265        Can start with a templateDeclPrefix.
266
267    class_object:
268        goal: a class declaration, but with specification of a base class
269        grammar:
270              nested-name "final"[opt] (":" base-specifier-list)[opt]
271            base-specifier-list ->
272              base-specifier "..."[opt]
273            | base-specifier-list, base-specifier "..."[opt]
274            base-specifier ->
275              base-type-specifier
276            | "virtual" access-spe"cifier[opt]    base-type-specifier
277            | access-specifier[opt] "virtual"[opt] base-type-specifier
278        Can start with a templateDeclPrefix.
279
280    enum_object:
281        goal: an unscoped enum or a scoped enum, optionally with the underlying
282              type specified
283        grammar:
284            ("class" | "struct")[opt] visibility[opt] nested-name (":" type)[opt]
285    enumerator_object:
286        goal: an element in a scoped or unscoped enum. The name should be
287              injected according to the scopedness.
288        grammar:
289            nested-name ("=" constant-expression)
290
291    namespace_object:
292        goal: a directive to put all following declarations in a specific scope
293        grammar:
294            nested-name
295"""
296
297udl_identifier_re = re.compile(r'''(?x)
298    [a-zA-Z_][a-zA-Z0-9_]*\b   # note, no word boundary in the beginning
299''')
300_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
301                        r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
302_visibility_re = re.compile(r'\b(public|private|protected)\b')
303_operator_re = re.compile(r'''(?x)
304        \[\s*\]
305    |   \(\s*\)
306    |   \+\+ | --
307    |   ->\*? | \,
308    |   (<<|>>)=? | && | \|\|
309    |   [!<>=/*%+|&^~-]=?
310    |   (\b(and|and_eq|bitand|bitor|compl|not|not_eq|or|or_eq|xor|xor_eq)\b)
311''')
312_fold_operator_re = re.compile(r'''(?x)
313        ->\*    |    \.\*    |    \,
314    |   (<<|>>)=?    |    &&    |    \|\|
315    |   !=
316    |   [<>=/*%+|&^~-]=?
317''')
318# see https://en.cppreference.com/w/cpp/keyword
319_keywords = [
320    'alignas', 'alignof', 'and', 'and_eq', 'asm', 'auto', 'bitand', 'bitor',
321    'bool', 'break', 'case', 'catch', 'char', 'char16_t', 'char32_t', 'class',
322    'compl', 'concept', 'const', 'constexpr', 'const_cast', 'continue',
323    'decltype', 'default', 'delete', 'do', 'double', 'dynamic_cast', 'else',
324    'enum', 'explicit', 'export', 'extern', 'false', 'float', 'for', 'friend',
325    'goto', 'if', 'inline', 'int', 'long', 'mutable', 'namespace', 'new',
326    'noexcept', 'not', 'not_eq', 'nullptr', 'operator', 'or', 'or_eq',
327    'private', 'protected', 'public', 'register', 'reinterpret_cast',
328    'requires', 'return', 'short', 'signed', 'sizeof', 'static',
329    'static_assert', 'static_cast', 'struct', 'switch', 'template', 'this',
330    'thread_local', 'throw', 'true', 'try', 'typedef', 'typeid', 'typename',
331    'union', 'unsigned', 'using', 'virtual', 'void', 'volatile', 'wchar_t',
332    'while', 'xor', 'xor_eq'
333]
334
335_max_id = 4
336_id_prefix = [None, '', '_CPPv2', '_CPPv3', '_CPPv4']
337# Ids are used in lookup keys which are used across pickled files,
338# so when _max_id changes, make sure to update the ENV_VERSION.
339
340# ------------------------------------------------------------------------------
341# Id v1 constants
342# ------------------------------------------------------------------------------
343
344_id_fundamental_v1 = {
345    'char': 'c',
346    'signed char': 'c',
347    'unsigned char': 'C',
348    'int': 'i',
349    'signed int': 'i',
350    'unsigned int': 'U',
351    'long': 'l',
352    'signed long': 'l',
353    'unsigned long': 'L',
354    'bool': 'b'
355}
356_id_shorthands_v1 = {
357    'std::string': 'ss',
358    'std::ostream': 'os',
359    'std::istream': 'is',
360    'std::iostream': 'ios',
361    'std::vector': 'v',
362    'std::map': 'm'
363}
364_id_operator_v1 = {
365    'new': 'new-operator',
366    'new[]': 'new-array-operator',
367    'delete': 'delete-operator',
368    'delete[]': 'delete-array-operator',
369    # the arguments will make the difference between unary and binary
370    # '+(unary)' : 'ps',
371    # '-(unary)' : 'ng',
372    # '&(unary)' : 'ad',
373    # '*(unary)' : 'de',
374    '~': 'inv-operator',
375    '+': 'add-operator',
376    '-': 'sub-operator',
377    '*': 'mul-operator',
378    '/': 'div-operator',
379    '%': 'mod-operator',
380    '&': 'and-operator',
381    '|': 'or-operator',
382    '^': 'xor-operator',
383    '=': 'assign-operator',
384    '+=': 'add-assign-operator',
385    '-=': 'sub-assign-operator',
386    '*=': 'mul-assign-operator',
387    '/=': 'div-assign-operator',
388    '%=': 'mod-assign-operator',
389    '&=': 'and-assign-operator',
390    '|=': 'or-assign-operator',
391    '^=': 'xor-assign-operator',
392    '<<': 'lshift-operator',
393    '>>': 'rshift-operator',
394    '<<=': 'lshift-assign-operator',
395    '>>=': 'rshift-assign-operator',
396    '==': 'eq-operator',
397    '!=': 'neq-operator',
398    '<': 'lt-operator',
399    '>': 'gt-operator',
400    '<=': 'lte-operator',
401    '>=': 'gte-operator',
402    '!': 'not-operator',
403    '&&': 'sand-operator',
404    '||': 'sor-operator',
405    '++': 'inc-operator',
406    '--': 'dec-operator',
407    ',': 'comma-operator',
408    '->*': 'pointer-by-pointer-operator',
409    '->': 'pointer-operator',
410    '()': 'call-operator',
411    '[]': 'subscript-operator'
412}
413
414# ------------------------------------------------------------------------------
415# Id v > 1 constants
416# ------------------------------------------------------------------------------
417
418_id_fundamental_v2 = {
419    # not all of these are actually parsed as fundamental types, TODO: do that
420    'void': 'v',
421    'bool': 'b',
422    'char': 'c',
423    'signed char': 'a',
424    'unsigned char': 'h',
425    'wchar_t': 'w',
426    'char32_t': 'Di',
427    'char16_t': 'Ds',
428    'short': 's',
429    'short int': 's',
430    'signed short': 's',
431    'signed short int': 's',
432    'unsigned short': 't',
433    'unsigned short int': 't',
434    'int': 'i',
435    'signed': 'i',
436    'signed int': 'i',
437    'unsigned': 'j',
438    'unsigned int': 'j',
439    'long': 'l',
440    'long int': 'l',
441    'signed long': 'l',
442    'signed long int': 'l',
443    'unsigned long': 'm',
444    'unsigned long int': 'm',
445    'long long': 'x',
446    'long long int': 'x',
447    'signed long long': 'x',
448    'signed long long int': 'x',
449    'unsigned long long': 'y',
450    'unsigned long long int': 'y',
451    'float': 'f',
452    'double': 'd',
453    'long double': 'e',
454    'auto': 'Da',
455    'decltype(auto)': 'Dc',
456    'std::nullptr_t': 'Dn'
457}
458_id_operator_v2 = {
459    'new': 'nw',
460    'new[]': 'na',
461    'delete': 'dl',
462    'delete[]': 'da',
463    # the arguments will make the difference between unary and binary
464    # in operator definitions
465    # '+(unary)' : 'ps',
466    # '-(unary)' : 'ng',
467    # '&(unary)' : 'ad',
468    # '*(unary)' : 'de',
469    '~': 'co', 'compl': 'co',
470    '+': 'pl',
471    '-': 'mi',
472    '*': 'ml',
473    '/': 'dv',
474    '%': 'rm',
475    '&': 'an', 'bitand': 'an',
476    '|': 'or', 'bitor': 'or',
477    '^': 'eo', 'xor': 'eo',
478    '=': 'aS',
479    '+=': 'pL',
480    '-=': 'mI',
481    '*=': 'mL',
482    '/=': 'dV',
483    '%=': 'rM',
484    '&=': 'aN', 'and_eq': 'aN',
485    '|=': 'oR', 'or_eq': 'oR',
486    '^=': 'eO', 'xor_eq': 'eO',
487    '<<': 'ls',
488    '>>': 'rs',
489    '<<=': 'lS',
490    '>>=': 'rS',
491    '==': 'eq',
492    '!=': 'ne', 'not_eq': 'ne',
493    '<': 'lt',
494    '>': 'gt',
495    '<=': 'le',
496    '>=': 'ge',
497    '!': 'nt', 'not': 'nt',
498    '&&': 'aa', 'and': 'aa',
499    '||': 'oo', 'or': 'oo',
500    '++': 'pp',
501    '--': 'mm',
502    ',': 'cm',
503    '->*': 'pm',
504    '->': 'pt',
505    '()': 'cl',
506    '[]': 'ix',
507    '.*': 'ds'  # this one is not overloadable, but we need it for expressions
508}
509_id_operator_unary_v2 = {
510    '++': 'pp_',
511    '--': 'mm_',
512    '*': 'de',
513    '&': 'ad',
514    '+': 'ps',
515    '-': 'ng',
516    '!': 'nt', 'not': 'nt',
517    '~': 'co', 'compl': 'co'
518}
519_id_char_from_prefix = {
520    None: 'c', 'u8': 'c',
521    'u': 'Ds', 'U': 'Di', 'L': 'w'
522}  # type: Dict[Any, str]
523# these are ordered by preceedence
524_expression_bin_ops = [
525    ['||', 'or'],
526    ['&&', 'and'],
527    ['|', 'bitor'],
528    ['^', 'xor'],
529    ['&', 'bitand'],
530    ['==', '!=', 'not_eq'],
531    ['<=', '>=', '<', '>'],
532    ['<<', '>>'],
533    ['+', '-'],
534    ['*', '/', '%'],
535    ['.*', '->*']
536]
537_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "not", "~", "compl"]
538_expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=",
539                              ">>=", "<<=", "&=", "and_eq", "^=", "|=", "xor_eq", "or_eq"]
540_id_explicit_cast = {
541    'dynamic_cast': 'dc',
542    'static_cast': 'sc',
543    'const_cast': 'cc',
544    'reinterpret_cast': 'rc'
545}
546
547
548class _DuplicateSymbolError(Exception):
549    def __init__(self, symbol: "Symbol", declaration: "ASTDeclaration") -> None:
550        assert symbol
551        assert declaration
552        self.symbol = symbol
553        self.declaration = declaration
554
555    def __str__(self) -> str:
556        return "Internal C++ duplicate symbol error:\n%s" % self.symbol.dump(0)
557
558
559class ASTBase(ASTBaseBase):
560    pass
561
562
563# Names
564################################################################################
565
566class ASTIdentifier(ASTBase):
567    def __init__(self, identifier: str) -> None:
568        assert identifier is not None
569        assert len(identifier) != 0
570        self.identifier = identifier
571
572    def is_anon(self) -> bool:
573        return self.identifier[0] == '@'
574
575    def get_id(self, version: int) -> str:
576        if self.is_anon() and version < 3:
577            raise NoOldIdError()
578        if version == 1:
579            if self.identifier == 'size_t':
580                return 's'
581            else:
582                return self.identifier
583        if self.identifier == "std":
584            return 'St'
585        elif self.identifier[0] == "~":
586            # a destructor, just use an arbitrary version of dtors
587            return 'D0'
588        else:
589            if self.is_anon():
590                return 'Ut%d_%s' % (len(self.identifier) - 1, self.identifier[1:])
591            else:
592                return str(len(self.identifier)) + self.identifier
593
594    # and this is where we finally make a difference between __str__ and the display string
595
596    def __str__(self) -> str:
597        return self.identifier
598
599    def get_display_string(self) -> str:
600        return "[anonymous]" if self.is_anon() else self.identifier
601
602    def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment",
603                           prefix: str, templateArgs: str, symbol: "Symbol") -> None:
604        verify_description_mode(mode)
605        if mode == 'markType':
606            targetText = prefix + self.identifier + templateArgs
607            pnode = addnodes.pending_xref('', refdomain='cpp',
608                                          reftype='identifier',
609                                          reftarget=targetText, modname=None,
610                                          classname=None)
611            pnode['cpp:parent_key'] = symbol.get_lookup_key()
612            if self.is_anon():
613                pnode += nodes.strong(text="[anonymous]")
614            else:
615                pnode += nodes.Text(self.identifier)
616            signode += pnode
617        elif mode == 'lastIsName':
618            if self.is_anon():
619                signode += nodes.strong(text="[anonymous]")
620            else:
621                signode += addnodes.desc_name(self.identifier, self.identifier)
622        elif mode == 'noneIsName':
623            if self.is_anon():
624                signode += nodes.strong(text="[anonymous]")
625            else:
626                signode += nodes.Text(self.identifier)
627        elif mode == 'udl':
628            # the target is 'operator""id' instead of just 'id'
629            assert len(prefix) == 0
630            assert len(templateArgs) == 0
631            assert not self.is_anon()
632            targetText = 'operator""' + self.identifier
633            pnode = addnodes.pending_xref('', refdomain='cpp',
634                                          reftype='identifier',
635                                          reftarget=targetText, modname=None,
636                                          classname=None)
637            pnode['cpp:parent_key'] = symbol.get_lookup_key()
638            pnode += nodes.Text(self.identifier)
639            signode += pnode
640        else:
641            raise Exception('Unknown description mode: %s' % mode)
642
643
644class ASTNestedNameElement(ASTBase):
645    def __init__(self, identOrOp: Union[ASTIdentifier, "ASTOperator"],
646                 templateArgs: "ASTTemplateArgs") -> None:
647        self.identOrOp = identOrOp
648        self.templateArgs = templateArgs
649
650    def is_operator(self) -> bool:
651        return False
652
653    def get_id(self, version: int) -> str:
654        res = self.identOrOp.get_id(version)
655        if self.templateArgs:
656            res += self.templateArgs.get_id(version)
657        return res
658
659    def _stringify(self, transform: StringifyTransform) -> str:
660        res = transform(self.identOrOp)
661        if self.templateArgs:
662            res += transform(self.templateArgs)
663        return res
664
665    def describe_signature(self, signode: TextElement, mode: str,
666                           env: "BuildEnvironment", prefix: str, symbol: "Symbol") -> None:
667        tArgs = str(self.templateArgs) if self.templateArgs is not None else ''
668        self.identOrOp.describe_signature(signode, mode, env, prefix, tArgs, symbol)
669        if self.templateArgs is not None:
670            self.templateArgs.describe_signature(signode, mode, env, symbol)
671
672
673class ASTNestedName(ASTBase):
674    def __init__(self, names: List[ASTNestedNameElement],
675                 templates: List[bool], rooted: bool) -> None:
676        assert len(names) > 0
677        self.names = names
678        self.templates = templates
679        assert len(self.names) == len(self.templates)
680        self.rooted = rooted
681
682    @property
683    def name(self) -> "ASTNestedName":
684        return self
685
686    def num_templates(self) -> int:
687        count = 0
688        for n in self.names:
689            if n.is_operator():
690                continue
691            if n.templateArgs:
692                count += 1
693        return count
694
695    def get_id(self, version: int, modifiers: str = '') -> str:
696        if version == 1:
697            tt = str(self)
698            if tt in _id_shorthands_v1:
699                return _id_shorthands_v1[tt]
700            else:
701                return '::'.join(n.get_id(version) for n in self.names)
702
703        res = []
704        if len(self.names) > 1 or len(modifiers) > 0:
705            res.append('N')
706        res.append(modifiers)
707        for n in self.names:
708            res.append(n.get_id(version))
709        if len(self.names) > 1 or len(modifiers) > 0:
710            res.append('E')
711        return ''.join(res)
712
713    def _stringify(self, transform: StringifyTransform) -> str:
714        res = []
715        if self.rooted:
716            res.append('')
717        for i in range(len(self.names)):
718            n = self.names[i]
719            t = self.templates[i]
720            if t:
721                res.append("template " + transform(n))
722            else:
723                res.append(transform(n))
724        return '::'.join(res)
725
726    def describe_signature(self, signode: TextElement, mode: str,
727                           env: "BuildEnvironment", symbol: "Symbol") -> None:
728        verify_description_mode(mode)
729        # just print the name part, with template args, not template params
730        if mode == 'noneIsName':
731            signode += nodes.Text(str(self))
732        elif mode == 'param':
733            name = str(self)
734            signode += nodes.emphasis(name, name)
735        elif mode == 'markType' or mode == 'lastIsName' or mode == 'markName':
736            # Each element should be a pending xref targeting the complete
737            # prefix. however, only the identifier part should be a link, such
738            # that template args can be a link as well.
739            # For 'lastIsName' we should also prepend template parameter lists.
740            templateParams = []  # type: List[Any]
741            if mode == 'lastIsName':
742                assert symbol is not None
743                if symbol.declaration.templatePrefix is not None:
744                    templateParams = symbol.declaration.templatePrefix.templates
745            iTemplateParams = 0
746            templateParamsPrefix = ''
747            prefix = ''
748            first = True
749            names = self.names[:-1] if mode == 'lastIsName' else self.names
750            # If lastIsName, then wrap all of the prefix in a desc_addname,
751            # else append directly to signode.
752            # NOTE: Breathe previously relied on the prefix being in the desc_addname node,
753            #       so it can remove it in inner declarations.
754            dest = signode
755            if mode == 'lastIsName':
756                dest = addnodes.desc_addname()
757            if self.rooted:
758                prefix += '::'
759                if mode == 'lastIsName' and len(names) == 0:
760                    signode += nodes.Text('::')
761                else:
762                    dest += nodes.Text('::')
763            for i in range(len(names)):
764                nne = names[i]
765                template = self.templates[i]
766                if not first:
767                    dest += nodes.Text('::')
768                    prefix += '::'
769                if template:
770                    dest += nodes.Text("template ")
771                first = False
772                txt_nne = str(nne)
773                if txt_nne != '':
774                    if nne.templateArgs and iTemplateParams < len(templateParams):
775                        templateParamsPrefix += str(templateParams[iTemplateParams])
776                        iTemplateParams += 1
777                    nne.describe_signature(dest, 'markType',
778                                           env, templateParamsPrefix + prefix, symbol)
779                prefix += txt_nne
780            if mode == 'lastIsName':
781                if len(self.names) > 1:
782                    dest += addnodes.desc_addname('::', '::')
783                    signode += dest
784                if self.templates[-1]:
785                    signode += nodes.Text("template ")
786                self.names[-1].describe_signature(signode, mode, env, '', symbol)
787        else:
788            raise Exception('Unknown description mode: %s' % mode)
789
790
791################################################################################
792# Expressions
793################################################################################
794
795class ASTExpression(ASTBase):
796    def get_id(self, version: int) -> str:
797        raise NotImplementedError(repr(self))
798
799    def describe_signature(self, signode: TextElement, mode: str,
800                           env: "BuildEnvironment", symbol: "Symbol") -> None:
801        raise NotImplementedError(repr(self))
802
803
804# Primary expressions
805################################################################################
806
807class ASTLiteral(ASTExpression):
808    pass
809
810
811class ASTPointerLiteral(ASTLiteral):
812    def _stringify(self, transform: StringifyTransform) -> str:
813        return 'nullptr'
814
815    def get_id(self, version: int) -> str:
816        return 'LDnE'
817
818    def describe_signature(self, signode: TextElement, mode: str,
819                           env: "BuildEnvironment", symbol: "Symbol") -> None:
820        signode.append(nodes.Text('nullptr'))
821
822
823class ASTBooleanLiteral(ASTLiteral):
824    def __init__(self, value: bool) -> None:
825        self.value = value
826
827    def _stringify(self, transform: StringifyTransform) -> str:
828        if self.value:
829            return 'true'
830        else:
831            return 'false'
832
833    def get_id(self, version: int) -> str:
834        if self.value:
835            return 'L1E'
836        else:
837            return 'L0E'
838
839    def describe_signature(self, signode: TextElement, mode: str,
840                           env: "BuildEnvironment", symbol: "Symbol") -> None:
841        signode.append(nodes.Text(str(self)))
842
843
844class ASTNumberLiteral(ASTLiteral):
845    def __init__(self, data: str) -> None:
846        self.data = data
847
848    def _stringify(self, transform: StringifyTransform) -> str:
849        return self.data
850
851    def get_id(self, version: int) -> str:
852        # TODO: floats should be mangled by writing the hex of the binary representation
853        return "L%sE" % self.data
854
855    def describe_signature(self, signode: TextElement, mode: str,
856                           env: "BuildEnvironment", symbol: "Symbol") -> None:
857        txt = str(self)
858        signode.append(nodes.Text(txt, txt))
859
860
861class ASTStringLiteral(ASTLiteral):
862    def __init__(self, data: str) -> None:
863        self.data = data
864
865    def _stringify(self, transform: StringifyTransform) -> str:
866        return self.data
867
868    def get_id(self, version: int) -> str:
869        # note: the length is not really correct with escaping
870        return "LA%d_KcE" % (len(self.data) - 2)
871
872    def describe_signature(self, signode: TextElement, mode: str,
873                           env: "BuildEnvironment", symbol: "Symbol") -> None:
874        txt = str(self)
875        signode.append(nodes.Text(txt, txt))
876
877
878class ASTCharLiteral(ASTLiteral):
879    def __init__(self, prefix: str, data: str) -> None:
880        self.prefix = prefix  # may be None when no prefix
881        self.data = data
882        assert prefix in _id_char_from_prefix
883        self.type = _id_char_from_prefix[prefix]
884        decoded = data.encode().decode('unicode-escape')
885        if len(decoded) == 1:
886            self.value = ord(decoded)
887        else:
888            raise UnsupportedMultiCharacterCharLiteral(decoded)
889
890    def _stringify(self, transform: StringifyTransform) -> str:
891        if self.prefix is None:
892            return "'" + self.data + "'"
893        else:
894            return self.prefix + "'" + self.data + "'"
895
896    def get_id(self, version: int) -> str:
897        # TODO: the ID should be have L E around it
898        return self.type + str(self.value)
899
900    def describe_signature(self, signode: TextElement, mode: str,
901                           env: "BuildEnvironment", symbol: "Symbol") -> None:
902        txt = str(self)
903        signode.append(nodes.Text(txt, txt))
904
905
906class ASTUserDefinedLiteral(ASTLiteral):
907    def __init__(self, literal: ASTLiteral, ident: ASTIdentifier):
908        self.literal = literal
909        self.ident = ident
910
911    def _stringify(self, transform: StringifyTransform) -> str:
912        return transform(self.literal) + transform(self.ident)
913
914    def get_id(self, version: int) -> str:
915        # mangle as if it was a function call: ident(literal)
916        return 'clL_Zli{}E{}E'.format(self.ident.get_id(version), self.literal.get_id(version))
917
918    def describe_signature(self, signode: TextElement, mode: str,
919                           env: "BuildEnvironment", symbol: "Symbol") -> None:
920        self.literal.describe_signature(signode, mode, env, symbol)
921        self.ident.describe_signature(signode, "udl", env, "", "", symbol)
922
923
924################################################################################
925
926class ASTThisLiteral(ASTExpression):
927    def _stringify(self, transform: StringifyTransform) -> str:
928        return "this"
929
930    def get_id(self, version: int) -> str:
931        return "fpT"
932
933    def describe_signature(self, signode: TextElement, mode: str,
934                           env: "BuildEnvironment", symbol: "Symbol") -> None:
935        signode.append(nodes.Text("this"))
936
937
938class ASTFoldExpr(ASTExpression):
939    def __init__(self, leftExpr: ASTExpression,
940                 op: str, rightExpr: ASTExpression) -> None:
941        assert leftExpr is not None or rightExpr is not None
942        self.leftExpr = leftExpr
943        self.op = op
944        self.rightExpr = rightExpr
945
946    def _stringify(self, transform: StringifyTransform) -> str:
947        res = ['(']
948        if self.leftExpr:
949            res.append(transform(self.leftExpr))
950            res.append(' ')
951            res.append(self.op)
952            res.append(' ')
953        res.append('...')
954        if self.rightExpr:
955            res.append(' ')
956            res.append(self.op)
957            res.append(' ')
958            res.append(transform(self.rightExpr))
959        res.append(')')
960        return ''.join(res)
961
962    def get_id(self, version: int) -> str:
963        assert version >= 3
964        if version == 3:
965            return str(self)
966        # https://github.com/itanium-cxx-abi/cxx-abi/pull/67
967        res = []
968        if self.leftExpr is None:  # (... op expr)
969            res.append('fl')
970        elif self.rightExpr is None:  # (expr op ...)
971            res.append('fr')
972        else:  # (expr op ... op expr)
973            # we don't check where the parameter pack is,
974            # we just always call this a binary left fold
975            res.append('fL')
976        res.append(_id_operator_v2[self.op])
977        if self.leftExpr:
978            res.append(self.leftExpr.get_id(version))
979        if self.rightExpr:
980            res.append(self.rightExpr.get_id(version))
981        return ''.join(res)
982
983    def describe_signature(self, signode: TextElement, mode: str,
984                           env: "BuildEnvironment", symbol: "Symbol") -> None:
985        signode.append(nodes.Text('('))
986        if self.leftExpr:
987            self.leftExpr.describe_signature(signode, mode, env, symbol)
988            signode.append(nodes.Text(' '))
989            signode.append(nodes.Text(self.op))
990            signode.append(nodes.Text(' '))
991        signode.append(nodes.Text('...'))
992        if self.rightExpr:
993            signode.append(nodes.Text(' '))
994            signode.append(nodes.Text(self.op))
995            signode.append(nodes.Text(' '))
996            self.rightExpr.describe_signature(signode, mode, env, symbol)
997        signode.append(nodes.Text(')'))
998
999
1000class ASTParenExpr(ASTExpression):
1001    def __init__(self, expr: ASTExpression):
1002        self.expr = expr
1003
1004    def _stringify(self, transform: StringifyTransform) -> str:
1005        return '(' + transform(self.expr) + ')'
1006
1007    def get_id(self, version: int) -> str:
1008        return self.expr.get_id(version)
1009
1010    def describe_signature(self, signode: TextElement, mode: str,
1011                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1012        signode.append(nodes.Text('(', '('))
1013        self.expr.describe_signature(signode, mode, env, symbol)
1014        signode.append(nodes.Text(')', ')'))
1015
1016
1017class ASTIdExpression(ASTExpression):
1018    def __init__(self, name: ASTNestedName):
1019        # note: this class is basically to cast a nested name as an expression
1020        self.name = name
1021
1022    def _stringify(self, transform: StringifyTransform) -> str:
1023        return transform(self.name)
1024
1025    def get_id(self, version: int) -> str:
1026        return self.name.get_id(version)
1027
1028    def describe_signature(self, signode: TextElement, mode: str,
1029                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1030        self.name.describe_signature(signode, mode, env, symbol)
1031
1032
1033# Postfix expressions
1034################################################################################
1035
1036class ASTPostfixOp(ASTBase):
1037    def get_id(self, idPrefix: str, version: int) -> str:
1038        raise NotImplementedError(repr(self))
1039
1040    def describe_signature(self, signode: TextElement, mode: str,
1041                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1042        raise NotImplementedError(repr(self))
1043
1044
1045class ASTPostfixArray(ASTPostfixOp):
1046    def __init__(self, expr: ASTExpression):
1047        self.expr = expr
1048
1049    def _stringify(self, transform: StringifyTransform) -> str:
1050        return '[' + transform(self.expr) + ']'
1051
1052    def get_id(self, idPrefix: str, version: int) -> str:
1053        return 'ix' + idPrefix + self.expr.get_id(version)
1054
1055    def describe_signature(self, signode: TextElement, mode: str,
1056                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1057        signode.append(nodes.Text('['))
1058        self.expr.describe_signature(signode, mode, env, symbol)
1059        signode.append(nodes.Text(']'))
1060
1061
1062class ASTPostfixMember(ASTPostfixOp):
1063    def __init__(self, name: ASTNestedName):
1064        self.name = name
1065
1066    def _stringify(self, transform: StringifyTransform) -> str:
1067        return '.' + transform(self.name)
1068
1069    def get_id(self, idPrefix: str, version: int) -> str:
1070        return 'dt' + idPrefix + self.name.get_id(version)
1071
1072    def describe_signature(self, signode: TextElement, mode: str,
1073                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1074        signode.append(nodes.Text('.'))
1075        self.name.describe_signature(signode, 'noneIsName', env, symbol)
1076
1077
1078class ASTPostfixMemberOfPointer(ASTPostfixOp):
1079    def __init__(self, name: ASTNestedName):
1080        self.name = name
1081
1082    def _stringify(self, transform: StringifyTransform) -> str:
1083        return '->' + transform(self.name)
1084
1085    def get_id(self, idPrefix: str, version: int) -> str:
1086        return 'pt' + idPrefix + self.name.get_id(version)
1087
1088    def describe_signature(self, signode: TextElement, mode: str,
1089                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1090        signode.append(nodes.Text('->'))
1091        self.name.describe_signature(signode, 'noneIsName', env, symbol)
1092
1093
1094class ASTPostfixInc(ASTPostfixOp):
1095    def _stringify(self, transform: StringifyTransform) -> str:
1096        return '++'
1097
1098    def get_id(self, idPrefix: str, version: int) -> str:
1099        return 'pp' + idPrefix
1100
1101    def describe_signature(self, signode: TextElement, mode: str,
1102                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1103        signode.append(nodes.Text('++'))
1104
1105
1106class ASTPostfixDec(ASTPostfixOp):
1107    def _stringify(self, transform: StringifyTransform) -> str:
1108        return '--'
1109
1110    def get_id(self, idPrefix: str, version: int) -> str:
1111        return 'mm' + idPrefix
1112
1113    def describe_signature(self, signode: TextElement, mode: str,
1114                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1115        signode.append(nodes.Text('--'))
1116
1117
1118class ASTPostfixCallExpr(ASTPostfixOp):
1119    def __init__(self, lst: Union["ASTParenExprList", "ASTBracedInitList"]) -> None:
1120        self.lst = lst
1121
1122    def _stringify(self, transform: StringifyTransform) -> str:
1123        return transform(self.lst)
1124
1125    def get_id(self, idPrefix: str, version: int) -> str:
1126        res = ['cl', idPrefix]
1127        for e in self.lst.exprs:
1128            res.append(e.get_id(version))
1129        res.append('E')
1130        return ''.join(res)
1131
1132    def describe_signature(self, signode: TextElement, mode: str,
1133                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1134        self.lst.describe_signature(signode, mode, env, symbol)
1135
1136
1137class ASTPostfixExpr(ASTExpression):
1138    def __init__(self, prefix: "ASTType", postFixes: List[ASTPostfixOp]):
1139        self.prefix = prefix
1140        self.postFixes = postFixes
1141
1142    def _stringify(self, transform: StringifyTransform) -> str:
1143        res = [transform(self.prefix)]
1144        for p in self.postFixes:
1145            res.append(transform(p))
1146        return ''.join(res)
1147
1148    def get_id(self, version: int) -> str:
1149        id = self.prefix.get_id(version)
1150        for p in self.postFixes:
1151            id = p.get_id(id, version)
1152        return id
1153
1154    def describe_signature(self, signode: TextElement, mode: str,
1155                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1156        self.prefix.describe_signature(signode, mode, env, symbol)
1157        for p in self.postFixes:
1158            p.describe_signature(signode, mode, env, symbol)
1159
1160
1161class ASTExplicitCast(ASTExpression):
1162    def __init__(self, cast: str, typ: "ASTType", expr: ASTExpression):
1163        assert cast in _id_explicit_cast
1164        self.cast = cast
1165        self.typ = typ
1166        self.expr = expr
1167
1168    def _stringify(self, transform: StringifyTransform) -> str:
1169        res = [self.cast]
1170        res.append('<')
1171        res.append(transform(self.typ))
1172        res.append('>(')
1173        res.append(transform(self.expr))
1174        res.append(')')
1175        return ''.join(res)
1176
1177    def get_id(self, version: int) -> str:
1178        return (_id_explicit_cast[self.cast] +
1179                self.typ.get_id(version) +
1180                self.expr.get_id(version))
1181
1182    def describe_signature(self, signode: TextElement, mode: str,
1183                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1184        signode.append(nodes.Text(self.cast))
1185        signode.append(nodes.Text('<'))
1186        self.typ.describe_signature(signode, mode, env, symbol)
1187        signode.append(nodes.Text('>'))
1188        signode.append(nodes.Text('('))
1189        self.expr.describe_signature(signode, mode, env, symbol)
1190        signode.append(nodes.Text(')'))
1191
1192
1193class ASTTypeId(ASTExpression):
1194    def __init__(self, typeOrExpr: Union["ASTType", ASTExpression], isType: bool):
1195        self.typeOrExpr = typeOrExpr
1196        self.isType = isType
1197
1198    def _stringify(self, transform: StringifyTransform) -> str:
1199        return 'typeid(' + transform(self.typeOrExpr) + ')'
1200
1201    def get_id(self, version: int) -> str:
1202        prefix = 'ti' if self.isType else 'te'
1203        return prefix + self.typeOrExpr.get_id(version)
1204
1205    def describe_signature(self, signode: TextElement, mode: str,
1206                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1207        signode.append(nodes.Text('typeid'))
1208        signode.append(nodes.Text('('))
1209        self.typeOrExpr.describe_signature(signode, mode, env, symbol)
1210        signode.append(nodes.Text(')'))
1211
1212
1213# Unary expressions
1214################################################################################
1215
1216class ASTUnaryOpExpr(ASTExpression):
1217    def __init__(self, op: str, expr: ASTExpression):
1218        self.op = op
1219        self.expr = expr
1220
1221    def _stringify(self, transform: StringifyTransform) -> str:
1222        if self.op[0] in 'cn':
1223            return self.op + " " + transform(self.expr)
1224        else:
1225            return self.op + transform(self.expr)
1226
1227    def get_id(self, version: int) -> str:
1228        return _id_operator_unary_v2[self.op] + self.expr.get_id(version)
1229
1230    def describe_signature(self, signode: TextElement, mode: str,
1231                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1232        signode.append(nodes.Text(self.op))
1233        if self.op[0] in 'cn':
1234            signode.append(nodes.Text(' '))
1235        self.expr.describe_signature(signode, mode, env, symbol)
1236
1237
1238class ASTSizeofParamPack(ASTExpression):
1239    def __init__(self, identifier: ASTIdentifier):
1240        self.identifier = identifier
1241
1242    def _stringify(self, transform: StringifyTransform) -> str:
1243        return "sizeof...(" + transform(self.identifier) + ")"
1244
1245    def get_id(self, version: int) -> str:
1246        return 'sZ' + self.identifier.get_id(version)
1247
1248    def describe_signature(self, signode: TextElement, mode: str,
1249                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1250        signode.append(nodes.Text('sizeof...('))
1251        self.identifier.describe_signature(signode, mode, env,
1252                                           symbol=symbol, prefix="", templateArgs="")
1253        signode.append(nodes.Text(')'))
1254
1255
1256class ASTSizeofType(ASTExpression):
1257    def __init__(self, typ: "ASTType"):
1258        self.typ = typ
1259
1260    def _stringify(self, transform: StringifyTransform) -> str:
1261        return "sizeof(" + transform(self.typ) + ")"
1262
1263    def get_id(self, version: int) -> str:
1264        return 'st' + self.typ.get_id(version)
1265
1266    def describe_signature(self, signode: TextElement, mode: str,
1267                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1268        signode.append(nodes.Text('sizeof('))
1269        self.typ.describe_signature(signode, mode, env, symbol)
1270        signode.append(nodes.Text(')'))
1271
1272
1273class ASTSizeofExpr(ASTExpression):
1274    def __init__(self, expr: ASTExpression):
1275        self.expr = expr
1276
1277    def _stringify(self, transform: StringifyTransform) -> str:
1278        return "sizeof " + transform(self.expr)
1279
1280    def get_id(self, version: int) -> str:
1281        return 'sz' + self.expr.get_id(version)
1282
1283    def describe_signature(self, signode: TextElement, mode: str,
1284                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1285        signode.append(nodes.Text('sizeof '))
1286        self.expr.describe_signature(signode, mode, env, symbol)
1287
1288
1289class ASTAlignofExpr(ASTExpression):
1290    def __init__(self, typ: "ASTType"):
1291        self.typ = typ
1292
1293    def _stringify(self, transform: StringifyTransform) -> str:
1294        return "alignof(" + transform(self.typ) + ")"
1295
1296    def get_id(self, version: int) -> str:
1297        return 'at' + self.typ.get_id(version)
1298
1299    def describe_signature(self, signode: TextElement, mode: str,
1300                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1301        signode.append(nodes.Text('alignof('))
1302        self.typ.describe_signature(signode, mode, env, symbol)
1303        signode.append(nodes.Text(')'))
1304
1305
1306class ASTNoexceptExpr(ASTExpression):
1307    def __init__(self, expr: ASTExpression):
1308        self.expr = expr
1309
1310    def _stringify(self, transform: StringifyTransform) -> str:
1311        return 'noexcept(' + transform(self.expr) + ')'
1312
1313    def get_id(self, version: int) -> str:
1314        return 'nx' + self.expr.get_id(version)
1315
1316    def describe_signature(self, signode: TextElement, mode: str,
1317                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1318        signode.append(nodes.Text('noexcept('))
1319        self.expr.describe_signature(signode, mode, env, symbol)
1320        signode.append(nodes.Text(')'))
1321
1322
1323class ASTNewExpr(ASTExpression):
1324    def __init__(self, rooted: bool, isNewTypeId: bool, typ: "ASTType",
1325                 initList: Union["ASTParenExprList", "ASTBracedInitList"]) -> None:
1326        self.rooted = rooted
1327        self.isNewTypeId = isNewTypeId
1328        self.typ = typ
1329        self.initList = initList
1330
1331    def _stringify(self, transform: StringifyTransform) -> str:
1332        res = []
1333        if self.rooted:
1334            res.append('::')
1335        res.append('new ')
1336        # TODO: placement
1337        if self.isNewTypeId:
1338            res.append(transform(self.typ))
1339        else:
1340            assert False
1341        if self.initList is not None:
1342            res.append(transform(self.initList))
1343        return ''.join(res)
1344
1345    def get_id(self, version: int) -> str:
1346        # the array part will be in the type mangling, so na is not used
1347        res = ['nw']
1348        # TODO: placement
1349        res.append('_')
1350        res.append(self.typ.get_id(version))
1351        if self.initList is not None:
1352            res.append(self.initList.get_id(version))
1353        else:
1354            res.append('E')
1355        return ''.join(res)
1356
1357    def describe_signature(self, signode: TextElement, mode: str,
1358                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1359        if self.rooted:
1360            signode.append(nodes.Text('::'))
1361        signode.append(nodes.Text('new '))
1362        # TODO: placement
1363        if self.isNewTypeId:
1364            self.typ.describe_signature(signode, mode, env, symbol)
1365        else:
1366            assert False
1367        if self.initList is not None:
1368            self.initList.describe_signature(signode, mode, env, symbol)
1369
1370
1371class ASTDeleteExpr(ASTExpression):
1372    def __init__(self, rooted: bool, array: bool, expr: ASTExpression):
1373        self.rooted = rooted
1374        self.array = array
1375        self.expr = expr
1376
1377    def _stringify(self, transform: StringifyTransform) -> str:
1378        res = []
1379        if self.rooted:
1380            res.append('::')
1381        res.append('delete ')
1382        if self.array:
1383            res.append('[] ')
1384        res.append(transform(self.expr))
1385        return ''.join(res)
1386
1387    def get_id(self, version: int) -> str:
1388        if self.array:
1389            id = "da"
1390        else:
1391            id = "dl"
1392        return id + self.expr.get_id(version)
1393
1394    def describe_signature(self, signode: TextElement, mode: str,
1395                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1396        if self.rooted:
1397            signode.append(nodes.Text('::'))
1398        signode.append(nodes.Text('delete '))
1399        if self.array:
1400            signode.append(nodes.Text('[] '))
1401        self.expr.describe_signature(signode, mode, env, symbol)
1402
1403
1404# Other expressions
1405################################################################################
1406
1407class ASTCastExpr(ASTExpression):
1408    def __init__(self, typ: "ASTType", expr: ASTExpression):
1409        self.typ = typ
1410        self.expr = expr
1411
1412    def _stringify(self, transform: StringifyTransform) -> str:
1413        res = ['(']
1414        res.append(transform(self.typ))
1415        res.append(')')
1416        res.append(transform(self.expr))
1417        return ''.join(res)
1418
1419    def get_id(self, version: int) -> str:
1420        return 'cv' + self.typ.get_id(version) + self.expr.get_id(version)
1421
1422    def describe_signature(self, signode: TextElement, mode: str,
1423                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1424        signode.append(nodes.Text('('))
1425        self.typ.describe_signature(signode, mode, env, symbol)
1426        signode.append(nodes.Text(')'))
1427        self.expr.describe_signature(signode, mode, env, symbol)
1428
1429
1430class ASTBinOpExpr(ASTExpression):
1431    def __init__(self, exprs: List[ASTExpression], ops: List[str]):
1432        assert len(exprs) > 0
1433        assert len(exprs) == len(ops) + 1
1434        self.exprs = exprs
1435        self.ops = ops
1436
1437    def _stringify(self, transform: StringifyTransform) -> str:
1438        res = []
1439        res.append(transform(self.exprs[0]))
1440        for i in range(1, len(self.exprs)):
1441            res.append(' ')
1442            res.append(self.ops[i - 1])
1443            res.append(' ')
1444            res.append(transform(self.exprs[i]))
1445        return ''.join(res)
1446
1447    def get_id(self, version: int) -> str:
1448        assert version >= 2
1449        res = []
1450        for i in range(len(self.ops)):
1451            res.append(_id_operator_v2[self.ops[i]])
1452            res.append(self.exprs[i].get_id(version))
1453        res.append(self.exprs[-1].get_id(version))
1454        return ''.join(res)
1455
1456    def describe_signature(self, signode: TextElement, mode: str,
1457                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1458        self.exprs[0].describe_signature(signode, mode, env, symbol)
1459        for i in range(1, len(self.exprs)):
1460            signode.append(nodes.Text(' '))
1461            signode.append(nodes.Text(self.ops[i - 1]))
1462            signode.append(nodes.Text(' '))
1463            self.exprs[i].describe_signature(signode, mode, env, symbol)
1464
1465
1466class ASTBracedInitList(ASTBase):
1467    def __init__(self, exprs: List[Union[ASTExpression, "ASTBracedInitList"]],
1468                 trailingComma: bool) -> None:
1469        self.exprs = exprs
1470        self.trailingComma = trailingComma
1471
1472    def get_id(self, version: int) -> str:
1473        return "il%sE" % ''.join(e.get_id(version) for e in self.exprs)
1474
1475    def _stringify(self, transform: StringifyTransform) -> str:
1476        exprs = [transform(e) for e in self.exprs]
1477        trailingComma = ',' if self.trailingComma else ''
1478        return '{%s%s}' % (', '.join(exprs), trailingComma)
1479
1480    def describe_signature(self, signode: TextElement, mode: str,
1481                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1482        verify_description_mode(mode)
1483        signode.append(nodes.Text('{'))
1484        first = True
1485        for e in self.exprs:
1486            if not first:
1487                signode.append(nodes.Text(', '))
1488            else:
1489                first = False
1490            e.describe_signature(signode, mode, env, symbol)
1491        if self.trailingComma:
1492            signode.append(nodes.Text(','))
1493        signode.append(nodes.Text('}'))
1494
1495
1496class ASTAssignmentExpr(ASTExpression):
1497    def __init__(self, exprs: List[Union[ASTExpression, ASTBracedInitList]], ops: List[str]):
1498        assert len(exprs) > 0
1499        assert len(exprs) == len(ops) + 1
1500        self.exprs = exprs
1501        self.ops = ops
1502
1503    def _stringify(self, transform: StringifyTransform) -> str:
1504        res = []
1505        res.append(transform(self.exprs[0]))
1506        for i in range(1, len(self.exprs)):
1507            res.append(' ')
1508            res.append(self.ops[i - 1])
1509            res.append(' ')
1510            res.append(transform(self.exprs[i]))
1511        return ''.join(res)
1512
1513    def get_id(self, version: int) -> str:
1514        res = []
1515        for i in range(len(self.ops)):
1516            res.append(_id_operator_v2[self.ops[i]])
1517            res.append(self.exprs[i].get_id(version))
1518        res.append(self.exprs[-1].get_id(version))
1519        return ''.join(res)
1520
1521    def describe_signature(self, signode: TextElement, mode: str,
1522                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1523        self.exprs[0].describe_signature(signode, mode, env, symbol)
1524        for i in range(1, len(self.exprs)):
1525            signode.append(nodes.Text(' '))
1526            signode.append(nodes.Text(self.ops[i - 1]))
1527            signode.append(nodes.Text(' '))
1528            self.exprs[i].describe_signature(signode, mode, env, symbol)
1529
1530
1531class ASTCommaExpr(ASTExpression):
1532    def __init__(self, exprs: List[ASTExpression]):
1533        assert len(exprs) > 0
1534        self.exprs = exprs
1535
1536    def _stringify(self, transform: StringifyTransform) -> str:
1537        return ', '.join(transform(e) for e in self.exprs)
1538
1539    def get_id(self, version: int) -> str:
1540        id_ = _id_operator_v2[',']
1541        res = []
1542        for i in range(len(self.exprs) - 1):
1543            res.append(id_)
1544            res.append(self.exprs[i].get_id(version))
1545        res.append(self.exprs[-1].get_id(version))
1546        return ''.join(res)
1547
1548    def describe_signature(self, signode: TextElement, mode: str,
1549                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1550        self.exprs[0].describe_signature(signode, mode, env, symbol)
1551        for i in range(1, len(self.exprs)):
1552            signode.append(nodes.Text(', '))
1553            self.exprs[i].describe_signature(signode, mode, env, symbol)
1554
1555
1556class ASTFallbackExpr(ASTExpression):
1557    def __init__(self, expr: str):
1558        self.expr = expr
1559
1560    def _stringify(self, transform: StringifyTransform) -> str:
1561        return self.expr
1562
1563    def get_id(self, version: int) -> str:
1564        return str(self.expr)
1565
1566    def describe_signature(self, signode: TextElement, mode: str,
1567                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1568        signode += nodes.Text(self.expr)
1569
1570
1571################################################################################
1572# Types
1573################################################################################
1574
1575# Things for ASTNestedName
1576################################################################################
1577
1578class ASTOperator(ASTBase):
1579    def is_anon(self) -> bool:
1580        return False
1581
1582    def is_operator(self) -> bool:
1583        return True
1584
1585    def get_id(self, version: int) -> str:
1586        raise NotImplementedError()
1587
1588    def describe_signature(self, signode: TextElement, mode: str,
1589                           env: "BuildEnvironment", prefix: str, templateArgs: str,
1590                           symbol: "Symbol") -> None:
1591        verify_description_mode(mode)
1592        identifier = str(self)
1593        if mode == 'lastIsName':
1594            signode += addnodes.desc_name(identifier, identifier)
1595        elif mode == 'markType':
1596            targetText = prefix + identifier + templateArgs
1597            pnode = addnodes.pending_xref('', refdomain='cpp',
1598                                          reftype='identifier',
1599                                          reftarget=targetText, modname=None,
1600                                          classname=None)
1601            pnode['cpp:parent_key'] = symbol.get_lookup_key()
1602            pnode += nodes.Text(identifier)
1603            signode += pnode
1604        else:
1605            signode += addnodes.desc_addname(identifier, identifier)
1606
1607
1608class ASTOperatorBuildIn(ASTOperator):
1609    def __init__(self, op: str) -> None:
1610        self.op = op
1611
1612    def get_id(self, version: int) -> str:
1613        if version == 1:
1614            ids = _id_operator_v1
1615            if self.op not in ids:
1616                raise NoOldIdError()
1617        else:
1618            ids = _id_operator_v2
1619        if self.op not in ids:
1620            raise Exception('Internal error: Build-in operator "%s" can not '
1621                            'be mapped to an id.' % self.op)
1622        return ids[self.op]
1623
1624    def _stringify(self, transform: StringifyTransform) -> str:
1625        if self.op in ('new', 'new[]', 'delete', 'delete[]') or self.op[0] in "abcnox":
1626            return 'operator ' + self.op
1627        else:
1628            return 'operator' + self.op
1629
1630
1631class ASTOperatorLiteral(ASTOperator):
1632    def __init__(self, identifier: ASTIdentifier) -> None:
1633        self.identifier = identifier
1634
1635    def get_id(self, version: int) -> str:
1636        if version == 1:
1637            raise NoOldIdError()
1638        else:
1639            return 'li' + self.identifier.get_id(version)
1640
1641    def _stringify(self, transform: StringifyTransform) -> str:
1642        return 'operator""' + transform(self.identifier)
1643
1644
1645class ASTOperatorType(ASTOperator):
1646    def __init__(self, type: "ASTType") -> None:
1647        self.type = type
1648
1649    def get_id(self, version: int) -> str:
1650        if version == 1:
1651            return 'castto-%s-operator' % self.type.get_id(version)
1652        else:
1653            return 'cv' + self.type.get_id(version)
1654
1655    def _stringify(self, transform: StringifyTransform) -> str:
1656        return ''.join(['operator ', transform(self.type)])
1657
1658    def get_name_no_template(self) -> str:
1659        return str(self)
1660
1661
1662class ASTTemplateArgConstant(ASTBase):
1663    def __init__(self, value: ASTExpression) -> None:
1664        self.value = value
1665
1666    def _stringify(self, transform: StringifyTransform) -> str:
1667        return transform(self.value)
1668
1669    def get_id(self, version: int) -> str:
1670        if version == 1:
1671            return str(self).replace(' ', '-')
1672        if version == 2:
1673            return 'X' + str(self) + 'E'
1674        return 'X' + self.value.get_id(version) + 'E'
1675
1676    def describe_signature(self, signode: TextElement, mode: str,
1677                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1678        verify_description_mode(mode)
1679        self.value.describe_signature(signode, mode, env, symbol)
1680
1681
1682class ASTTemplateArgs(ASTBase):
1683    def __init__(self, args: List[Union["ASTType", ASTTemplateArgConstant]],
1684                 packExpansion: bool) -> None:
1685        assert args is not None
1686        self.args = args
1687        self.packExpansion = packExpansion
1688
1689    def get_id(self, version: int) -> str:
1690        if version == 1:
1691            res = []
1692            res.append(':')
1693            res.append('.'.join(a.get_id(version) for a in self.args))
1694            res.append(':')
1695            return ''.join(res)
1696
1697        res = []
1698        res.append('I')
1699        if len(self.args) > 0:
1700            for a in self.args[:-1]:
1701                res.append(a.get_id(version))
1702            if self.packExpansion:
1703                res.append('J')
1704            res.append(self.args[-1].get_id(version))
1705            if self.packExpansion:
1706                res.append('E')
1707        res.append('E')
1708        return ''.join(res)
1709
1710    def _stringify(self, transform: StringifyTransform) -> str:
1711        res = ', '.join(transform(a) for a in self.args)
1712        if self.packExpansion:
1713            res += '...'
1714        return '<' + res + '>'
1715
1716    def describe_signature(self, signode: TextElement, mode: str,
1717                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1718        verify_description_mode(mode)
1719        signode += nodes.Text('<')
1720        first = True
1721        for a in self.args:
1722            if not first:
1723                signode += nodes.Text(', ')
1724            first = False
1725            a.describe_signature(signode, 'markType', env, symbol=symbol)
1726        if self.packExpansion:
1727            signode += nodes.Text('...')
1728        signode += nodes.Text('>')
1729
1730
1731# Main part of declarations
1732################################################################################
1733
1734class ASTTrailingTypeSpec(ASTBase):
1735    def get_id(self, version: int) -> str:
1736        raise NotImplementedError(repr(self))
1737
1738    def describe_signature(self, signode: TextElement, mode: str,
1739                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1740        raise NotImplementedError(repr(self))
1741
1742
1743class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
1744    def __init__(self, name: str) -> None:
1745        self.name = name
1746
1747    def _stringify(self, transform: StringifyTransform) -> str:
1748        return self.name
1749
1750    def get_id(self, version: int) -> str:
1751        if version == 1:
1752            res = []
1753            for a in self.name.split(' '):
1754                if a in _id_fundamental_v1:
1755                    res.append(_id_fundamental_v1[a])
1756                else:
1757                    res.append(a)
1758            return '-'.join(res)
1759
1760        if self.name not in _id_fundamental_v2:
1761            raise Exception(
1762                'Semi-internal error: Fundamental type "%s" can not be mapped '
1763                'to an id. Is it a true fundamental type? If not so, the '
1764                'parser should have rejected it.' % self.name)
1765        return _id_fundamental_v2[self.name]
1766
1767    def describe_signature(self, signode: TextElement, mode: str,
1768                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1769        signode += nodes.Text(str(self.name))
1770
1771
1772class ASTTrailingTypeSpecDecltypeAuto(ASTTrailingTypeSpec):
1773    def _stringify(self, transform: StringifyTransform) -> str:
1774        return 'decltype(auto)'
1775
1776    def get_id(self, version: int) -> str:
1777        if version == 1:
1778            raise NoOldIdError()
1779        return 'Dc'
1780
1781    def describe_signature(self, signode: TextElement, mode: str,
1782                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1783        signode.append(nodes.Text(str(self)))
1784
1785
1786class ASTTrailingTypeSpecDecltype(ASTTrailingTypeSpec):
1787    def __init__(self, expr: ASTExpression):
1788        self.expr = expr
1789
1790    def _stringify(self, transform: StringifyTransform) -> str:
1791        return 'decltype(' + transform(self.expr) + ')'
1792
1793    def get_id(self, version: int) -> str:
1794        if version == 1:
1795            raise NoOldIdError()
1796        return 'DT' + self.expr.get_id(version) + "E"
1797
1798    def describe_signature(self, signode: TextElement, mode: str,
1799                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1800        signode.append(nodes.Text('decltype('))
1801        self.expr.describe_signature(signode, mode, env, symbol)
1802        signode.append(nodes.Text(')'))
1803
1804
1805class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
1806    def __init__(self, prefix: str, nestedName: ASTNestedName) -> None:
1807        self.prefix = prefix
1808        self.nestedName = nestedName
1809
1810    @property
1811    def name(self) -> ASTNestedName:
1812        return self.nestedName
1813
1814    def get_id(self, version: int) -> str:
1815        return self.nestedName.get_id(version)
1816
1817    def _stringify(self, transform: StringifyTransform) -> str:
1818        res = []
1819        if self.prefix:
1820            res.append(self.prefix)
1821            res.append(' ')
1822        res.append(transform(self.nestedName))
1823        return ''.join(res)
1824
1825    def describe_signature(self, signode: TextElement, mode: str,
1826                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1827        if self.prefix:
1828            signode += addnodes.desc_annotation(self.prefix, self.prefix)
1829            signode += nodes.Text(' ')
1830        self.nestedName.describe_signature(signode, mode, env, symbol=symbol)
1831
1832
1833class ASTFunctionParameter(ASTBase):
1834    def __init__(self, arg: Union["ASTTypeWithInit",
1835                                  "ASTTemplateParamConstrainedTypeWithInit"],
1836                 ellipsis: bool = False) -> None:
1837        self.arg = arg
1838        self.ellipsis = ellipsis
1839
1840    def get_id(self, version: int, objectType: str = None, symbol: "Symbol" = None) -> str:
1841        # this is not part of the normal name mangling in C++
1842        if symbol:
1843            # the anchor will be our parent
1844            return symbol.parent.declaration.get_id(version, prefixed=False)
1845        # else, do the usual
1846        if self.ellipsis:
1847            return 'z'
1848        else:
1849            return self.arg.get_id(version)
1850
1851    def _stringify(self, transform: StringifyTransform) -> str:
1852        if self.ellipsis:
1853            return '...'
1854        else:
1855            return transform(self.arg)
1856
1857    def describe_signature(self, signode: TextElement, mode: str,
1858                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1859        verify_description_mode(mode)
1860        if self.ellipsis:
1861            signode += nodes.Text('...')
1862        else:
1863            self.arg.describe_signature(signode, mode, env, symbol=symbol)
1864
1865
1866class ASTNoexceptSpec(ASTBase):
1867    def __init__(self, expr: Optional[ASTExpression]):
1868        self.expr = expr
1869
1870    def _stringify(self, transform: StringifyTransform) -> str:
1871        if self.expr:
1872            return 'noexcept(' + transform(self.expr) + ')'
1873        return 'noexcept'
1874
1875    def describe_signature(self, signode: TextElement, mode: str,
1876                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1877        signode += addnodes.desc_annotation('noexcept', 'noexcept')
1878        if self.expr:
1879            signode.append(nodes.Text('('))
1880            self.expr.describe_signature(signode, mode, env, symbol)
1881            signode.append(nodes.Text(')'))
1882
1883
1884class ASTParametersQualifiers(ASTBase):
1885    def __init__(self, args: List[ASTFunctionParameter], volatile: bool, const: bool,
1886                 refQual: str, exceptionSpec: ASTNoexceptSpec, trailingReturn: "ASTType",
1887                 override: bool, final: bool, attrs: List[ASTAttribute],
1888                 initializer: str) -> None:
1889        self.args = args
1890        self.volatile = volatile
1891        self.const = const
1892        self.refQual = refQual
1893        self.exceptionSpec = exceptionSpec
1894        self.trailingReturn = trailingReturn
1895        self.override = override
1896        self.final = final
1897        self.attrs = attrs
1898        self.initializer = initializer
1899
1900    @property
1901    def function_params(self) -> List[ASTFunctionParameter]:
1902        return self.args
1903
1904    def get_modifiers_id(self, version: int) -> str:
1905        res = []
1906        if self.volatile:
1907            res.append('V')
1908        if self.const:
1909            if version == 1:
1910                res.append('C')
1911            else:
1912                res.append('K')
1913        if self.refQual == '&&':
1914            res.append('O')
1915        elif self.refQual == '&':
1916            res.append('R')
1917        return ''.join(res)
1918
1919    def get_param_id(self, version: int) -> str:
1920        if version == 1:
1921            if len(self.args) == 0:
1922                return ''
1923            else:
1924                return '__' + '.'.join(a.get_id(version) for a in self.args)
1925        if len(self.args) == 0:
1926            return 'v'
1927        else:
1928            return ''.join(a.get_id(version) for a in self.args)
1929
1930    def _stringify(self, transform: StringifyTransform) -> str:
1931        res = []
1932        res.append('(')
1933        first = True
1934        for a in self.args:
1935            if not first:
1936                res.append(', ')
1937            first = False
1938            res.append(str(a))
1939        res.append(')')
1940        if self.volatile:
1941            res.append(' volatile')
1942        if self.const:
1943            res.append(' const')
1944        if self.refQual:
1945            res.append(' ')
1946            res.append(self.refQual)
1947        if self.exceptionSpec:
1948            res.append(' ')
1949            res.append(transform(self.exceptionSpec))
1950        if self.trailingReturn:
1951            res.append(' -> ')
1952            res.append(transform(self.trailingReturn))
1953        if self.final:
1954            res.append(' final')
1955        if self.override:
1956            res.append(' override')
1957        for attr in self.attrs:
1958            res.append(' ')
1959            res.append(transform(attr))
1960        if self.initializer:
1961            res.append(' = ')
1962            res.append(self.initializer)
1963        return ''.join(res)
1964
1965    def describe_signature(self, signode: TextElement, mode: str,
1966                           env: "BuildEnvironment", symbol: "Symbol") -> None:
1967        verify_description_mode(mode)
1968        paramlist = addnodes.desc_parameterlist()
1969        for arg in self.args:
1970            param = addnodes.desc_parameter('', '', noemph=True)
1971            if mode == 'lastIsName':  # i.e., outer-function params
1972                arg.describe_signature(param, 'param', env, symbol=symbol)
1973            else:
1974                arg.describe_signature(param, 'markType', env, symbol=symbol)
1975            paramlist += param
1976        signode += paramlist
1977
1978        def _add_anno(signode: TextElement, text: str) -> None:
1979            signode += nodes.Text(' ')
1980            signode += addnodes.desc_annotation(text, text)
1981
1982        def _add_text(signode: TextElement, text: str) -> None:
1983            signode += nodes.Text(' ' + text)
1984
1985        if self.volatile:
1986            _add_anno(signode, 'volatile')
1987        if self.const:
1988            _add_anno(signode, 'const')
1989        if self.refQual:
1990            _add_text(signode, self.refQual)
1991        if self.exceptionSpec:
1992            signode += nodes.Text(' ')
1993            self.exceptionSpec.describe_signature(signode, mode, env, symbol)
1994        if self.trailingReturn:
1995            signode += nodes.Text(' -> ')
1996            self.trailingReturn.describe_signature(signode, mode, env, symbol)
1997        if self.final:
1998            _add_anno(signode, 'final')
1999        if self.override:
2000            _add_anno(signode, 'override')
2001        for attr in self.attrs:
2002            signode += nodes.Text(' ')
2003            attr.describe_signature(signode)
2004        if self.initializer:
2005            _add_text(signode, '= ' + str(self.initializer))
2006
2007
2008class ASTDeclSpecsSimple(ASTBase):
2009    def __init__(self, storage: str, threadLocal: bool, inline: bool, virtual: bool,
2010                 explicit: bool, constexpr: bool, volatile: bool, const: bool,
2011                 friend: bool, attrs: List[ASTAttribute]) -> None:
2012        self.storage = storage
2013        self.threadLocal = threadLocal
2014        self.inline = inline
2015        self.virtual = virtual
2016        self.explicit = explicit
2017        self.constexpr = constexpr
2018        self.volatile = volatile
2019        self.const = const
2020        self.friend = friend
2021        self.attrs = attrs
2022
2023    def mergeWith(self, other: "ASTDeclSpecsSimple") -> "ASTDeclSpecsSimple":
2024        if not other:
2025            return self
2026        return ASTDeclSpecsSimple(self.storage or other.storage,
2027                                  self.threadLocal or other.threadLocal,
2028                                  self.inline or other.inline,
2029                                  self.virtual or other.virtual,
2030                                  self.explicit or other.explicit,
2031                                  self.constexpr or other.constexpr,
2032                                  self.volatile or other.volatile,
2033                                  self.const or other.const,
2034                                  self.friend or other.friend,
2035                                  self.attrs + other.attrs)
2036
2037    def _stringify(self, transform: StringifyTransform) -> str:
2038        res = []  # type: List[str]
2039        res.extend(transform(attr) for attr in self.attrs)
2040        if self.storage:
2041            res.append(self.storage)
2042        if self.threadLocal:
2043            res.append('thread_local')
2044        if self.inline:
2045            res.append('inline')
2046        if self.friend:
2047            res.append('friend')
2048        if self.virtual:
2049            res.append('virtual')
2050        if self.explicit:
2051            res.append('explicit')
2052        if self.constexpr:
2053            res.append('constexpr')
2054        if self.volatile:
2055            res.append('volatile')
2056        if self.const:
2057            res.append('const')
2058        return ' '.join(res)
2059
2060    def describe_signature(self, signode: TextElement) -> None:
2061        addSpace = False
2062        for attr in self.attrs:
2063            if addSpace:
2064                signode += nodes.Text(' ')
2065            addSpace = True
2066            attr.describe_signature(signode)
2067
2068        def _add(signode: TextElement, text: str) -> bool:
2069            if addSpace:
2070                signode += nodes.Text(' ')
2071            signode += addnodes.desc_annotation(text, text)
2072            return True
2073
2074        if self.storage:
2075            addSpace = _add(signode, self.storage)
2076        if self.threadLocal:
2077            addSpace = _add(signode, 'thread_local')
2078        if self.inline:
2079            addSpace = _add(signode, 'inline')
2080        if self.friend:
2081            addSpace = _add(signode, 'friend')
2082        if self.virtual:
2083            addSpace = _add(signode, 'virtual')
2084        if self.explicit:
2085            addSpace = _add(signode, 'explicit')
2086        if self.constexpr:
2087            addSpace = _add(signode, 'constexpr')
2088        if self.volatile:
2089            addSpace = _add(signode, 'volatile')
2090        if self.const:
2091            addSpace = _add(signode, 'const')
2092
2093
2094class ASTDeclSpecs(ASTBase):
2095    def __init__(self, outer: str,
2096                 leftSpecs: ASTDeclSpecsSimple, rightSpecs: ASTDeclSpecsSimple,
2097                 trailing: ASTTrailingTypeSpec) -> None:
2098        # leftSpecs and rightSpecs are used for output
2099        # allSpecs are used for id generation
2100        self.outer = outer
2101        self.leftSpecs = leftSpecs
2102        self.rightSpecs = rightSpecs
2103        self.allSpecs = self.leftSpecs.mergeWith(self.rightSpecs)
2104        self.trailingTypeSpec = trailing
2105
2106    def get_id(self, version: int) -> str:
2107        if version == 1:
2108            res = []
2109            res.append(self.trailingTypeSpec.get_id(version))
2110            if self.allSpecs.volatile:
2111                res.append('V')
2112            if self.allSpecs.const:
2113                res.append('C')
2114            return ''.join(res)
2115        res = []
2116        if self.allSpecs.volatile:
2117            res.append('V')
2118        if self.allSpecs.const:
2119            res.append('K')
2120        if self.trailingTypeSpec is not None:
2121            res.append(self.trailingTypeSpec.get_id(version))
2122        return ''.join(res)
2123
2124    def _stringify(self, transform: StringifyTransform) -> str:
2125        res = []  # type: List[str]
2126        l = transform(self.leftSpecs)
2127        if len(l) > 0:
2128            res.append(l)
2129        if self.trailingTypeSpec:
2130            if len(res) > 0:
2131                res.append(" ")
2132            res.append(transform(self.trailingTypeSpec))
2133            r = str(self.rightSpecs)
2134            if len(r) > 0:
2135                if len(res) > 0:
2136                    res.append(" ")
2137                res.append(r)
2138        return "".join(res)
2139
2140    def describe_signature(self, signode: TextElement, mode: str,
2141                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2142        verify_description_mode(mode)
2143        numChildren = len(signode)
2144        self.leftSpecs.describe_signature(signode)
2145        addSpace = len(signode) != numChildren
2146
2147        if self.trailingTypeSpec:
2148            if addSpace:
2149                signode += nodes.Text(' ')
2150            numChildren = len(signode)
2151            self.trailingTypeSpec.describe_signature(signode, mode, env,
2152                                                     symbol=symbol)
2153            addSpace = len(signode) != numChildren
2154
2155            if len(str(self.rightSpecs)) > 0:
2156                if addSpace:
2157                    signode += nodes.Text(' ')
2158                self.rightSpecs.describe_signature(signode)
2159
2160
2161# Declarator
2162################################################################################
2163
2164class ASTArray(ASTBase):
2165    def __init__(self, size: ASTExpression):
2166        self.size = size
2167
2168    def _stringify(self, transform: StringifyTransform) -> str:
2169        if self.size:
2170            return '[' + transform(self.size) + ']'
2171        else:
2172            return '[]'
2173
2174    def get_id(self, version: int) -> str:
2175        if version == 1:
2176            return 'A'
2177        if version == 2:
2178            if self.size:
2179                return 'A' + str(self.size) + '_'
2180            else:
2181                return 'A_'
2182        if self.size:
2183            return 'A' + self.size.get_id(version) + '_'
2184        else:
2185            return 'A_'
2186
2187    def describe_signature(self, signode: TextElement, mode: str,
2188                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2189        verify_description_mode(mode)
2190        signode.append(nodes.Text("["))
2191        if self.size:
2192            self.size.describe_signature(signode, mode, env, symbol)
2193        signode.append(nodes.Text("]"))
2194
2195
2196class ASTDeclarator(ASTBase):
2197    @property
2198    def name(self) -> ASTNestedName:
2199        raise NotImplementedError(repr(self))
2200
2201    @property
2202    def isPack(self) -> bool:
2203        raise NotImplementedError(repr(self))
2204
2205    @property
2206    def function_params(self) -> List[ASTFunctionParameter]:
2207        raise NotImplementedError(repr(self))
2208
2209    @property
2210    def trailingReturn(self) -> "ASTType":
2211        raise NotImplementedError(repr(self))
2212
2213    def require_space_after_declSpecs(self) -> bool:
2214        raise NotImplementedError(repr(self))
2215
2216    def get_modifiers_id(self, version: int) -> str:
2217        raise NotImplementedError(repr(self))
2218
2219    def get_param_id(self, version: int) -> str:
2220        raise NotImplementedError(repr(self))
2221
2222    def get_ptr_suffix_id(self, version: int) -> str:
2223        raise NotImplementedError(repr(self))
2224
2225    def get_type_id(self, version: int, returnTypeId: str) -> str:
2226        raise NotImplementedError(repr(self))
2227
2228    def is_function_type(self) -> bool:
2229        raise NotImplementedError(repr(self))
2230
2231    def describe_signature(self, signode: TextElement, mode: str,
2232                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2233        raise NotImplementedError(repr(self))
2234
2235
2236class ASTDeclaratorNameParamQual(ASTDeclarator):
2237    def __init__(self, declId: ASTNestedName,
2238                 arrayOps: List[ASTArray],
2239                 paramQual: ASTParametersQualifiers) -> None:
2240        self.declId = declId
2241        self.arrayOps = arrayOps
2242        self.paramQual = paramQual
2243
2244    @property
2245    def name(self) -> ASTNestedName:
2246        return self.declId
2247
2248    @property
2249    def isPack(self) -> bool:
2250        return False
2251
2252    @property
2253    def function_params(self) -> List[ASTFunctionParameter]:
2254        return self.paramQual.function_params
2255
2256    @property
2257    def trailingReturn(self) -> "ASTType":
2258        return self.paramQual.trailingReturn
2259
2260    # only the modifiers for a function, e.g.,
2261    def get_modifiers_id(self, version: int) -> str:
2262        # cv-qualifiers
2263        if self.paramQual:
2264            return self.paramQual.get_modifiers_id(version)
2265        raise Exception("This should only be called on a function: %s" % self)
2266
2267    def get_param_id(self, version: int) -> str:  # only the parameters (if any)
2268        if self.paramQual:
2269            return self.paramQual.get_param_id(version)
2270        else:
2271            return ''
2272
2273    def get_ptr_suffix_id(self, version: int) -> str:  # only the array specifiers
2274        return ''.join(a.get_id(version) for a in self.arrayOps)
2275
2276    def get_type_id(self, version: int, returnTypeId: str) -> str:
2277        assert version >= 2
2278        res = []
2279        # TOOD: can we actually have both array ops and paramQual?
2280        res.append(self.get_ptr_suffix_id(version))
2281        if self.paramQual:
2282            res.append(self.get_modifiers_id(version))
2283            res.append('F')
2284            res.append(returnTypeId)
2285            res.append(self.get_param_id(version))
2286            res.append('E')
2287        else:
2288            res.append(returnTypeId)
2289        return ''.join(res)
2290
2291    # ------------------------------------------------------------------------
2292
2293    def require_space_after_declSpecs(self) -> bool:
2294        return self.declId is not None
2295
2296    def is_function_type(self) -> bool:
2297        return self.paramQual is not None
2298
2299    def _stringify(self, transform: StringifyTransform) -> str:
2300        res = []
2301        if self.declId:
2302            res.append(transform(self.declId))
2303        for op in self.arrayOps:
2304            res.append(transform(op))
2305        if self.paramQual:
2306            res.append(transform(self.paramQual))
2307        return ''.join(res)
2308
2309    def describe_signature(self, signode: TextElement, mode: str,
2310                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2311        verify_description_mode(mode)
2312        if self.declId:
2313            self.declId.describe_signature(signode, mode, env, symbol)
2314        for op in self.arrayOps:
2315            op.describe_signature(signode, mode, env, symbol)
2316        if self.paramQual:
2317            self.paramQual.describe_signature(signode, mode, env, symbol)
2318
2319
2320class ASTDeclaratorNameBitField(ASTDeclarator):
2321    def __init__(self, declId: ASTNestedName, size: ASTExpression):
2322        self.declId = declId
2323        self.size = size
2324
2325    @property
2326    def name(self) -> ASTNestedName:
2327        return self.declId
2328
2329    def get_param_id(self, version: int) -> str:  # only the parameters (if any)
2330        return ''
2331
2332    def get_ptr_suffix_id(self, version: int) -> str:  # only the array specifiers
2333        return ''
2334
2335    # ------------------------------------------------------------------------
2336
2337    def require_space_after_declSpecs(self) -> bool:
2338        return self.declId is not None
2339
2340    def is_function_type(self) -> bool:
2341        return False
2342
2343    def _stringify(self, transform: StringifyTransform) -> str:
2344        res = []
2345        if self.declId:
2346            res.append(transform(self.declId))
2347        res.append(" : ")
2348        res.append(transform(self.size))
2349        return ''.join(res)
2350
2351    def describe_signature(self, signode: TextElement, mode: str,
2352                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2353        verify_description_mode(mode)
2354        if self.declId:
2355            self.declId.describe_signature(signode, mode, env, symbol)
2356        signode.append(nodes.Text(' : ', ' : '))
2357        self.size.describe_signature(signode, mode, env, symbol)
2358
2359
2360class ASTDeclaratorPtr(ASTDeclarator):
2361    def __init__(self, next: ASTDeclarator, volatile: bool, const: bool,
2362                 attrs: List[ASTAttribute]) -> None:
2363        assert next
2364        self.next = next
2365        self.volatile = volatile
2366        self.const = const
2367        self.attrs = attrs
2368
2369    @property
2370    def name(self) -> ASTNestedName:
2371        return self.next.name
2372
2373    @property
2374    def function_params(self) -> List[ASTFunctionParameter]:
2375        return self.next.function_params
2376
2377    @property
2378    def trailingReturn(self) -> "ASTType":
2379        return self.next.trailingReturn
2380
2381    def require_space_after_declSpecs(self) -> bool:
2382        return self.next.require_space_after_declSpecs()
2383
2384    def _stringify(self, transform: StringifyTransform) -> str:
2385        res = ['*']
2386        for a in self.attrs:
2387            res.append(transform(a))
2388        if len(self.attrs) > 0 and (self.volatile or self.const):
2389            res.append(' ')
2390        if self.volatile:
2391            res.append('volatile')
2392        if self.const:
2393            if self.volatile:
2394                res.append(' ')
2395            res.append('const')
2396        if self.const or self.volatile or len(self.attrs) > 0:
2397            if self.next.require_space_after_declSpecs():
2398                res.append(' ')
2399        res.append(transform(self.next))
2400        return ''.join(res)
2401
2402    def get_modifiers_id(self, version: int) -> str:
2403        return self.next.get_modifiers_id(version)
2404
2405    def get_param_id(self, version: int) -> str:
2406        return self.next.get_param_id(version)
2407
2408    def get_ptr_suffix_id(self, version: int) -> str:
2409        if version == 1:
2410            res = ['P']
2411            if self.volatile:
2412                res.append('V')
2413            if self.const:
2414                res.append('C')
2415            res.append(self.next.get_ptr_suffix_id(version))
2416            return ''.join(res)
2417
2418        res = [self.next.get_ptr_suffix_id(version)]
2419        res.append('P')
2420        if self.volatile:
2421            res.append('V')
2422        if self.const:
2423            res.append('C')
2424        return ''.join(res)
2425
2426    def get_type_id(self, version: int, returnTypeId: str) -> str:
2427        # ReturnType *next, so we are part of the return type of 'next
2428        res = ['P']
2429        if self.volatile:
2430            res.append('V')
2431        if self.const:
2432            res.append('C')
2433        res.append(returnTypeId)
2434        return self.next.get_type_id(version, returnTypeId=''.join(res))
2435
2436    def is_function_type(self) -> bool:
2437        return self.next.is_function_type()
2438
2439    def describe_signature(self, signode: TextElement, mode: str,
2440                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2441        verify_description_mode(mode)
2442        signode += nodes.Text("*")
2443        for a in self.attrs:
2444            a.describe_signature(signode)
2445        if len(self.attrs) > 0 and (self.volatile or self.const):
2446            signode += nodes.Text(' ')
2447
2448        def _add_anno(signode: TextElement, text: str) -> None:
2449            signode += addnodes.desc_annotation(text, text)
2450        if self.volatile:
2451            _add_anno(signode, 'volatile')
2452        if self.const:
2453            if self.volatile:
2454                signode += nodes.Text(' ')
2455            _add_anno(signode, 'const')
2456        if self.const or self.volatile or len(self.attrs) > 0:
2457            if self.next.require_space_after_declSpecs():
2458                signode += nodes.Text(' ')
2459        self.next.describe_signature(signode, mode, env, symbol)
2460
2461
2462class ASTDeclaratorRef(ASTDeclarator):
2463    def __init__(self, next: ASTDeclarator, attrs: List[ASTAttribute]) -> None:
2464        assert next
2465        self.next = next
2466        self.attrs = attrs
2467
2468    @property
2469    def name(self) -> ASTNestedName:
2470        return self.next.name
2471
2472    @property
2473    def isPack(self) -> bool:
2474        return True
2475
2476    @property
2477    def function_params(self) -> List[ASTFunctionParameter]:
2478        return self.next.function_params
2479
2480    @property
2481    def trailingReturn(self) -> "ASTType":
2482        return self.next.trailingReturn
2483
2484    def require_space_after_declSpecs(self) -> bool:
2485        return self.next.require_space_after_declSpecs()
2486
2487    def _stringify(self, transform: StringifyTransform) -> str:
2488        res = ['&']
2489        for a in self.attrs:
2490            res.append(transform(a))
2491        if len(self.attrs) > 0 and self.next.require_space_after_declSpecs():
2492            res.append(' ')
2493        res.append(transform(self.next))
2494        return ''.join(res)
2495
2496    def get_modifiers_id(self, version: int) -> str:
2497        return self.next.get_modifiers_id(version)
2498
2499    def get_param_id(self, version: int) -> str:  # only the parameters (if any)
2500        return self.next.get_param_id(version)
2501
2502    def get_ptr_suffix_id(self, version: int) -> str:
2503        if version == 1:
2504            return 'R' + self.next.get_ptr_suffix_id(version)
2505        else:
2506            return self.next.get_ptr_suffix_id(version) + 'R'
2507
2508    def get_type_id(self, version: int, returnTypeId: str) -> str:
2509        assert version >= 2
2510        # ReturnType &next, so we are part of the return type of 'next
2511        return self.next.get_type_id(version, returnTypeId='R' + returnTypeId)
2512
2513    def is_function_type(self) -> bool:
2514        return self.next.is_function_type()
2515
2516    def describe_signature(self, signode: TextElement, mode: str,
2517                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2518        verify_description_mode(mode)
2519        signode += nodes.Text("&")
2520        for a in self.attrs:
2521            a.describe_signature(signode)
2522        if len(self.attrs) > 0 and self.next.require_space_after_declSpecs():
2523            signode += nodes.Text(' ')
2524        self.next.describe_signature(signode, mode, env, symbol)
2525
2526
2527class ASTDeclaratorParamPack(ASTDeclarator):
2528    def __init__(self, next: ASTDeclarator) -> None:
2529        assert next
2530        self.next = next
2531
2532    @property
2533    def name(self) -> ASTNestedName:
2534        return self.next.name
2535
2536    @property
2537    def function_params(self) -> List[ASTFunctionParameter]:
2538        return self.next.function_params
2539
2540    @property
2541    def trailingReturn(self) -> "ASTType":
2542        return self.next.trailingReturn
2543
2544    def require_space_after_declSpecs(self) -> bool:
2545        return False
2546
2547    def _stringify(self, transform: StringifyTransform) -> str:
2548        res = transform(self.next)
2549        if self.next.name:
2550            res = ' ' + res
2551        return '...' + res
2552
2553    def get_modifiers_id(self, version: int) -> str:
2554        return self.next.get_modifiers_id(version)
2555
2556    def get_param_id(self, version: int) -> str:  # only the parameters (if any)
2557        return self.next.get_param_id(version)
2558
2559    def get_ptr_suffix_id(self, version: int) -> str:
2560        if version == 1:
2561            return 'Dp' + self.next.get_ptr_suffix_id(version)
2562        else:
2563            return self.next.get_ptr_suffix_id(version) + 'Dp'
2564
2565    def get_type_id(self, version: int, returnTypeId: str) -> str:
2566        assert version >= 2
2567        # ReturnType... next, so we are part of the return type of 'next
2568        return self.next.get_type_id(version, returnTypeId='Dp' + returnTypeId)
2569
2570    def is_function_type(self) -> bool:
2571        return self.next.is_function_type()
2572
2573    def describe_signature(self, signode: TextElement, mode: str,
2574                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2575        verify_description_mode(mode)
2576        signode += nodes.Text("...")
2577        if self.next.name:
2578            signode += nodes.Text(' ')
2579        self.next.describe_signature(signode, mode, env, symbol)
2580
2581
2582class ASTDeclaratorMemPtr(ASTDeclarator):
2583    def __init__(self, className: ASTNestedName,
2584                 const: bool, volatile: bool, next: ASTDeclarator) -> None:
2585        assert className
2586        assert next
2587        self.className = className
2588        self.const = const
2589        self.volatile = volatile
2590        self.next = next
2591
2592    @property
2593    def name(self) -> ASTNestedName:
2594        return self.next.name
2595
2596    @property
2597    def function_params(self) -> List[ASTFunctionParameter]:
2598        return self.next.function_params
2599
2600    @property
2601    def trailingReturn(self) -> "ASTType":
2602        return self.next.trailingReturn
2603
2604    def require_space_after_declSpecs(self) -> bool:
2605        return True
2606
2607    def _stringify(self, transform: StringifyTransform) -> str:
2608        res = []
2609        res.append(transform(self.className))
2610        res.append('::*')
2611        if self.volatile:
2612            res.append('volatile')
2613        if self.const:
2614            if self.volatile:
2615                res.append(' ')
2616            res.append('const')
2617        if self.next.require_space_after_declSpecs():
2618            res.append(' ')
2619        res.append(transform(self.next))
2620        return ''.join(res)
2621
2622    def get_modifiers_id(self, version: int) -> str:
2623        if version == 1:
2624            raise NoOldIdError()
2625        else:
2626            return self.next.get_modifiers_id(version)
2627
2628    def get_param_id(self, version: int) -> str:  # only the parameters (if any)
2629        if version == 1:
2630            raise NoOldIdError()
2631        else:
2632            return self.next.get_param_id(version)
2633
2634    def get_ptr_suffix_id(self, version: int) -> str:
2635        if version == 1:
2636            raise NoOldIdError()
2637        else:
2638            raise NotImplementedError()
2639            return self.next.get_ptr_suffix_id(version) + 'Dp'
2640
2641    def get_type_id(self, version: int, returnTypeId: str) -> str:
2642        assert version >= 2
2643        # ReturnType name::* next, so we are part of the return type of next
2644        nextReturnTypeId = ''
2645        if self.volatile:
2646            nextReturnTypeId += 'V'
2647        if self.const:
2648            nextReturnTypeId += 'K'
2649        nextReturnTypeId += 'M'
2650        nextReturnTypeId += self.className.get_id(version)
2651        nextReturnTypeId += returnTypeId
2652        return self.next.get_type_id(version, nextReturnTypeId)
2653
2654    def is_function_type(self) -> bool:
2655        return self.next.is_function_type()
2656
2657    def describe_signature(self, signode: TextElement, mode: str,
2658                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2659        verify_description_mode(mode)
2660        self.className.describe_signature(signode, mode, env, symbol)
2661        signode += nodes.Text('::*')
2662
2663        def _add_anno(signode: TextElement, text: str) -> None:
2664            signode += addnodes.desc_annotation(text, text)
2665        if self.volatile:
2666            _add_anno(signode, 'volatile')
2667        if self.const:
2668            if self.volatile:
2669                signode += nodes.Text(' ')
2670            _add_anno(signode, 'const')
2671        if self.next.require_space_after_declSpecs():
2672            signode += nodes.Text(' ')
2673        self.next.describe_signature(signode, mode, env, symbol)
2674
2675
2676class ASTDeclaratorParen(ASTDeclarator):
2677    def __init__(self, inner: ASTDeclarator, next: ASTDeclarator) -> None:
2678        assert inner
2679        assert next
2680        self.inner = inner
2681        self.next = next
2682        # TODO: we assume the name, params, and qualifiers are in inner
2683
2684    @property
2685    def name(self) -> ASTNestedName:
2686        return self.inner.name
2687
2688    @property
2689    def function_params(self) -> List[ASTFunctionParameter]:
2690        return self.inner.function_params
2691
2692    @property
2693    def trailingReturn(self) -> "ASTType":
2694        return self.inner.trailingReturn
2695
2696    def require_space_after_declSpecs(self) -> bool:
2697        return True
2698
2699    def _stringify(self, transform: StringifyTransform) -> str:
2700        res = ['(']
2701        res.append(transform(self.inner))
2702        res.append(')')
2703        res.append(transform(self.next))
2704        return ''.join(res)
2705
2706    def get_modifiers_id(self, version: int) -> str:
2707        return self.inner.get_modifiers_id(version)
2708
2709    def get_param_id(self, version: int) -> str:  # only the parameters (if any)
2710        return self.inner.get_param_id(version)
2711
2712    def get_ptr_suffix_id(self, version: int) -> str:
2713        if version == 1:
2714            raise NoOldIdError()  # TODO: was this implemented before?
2715            return self.next.get_ptr_suffix_id(version) + \
2716                self.inner.get_ptr_suffix_id(version)
2717        else:
2718            return self.inner.get_ptr_suffix_id(version) + \
2719                self.next.get_ptr_suffix_id(version)
2720
2721    def get_type_id(self, version: int, returnTypeId: str) -> str:
2722        assert version >= 2
2723        # ReturnType (inner)next, so 'inner' returns everything outside
2724        nextId = self.next.get_type_id(version, returnTypeId)
2725        return self.inner.get_type_id(version, returnTypeId=nextId)
2726
2727    def is_function_type(self) -> bool:
2728        return self.inner.is_function_type()
2729
2730    def describe_signature(self, signode: TextElement, mode: str,
2731                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2732        verify_description_mode(mode)
2733        signode += nodes.Text('(')
2734        self.inner.describe_signature(signode, mode, env, symbol)
2735        signode += nodes.Text(')')
2736        self.next.describe_signature(signode, "noneIsName", env, symbol)
2737
2738
2739# Type and initializer stuff
2740##############################################################################################
2741
2742class ASTPackExpansionExpr(ASTExpression):
2743    def __init__(self, expr: Union[ASTExpression, ASTBracedInitList]):
2744        self.expr = expr
2745
2746    def _stringify(self, transform: StringifyTransform) -> str:
2747        return transform(self.expr) + '...'
2748
2749    def get_id(self, version: int) -> str:
2750        id = self.expr.get_id(version)
2751        return 'sp' + id
2752
2753    def describe_signature(self, signode: TextElement, mode: str,
2754                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2755        self.expr.describe_signature(signode, mode, env, symbol)
2756        signode += nodes.Text('...')
2757
2758
2759class ASTParenExprList(ASTBaseParenExprList):
2760    def __init__(self, exprs: List[Union[ASTExpression, ASTBracedInitList]]) -> None:
2761        self.exprs = exprs
2762
2763    def get_id(self, version: int) -> str:
2764        return "pi%sE" % ''.join(e.get_id(version) for e in self.exprs)
2765
2766    def _stringify(self, transform: StringifyTransform) -> str:
2767        exprs = [transform(e) for e in self.exprs]
2768        return '(%s)' % ', '.join(exprs)
2769
2770    def describe_signature(self, signode: TextElement, mode: str,
2771                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2772        verify_description_mode(mode)
2773        signode.append(nodes.Text('('))
2774        first = True
2775        for e in self.exprs:
2776            if not first:
2777                signode.append(nodes.Text(', '))
2778            else:
2779                first = False
2780            e.describe_signature(signode, mode, env, symbol)
2781        signode.append(nodes.Text(')'))
2782
2783
2784class ASTInitializer(ASTBase):
2785    def __init__(self, value: Union[ASTExpression, ASTBracedInitList],
2786                 hasAssign: bool = True) -> None:
2787        self.value = value
2788        self.hasAssign = hasAssign
2789
2790    def _stringify(self, transform: StringifyTransform) -> str:
2791        val = transform(self.value)
2792        if self.hasAssign:
2793            return ' = ' + val
2794        else:
2795            return val
2796
2797    def describe_signature(self, signode: TextElement, mode: str,
2798                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2799        verify_description_mode(mode)
2800        if self.hasAssign:
2801            signode.append(nodes.Text(' = '))
2802        self.value.describe_signature(signode, 'markType', env, symbol)
2803
2804
2805class ASTType(ASTBase):
2806    def __init__(self, declSpecs: ASTDeclSpecs, decl: ASTDeclarator) -> None:
2807        assert declSpecs
2808        assert decl
2809        self.declSpecs = declSpecs
2810        self.decl = decl
2811
2812    @property
2813    def name(self) -> ASTNestedName:
2814        return self.decl.name
2815
2816    @property
2817    def isPack(self) -> bool:
2818        return self.decl.isPack
2819
2820    @property
2821    def function_params(self) -> List[ASTFunctionParameter]:
2822        return self.decl.function_params
2823
2824    @property
2825    def trailingReturn(self) -> "ASTType":
2826        return self.decl.trailingReturn
2827
2828    def get_id(self, version: int, objectType: str = None,
2829               symbol: "Symbol" = None) -> str:
2830        if version == 1:
2831            res = []
2832            if objectType:  # needs the name
2833                if objectType == 'function':  # also modifiers
2834                    res.append(symbol.get_full_nested_name().get_id(version))
2835                    res.append(self.decl.get_param_id(version))
2836                    res.append(self.decl.get_modifiers_id(version))
2837                    if (self.declSpecs.leftSpecs.constexpr or
2838                            (self.declSpecs.rightSpecs and
2839                             self.declSpecs.rightSpecs.constexpr)):
2840                        res.append('CE')
2841                elif objectType == 'type':  # just the name
2842                    res.append(symbol.get_full_nested_name().get_id(version))
2843                else:
2844                    print(objectType)
2845                    assert False
2846            else:  # only type encoding
2847                if self.decl.is_function_type():
2848                    raise NoOldIdError()
2849                res.append(self.declSpecs.get_id(version))
2850                res.append(self.decl.get_ptr_suffix_id(version))
2851                res.append(self.decl.get_param_id(version))
2852            return ''.join(res)
2853        # other versions
2854        res = []
2855        if objectType:  # needs the name
2856            if objectType == 'function':  # also modifiers
2857                modifiers = self.decl.get_modifiers_id(version)
2858                res.append(symbol.get_full_nested_name().get_id(version, modifiers))
2859                if version >= 4:
2860                    # with templates we need to mangle the return type in as well
2861                    templ = symbol.declaration.templatePrefix
2862                    if templ is not None:
2863                        typeId = self.decl.get_ptr_suffix_id(version)
2864                        if self.trailingReturn:
2865                            returnTypeId = self.trailingReturn.get_id(version)
2866                        else:
2867                            returnTypeId = self.declSpecs.get_id(version)
2868                        res.append(typeId)
2869                        res.append(returnTypeId)
2870                res.append(self.decl.get_param_id(version))
2871            elif objectType == 'type':  # just the name
2872                res.append(symbol.get_full_nested_name().get_id(version))
2873            else:
2874                print(objectType)
2875                assert False
2876        else:  # only type encoding
2877            # the 'returnType' of a non-function type is simply just the last
2878            # type, i.e., for 'int*' it is 'int'
2879            returnTypeId = self.declSpecs.get_id(version)
2880            typeId = self.decl.get_type_id(version, returnTypeId)
2881            res.append(typeId)
2882        return ''.join(res)
2883
2884    def _stringify(self, transform: StringifyTransform) -> str:
2885        res = []
2886        declSpecs = transform(self.declSpecs)
2887        res.append(declSpecs)
2888        if self.decl.require_space_after_declSpecs() and len(declSpecs) > 0:
2889            res.append(' ')
2890        res.append(transform(self.decl))
2891        return ''.join(res)
2892
2893    def get_type_declaration_prefix(self) -> str:
2894        if self.declSpecs.trailingTypeSpec:
2895            return 'typedef'
2896        else:
2897            return 'type'
2898
2899    def describe_signature(self, signode: TextElement, mode: str,
2900                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2901        verify_description_mode(mode)
2902        self.declSpecs.describe_signature(signode, 'markType', env, symbol)
2903        if (self.decl.require_space_after_declSpecs() and
2904                len(str(self.declSpecs)) > 0):
2905            signode += nodes.Text(' ')
2906        # for parameters that don't really declare new names we get 'markType',
2907        # this should not be propagated, but be 'noneIsName'.
2908        if mode == 'markType':
2909            mode = 'noneIsName'
2910        self.decl.describe_signature(signode, mode, env, symbol)
2911
2912
2913class ASTTemplateParamConstrainedTypeWithInit(ASTBase):
2914    def __init__(self, type: ASTType, init: ASTType) -> None:
2915        assert type
2916        self.type = type
2917        self.init = init
2918
2919    @property
2920    def name(self) -> ASTNestedName:
2921        return self.type.name
2922
2923    @property
2924    def isPack(self) -> bool:
2925        return self.type.isPack
2926
2927    def get_id(self, version: int, objectType: str = None, symbol: "Symbol" = None) -> str:
2928        # this is not part of the normal name mangling in C++
2929        assert version >= 2
2930        if symbol:
2931            # the anchor will be our parent
2932            return symbol.parent.declaration.get_id(version, prefixed=False)
2933        else:
2934            return self.type.get_id(version)
2935
2936    def _stringify(self, transform: StringifyTransform) -> str:
2937        res = transform(self.type)
2938        if self.init:
2939            res += " = "
2940            res += transform(self.init)
2941        return res
2942
2943    def describe_signature(self, signode: TextElement, mode: str,
2944                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2945        self.type.describe_signature(signode, mode, env, symbol)
2946        if self.init:
2947            signode += nodes.Text(" = ")
2948            self.init.describe_signature(signode, mode, env, symbol)
2949
2950
2951class ASTTypeWithInit(ASTBase):
2952    def __init__(self, type: ASTType, init: ASTInitializer) -> None:
2953        self.type = type
2954        self.init = init
2955
2956    @property
2957    def name(self) -> ASTNestedName:
2958        return self.type.name
2959
2960    @property
2961    def isPack(self) -> bool:
2962        return self.type.isPack
2963
2964    def get_id(self, version: int, objectType: str = None,
2965               symbol: "Symbol" = None) -> str:
2966        if objectType != 'member':
2967            return self.type.get_id(version, objectType)
2968        if version == 1:
2969            return (symbol.get_full_nested_name().get_id(version) + '__' +
2970                    self.type.get_id(version))
2971        return symbol.get_full_nested_name().get_id(version)
2972
2973    def _stringify(self, transform: StringifyTransform) -> str:
2974        res = []
2975        res.append(transform(self.type))
2976        if self.init:
2977            res.append(transform(self.init))
2978        return ''.join(res)
2979
2980    def describe_signature(self, signode: TextElement, mode: str,
2981                           env: "BuildEnvironment", symbol: "Symbol") -> None:
2982        verify_description_mode(mode)
2983        self.type.describe_signature(signode, mode, env, symbol)
2984        if self.init:
2985            self.init.describe_signature(signode, mode, env, symbol)
2986
2987
2988class ASTTypeUsing(ASTBase):
2989    def __init__(self, name: ASTNestedName, type: ASTType) -> None:
2990        self.name = name
2991        self.type = type
2992
2993    def get_id(self, version: int, objectType: str = None,
2994               symbol: "Symbol" = None) -> str:
2995        if version == 1:
2996            raise NoOldIdError()
2997        return symbol.get_full_nested_name().get_id(version)
2998
2999    def _stringify(self, transform: StringifyTransform) -> str:
3000        res = []
3001        res.append(transform(self.name))
3002        if self.type:
3003            res.append(' = ')
3004            res.append(transform(self.type))
3005        return ''.join(res)
3006
3007    def get_type_declaration_prefix(self) -> str:
3008        return 'using'
3009
3010    def describe_signature(self, signode: TextElement, mode: str,
3011                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3012        verify_description_mode(mode)
3013        self.name.describe_signature(signode, mode, env, symbol=symbol)
3014        if self.type:
3015            signode += nodes.Text(' = ')
3016            self.type.describe_signature(signode, 'markType', env, symbol=symbol)
3017
3018
3019# Other declarations
3020##############################################################################################
3021
3022class ASTConcept(ASTBase):
3023    def __init__(self, nestedName: ASTNestedName, initializer: ASTInitializer) -> None:
3024        self.nestedName = nestedName
3025        self.initializer = initializer
3026
3027    @property
3028    def name(self) -> ASTNestedName:
3029        return self.nestedName
3030
3031    def get_id(self, version: int, objectType: str = None,
3032               symbol: "Symbol" = None) -> str:
3033        if version == 1:
3034            raise NoOldIdError()
3035        return symbol.get_full_nested_name().get_id(version)
3036
3037    def _stringify(self, transform: StringifyTransform) -> str:
3038        res = transform(self.nestedName)
3039        if self.initializer:
3040            res += transform(self.initializer)
3041        return res
3042
3043    def describe_signature(self, signode: TextElement, mode: str,
3044                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3045        self.nestedName.describe_signature(signode, mode, env, symbol)
3046        if self.initializer:
3047            self.initializer.describe_signature(signode, mode, env, symbol)
3048
3049
3050class ASTBaseClass(ASTBase):
3051    def __init__(self, name: ASTNestedName, visibility: str,
3052                 virtual: bool, pack: bool) -> None:
3053        self.name = name
3054        self.visibility = visibility
3055        self.virtual = virtual
3056        self.pack = pack
3057
3058    def _stringify(self, transform: StringifyTransform) -> str:
3059        res = []
3060
3061        if self.visibility is not None:
3062            res.append(self.visibility)
3063            res.append(' ')
3064        if self.virtual:
3065            res.append('virtual ')
3066        res.append(transform(self.name))
3067        if self.pack:
3068            res.append('...')
3069        return ''.join(res)
3070
3071    def describe_signature(self, signode: TextElement, mode: str,
3072                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3073        verify_description_mode(mode)
3074        if self.visibility is not None:
3075            signode += addnodes.desc_annotation(self.visibility,
3076                                                self.visibility)
3077            signode += nodes.Text(' ')
3078        if self.virtual:
3079            signode += addnodes.desc_annotation('virtual', 'virtual')
3080            signode += nodes.Text(' ')
3081        self.name.describe_signature(signode, 'markType', env, symbol=symbol)
3082        if self.pack:
3083            signode += nodes.Text('...')
3084
3085
3086class ASTClass(ASTBase):
3087    def __init__(self, name: ASTNestedName, final: bool, bases: List[ASTBaseClass]) -> None:
3088        self.name = name
3089        self.final = final
3090        self.bases = bases
3091
3092    def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
3093        return symbol.get_full_nested_name().get_id(version)
3094
3095    def _stringify(self, transform: StringifyTransform) -> str:
3096        res = []
3097        res.append(transform(self.name))
3098        if self.final:
3099            res.append(' final')
3100        if len(self.bases) > 0:
3101            res.append(' : ')
3102            first = True
3103            for b in self.bases:
3104                if not first:
3105                    res.append(', ')
3106                first = False
3107                res.append(transform(b))
3108        return ''.join(res)
3109
3110    def describe_signature(self, signode: TextElement, mode: str,
3111                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3112        verify_description_mode(mode)
3113        self.name.describe_signature(signode, mode, env, symbol=symbol)
3114        if self.final:
3115            signode += nodes.Text(' ')
3116            signode += addnodes.desc_annotation('final', 'final')
3117        if len(self.bases) > 0:
3118            signode += nodes.Text(' : ')
3119            for b in self.bases:
3120                b.describe_signature(signode, mode, env, symbol=symbol)
3121                signode += nodes.Text(', ')
3122            signode.pop()
3123
3124
3125class ASTUnion(ASTBase):
3126    def __init__(self, name: ASTNestedName) -> None:
3127        self.name = name
3128
3129    def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
3130        if version == 1:
3131            raise NoOldIdError()
3132        return symbol.get_full_nested_name().get_id(version)
3133
3134    def _stringify(self, transform: StringifyTransform) -> str:
3135        return transform(self.name)
3136
3137    def describe_signature(self, signode: TextElement, mode: str,
3138                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3139        verify_description_mode(mode)
3140        self.name.describe_signature(signode, mode, env, symbol=symbol)
3141
3142
3143class ASTEnum(ASTBase):
3144    def __init__(self, name: ASTNestedName, scoped: str,
3145                 underlyingType: ASTType) -> None:
3146        self.name = name
3147        self.scoped = scoped
3148        self.underlyingType = underlyingType
3149
3150    def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
3151        if version == 1:
3152            raise NoOldIdError()
3153        return symbol.get_full_nested_name().get_id(version)
3154
3155    def _stringify(self, transform: StringifyTransform) -> str:
3156        res = []
3157        if self.scoped:
3158            res.append(self.scoped)
3159            res.append(' ')
3160        res.append(transform(self.name))
3161        if self.underlyingType:
3162            res.append(' : ')
3163            res.append(transform(self.underlyingType))
3164        return ''.join(res)
3165
3166    def describe_signature(self, signode: TextElement, mode: str,
3167                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3168        verify_description_mode(mode)
3169        # self.scoped has been done by the CPPEnumObject
3170        self.name.describe_signature(signode, mode, env, symbol=symbol)
3171        if self.underlyingType:
3172            signode += nodes.Text(' : ')
3173            self.underlyingType.describe_signature(signode, 'noneIsName',
3174                                                   env, symbol=symbol)
3175
3176
3177class ASTEnumerator(ASTBase):
3178    def __init__(self, name: ASTNestedName, init: ASTInitializer) -> None:
3179        self.name = name
3180        self.init = init
3181
3182    def get_id(self, version: int, objectType: str, symbol: "Symbol") -> str:
3183        if version == 1:
3184            raise NoOldIdError()
3185        return symbol.get_full_nested_name().get_id(version)
3186
3187    def _stringify(self, transform: StringifyTransform) -> str:
3188        res = []
3189        res.append(transform(self.name))
3190        if self.init:
3191            res.append(transform(self.init))
3192        return ''.join(res)
3193
3194    def describe_signature(self, signode: TextElement, mode: str,
3195                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3196        verify_description_mode(mode)
3197        self.name.describe_signature(signode, mode, env, symbol)
3198        if self.init:
3199            self.init.describe_signature(signode, 'markType', env, symbol)
3200
3201
3202################################################################################
3203# Templates
3204################################################################################
3205
3206# Parameters
3207################################################################################
3208
3209class ASTTemplateParam(ASTBase):
3210    def get_identifier(self) -> ASTIdentifier:
3211        raise NotImplementedError(repr(self))
3212
3213    def get_id(self, version: int) -> str:
3214        raise NotImplementedError(repr(self))
3215
3216    def describe_signature(self, parentNode: TextElement, mode: str,
3217                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3218        raise NotImplementedError(repr(self))
3219
3220
3221class ASTTemplateKeyParamPackIdDefault(ASTTemplateParam):
3222    def __init__(self, key: str, identifier: ASTIdentifier,
3223                 parameterPack: bool, default: ASTType) -> None:
3224        assert key
3225        if parameterPack:
3226            assert default is None
3227        self.key = key
3228        self.identifier = identifier
3229        self.parameterPack = parameterPack
3230        self.default = default
3231
3232    def get_identifier(self) -> ASTIdentifier:
3233        return self.identifier
3234
3235    def get_id(self, version: int) -> str:
3236        assert version >= 2
3237        # this is not part of the normal name mangling in C++
3238        res = []
3239        if self.parameterPack:
3240            res.append('Dp')
3241        else:
3242            res.append('0')  # we need to put something
3243        return ''.join(res)
3244
3245    def _stringify(self, transform: StringifyTransform) -> str:
3246        res = [self.key]
3247        if self.parameterPack:
3248            if self.identifier:
3249                res.append(' ')
3250            res.append('...')
3251        if self.identifier:
3252            if not self.parameterPack:
3253                res.append(' ')
3254            res.append(transform(self.identifier))
3255        if self.default:
3256            res.append(' = ')
3257            res.append(transform(self.default))
3258        return ''.join(res)
3259
3260    def describe_signature(self, signode: TextElement, mode: str,
3261                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3262        signode += nodes.Text(self.key)
3263        if self.parameterPack:
3264            if self.identifier:
3265                signode += nodes.Text(' ')
3266            signode += nodes.Text('...')
3267        if self.identifier:
3268            if not self.parameterPack:
3269                signode += nodes.Text(' ')
3270            self.identifier.describe_signature(signode, mode, env, '', '', symbol)
3271        if self.default:
3272            signode += nodes.Text(' = ')
3273            self.default.describe_signature(signode, 'markType', env, symbol)
3274
3275
3276class ASTTemplateParamType(ASTTemplateParam):
3277    def __init__(self, data: ASTTemplateKeyParamPackIdDefault) -> None:
3278        assert data
3279        self.data = data
3280
3281    @property
3282    def name(self) -> ASTNestedName:
3283        id = self.get_identifier()
3284        return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
3285
3286    @property
3287    def isPack(self) -> bool:
3288        return self.data.parameterPack
3289
3290    def get_identifier(self) -> ASTIdentifier:
3291        return self.data.get_identifier()
3292
3293    def get_id(self, version: int, objectType: str = None, symbol: "Symbol" = None) -> str:
3294        # this is not part of the normal name mangling in C++
3295        assert version >= 2
3296        if symbol:
3297            # the anchor will be our parent
3298            return symbol.parent.declaration.get_id(version, prefixed=False)
3299        else:
3300            return self.data.get_id(version)
3301
3302    def _stringify(self, transform: StringifyTransform) -> str:
3303        return transform(self.data)
3304
3305    def describe_signature(self, signode: TextElement, mode: str,
3306                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3307        self.data.describe_signature(signode, mode, env, symbol)
3308
3309
3310class ASTTemplateParamTemplateType(ASTTemplateParam):
3311    def __init__(self, nestedParams: "ASTTemplateParams",
3312                 data: ASTTemplateKeyParamPackIdDefault) -> None:
3313        assert nestedParams
3314        assert data
3315        self.nestedParams = nestedParams
3316        self.data = data
3317
3318    @property
3319    def name(self) -> ASTNestedName:
3320        id = self.get_identifier()
3321        return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
3322
3323    @property
3324    def isPack(self) -> bool:
3325        return self.data.parameterPack
3326
3327    def get_identifier(self) -> ASTIdentifier:
3328        return self.data.get_identifier()
3329
3330    def get_id(self, version: int, objectType: str = None, symbol: "Symbol" = None) -> str:
3331        assert version >= 2
3332        # this is not part of the normal name mangling in C++
3333        if symbol:
3334            # the anchor will be our parent
3335            return symbol.parent.declaration.get_id(version, prefixed=None)
3336        else:
3337            return self.nestedParams.get_id(version) + self.data.get_id(version)
3338
3339    def _stringify(self, transform: StringifyTransform) -> str:
3340        return transform(self.nestedParams) + transform(self.data)
3341
3342    def describe_signature(self, signode: TextElement, mode: str,
3343                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3344        self.nestedParams.describe_signature(signode, 'noneIsName', env, symbol)
3345        signode += nodes.Text(' ')
3346        self.data.describe_signature(signode, mode, env, symbol)
3347
3348
3349class ASTTemplateParamNonType(ASTTemplateParam):
3350    def __init__(self,
3351                 param: Union[ASTTypeWithInit,
3352                              ASTTemplateParamConstrainedTypeWithInit]) -> None:
3353        assert param
3354        self.param = param
3355
3356    @property
3357    def name(self) -> ASTNestedName:
3358        id = self.get_identifier()
3359        return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
3360
3361    @property
3362    def isPack(self) -> bool:
3363        return self.param.isPack
3364
3365    def get_identifier(self) -> ASTIdentifier:
3366        name = self.param.name
3367        if name:
3368            assert len(name.names) == 1
3369            assert name.names[0].identOrOp
3370            assert not name.names[0].templateArgs
3371            res = name.names[0].identOrOp
3372            assert isinstance(res, ASTIdentifier)
3373            return res
3374        else:
3375            return None
3376
3377    def get_id(self, version: int, objectType: str = None, symbol: "Symbol" = None) -> str:
3378        assert version >= 2
3379        # this is not part of the normal name mangling in C++
3380        if symbol:
3381            # the anchor will be our parent
3382            return symbol.parent.declaration.get_id(version, prefixed=None)
3383        else:
3384            return '_' + self.param.get_id(version)
3385
3386    def _stringify(self, transform: StringifyTransform) -> str:
3387        return transform(self.param)
3388
3389    def describe_signature(self, signode: TextElement, mode: str,
3390                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3391        self.param.describe_signature(signode, mode, env, symbol)
3392
3393
3394class ASTTemplateParams(ASTBase):
3395    def __init__(self, params: List[ASTTemplateParam]) -> None:
3396        assert params is not None
3397        self.params = params
3398
3399    def get_id(self, version: int) -> str:
3400        assert version >= 2
3401        res = []
3402        res.append("I")
3403        for param in self.params:
3404            res.append(param.get_id(version))
3405        res.append("E")
3406        return ''.join(res)
3407
3408    def _stringify(self, transform: StringifyTransform) -> str:
3409        res = []
3410        res.append("template<")
3411        res.append(", ".join(transform(a) for a in self.params))
3412        res.append("> ")
3413        return ''.join(res)
3414
3415    def describe_signature(self, signode: TextElement, mode: str,
3416                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3417        signode += nodes.Text("template<")
3418        first = True
3419        for param in self.params:
3420            if not first:
3421                signode += nodes.Text(", ")
3422            first = False
3423            param.describe_signature(signode, mode, env, symbol)
3424        signode += nodes.Text(">")
3425
3426    def describe_signature_as_introducer(
3427            self, parentNode: desc_signature, mode: str, env: "BuildEnvironment",
3428            symbol: "Symbol", lineSpec: bool) -> None:
3429        def makeLine(parentNode: desc_signature) -> addnodes.desc_signature_line:
3430            signode = addnodes.desc_signature_line()
3431            parentNode += signode
3432            signode.sphinx_line_type = 'templateParams'
3433            return signode
3434        lineNode = makeLine(parentNode)
3435        lineNode += nodes.Text("template<")
3436        first = True
3437        for param in self.params:
3438            if not first:
3439                lineNode += nodes.Text(", ")
3440            first = False
3441            if lineSpec:
3442                lineNode = makeLine(parentNode)
3443            param.describe_signature(lineNode, mode, env, symbol)
3444        if lineSpec and not first:
3445            lineNode = makeLine(parentNode)
3446        lineNode += nodes.Text(">")
3447
3448
3449# Template introducers
3450################################################################################
3451
3452class ASTTemplateIntroductionParameter(ASTBase):
3453    def __init__(self, identifier: ASTIdentifier, parameterPack: bool) -> None:
3454        self.identifier = identifier
3455        self.parameterPack = parameterPack
3456
3457    @property
3458    def name(self) -> ASTNestedName:
3459        id = self.get_identifier()
3460        return ASTNestedName([ASTNestedNameElement(id, None)], [False], rooted=False)
3461
3462    @property
3463    def isPack(self) -> bool:
3464        return self.parameterPack
3465
3466    def get_identifier(self) -> ASTIdentifier:
3467        return self.identifier
3468
3469    def get_id(self, version: int, objectType: str = None, symbol: "Symbol" = None) -> str:
3470        assert version >= 2
3471        # this is not part of the normal name mangling in C++
3472        if symbol:
3473            # the anchor will be our parent
3474            return symbol.parent.declaration.get_id(version, prefixed=None)
3475        else:
3476            if self.parameterPack:
3477                return 'Dp'
3478            else:
3479                return '0'  # we need to put something
3480
3481    def get_id_as_arg(self, version: int) -> str:
3482        assert version >= 2
3483        # used for the implicit requires clause
3484        res = self.identifier.get_id(version)
3485        if self.parameterPack:
3486            return 'sp' + res
3487        else:
3488            return res
3489
3490    def _stringify(self, transform: StringifyTransform) -> str:
3491        res = []
3492        if self.parameterPack:
3493            res.append('...')
3494        res.append(transform(self.identifier))
3495        return ''.join(res)
3496
3497    def describe_signature(self, signode: TextElement, mode: str,
3498                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3499        if self.parameterPack:
3500            signode += nodes.Text('...')
3501        self.identifier.describe_signature(signode, mode, env, '', '', symbol)
3502
3503
3504class ASTTemplateIntroduction(ASTBase):
3505    def __init__(self, concept: ASTNestedName,
3506                 params: List[ASTTemplateIntroductionParameter]) -> None:
3507        assert len(params) > 0
3508        self.concept = concept
3509        self.params = params
3510
3511    def get_id(self, version: int) -> str:
3512        assert version >= 2
3513        # first do the same as a normal template parameter list
3514        res = []
3515        res.append("I")
3516        for param in self.params:
3517            res.append(param.get_id(version))
3518        res.append("E")
3519        # let's use X expr E, which is otherwise for constant template args
3520        res.append("X")
3521        res.append(self.concept.get_id(version))
3522        res.append("I")
3523        for param in self.params:
3524            res.append(param.get_id_as_arg(version))
3525        res.append("E")
3526        res.append("E")
3527        return ''.join(res)
3528
3529    def _stringify(self, transform: StringifyTransform) -> str:
3530        res = []
3531        res.append(transform(self.concept))
3532        res.append('{')
3533        res.append(', '.join(transform(param) for param in self.params))
3534        res.append('} ')
3535        return ''.join(res)
3536
3537    def describe_signature_as_introducer(
3538            self, parentNode: desc_signature, mode: str,
3539            env: "BuildEnvironment", symbol: "Symbol", lineSpec: bool) -> None:
3540        # Note: 'lineSpec' has no effect on template introductions.
3541        signode = addnodes.desc_signature_line()
3542        parentNode += signode
3543        signode.sphinx_line_type = 'templateIntroduction'
3544        self.concept.describe_signature(signode, 'markType', env, symbol)
3545        signode += nodes.Text('{')
3546        first = True
3547        for param in self.params:
3548            if not first:
3549                signode += nodes.Text(', ')
3550            first = False
3551            param.describe_signature(signode, mode, env, symbol)
3552        signode += nodes.Text('}')
3553
3554
3555################################################################################
3556
3557class ASTTemplateDeclarationPrefix(ASTBase):
3558    def __init__(self,
3559                 templates: List[Union[ASTTemplateParams,
3560                                       ASTTemplateIntroduction]]) -> None:
3561        # templates is None means it's an explicit instantiation of a variable
3562        self.templates = templates
3563
3564    def get_id(self, version: int) -> str:
3565        assert version >= 2
3566        # this is not part of a normal name mangling system
3567        res = []
3568        for t in self.templates:
3569            res.append(t.get_id(version))
3570        return ''.join(res)
3571
3572    def _stringify(self, transform: StringifyTransform) -> str:
3573        res = []
3574        for t in self.templates:
3575            res.append(transform(t))
3576        return ''.join(res)
3577
3578    def describe_signature(self, signode: desc_signature, mode: str,
3579                           env: "BuildEnvironment", symbol: "Symbol", lineSpec: bool) -> None:
3580        verify_description_mode(mode)
3581        for t in self.templates:
3582            t.describe_signature_as_introducer(signode, 'lastIsName', env, symbol, lineSpec)
3583
3584
3585class ASTRequiresClause(ASTBase):
3586    def __init__(self, expr: ASTExpression) -> None:
3587        self.expr = expr
3588
3589    def _stringify(self, transform: StringifyTransform) -> str:
3590        return 'requires ' + transform(self.expr)
3591
3592    def describe_signature(self, signode: addnodes.desc_signature_line, mode: str,
3593                           env: "BuildEnvironment", symbol: "Symbol") -> None:
3594        signode += nodes.Text('requires ', 'requires ')
3595        self.expr.describe_signature(signode, mode, env, symbol)
3596
3597
3598################################################################################
3599################################################################################
3600
3601class ASTDeclaration(ASTBase):
3602    def __init__(self, objectType: str, directiveType: str, visibility: str,
3603                 templatePrefix: ASTTemplateDeclarationPrefix,
3604                 requiresClause: ASTRequiresClause, declaration: Any,
3605                 trailingRequiresClause: ASTRequiresClause,
3606                 semicolon: bool = False) -> None:
3607        self.objectType = objectType
3608        self.directiveType = directiveType
3609        self.visibility = visibility
3610        self.templatePrefix = templatePrefix
3611        self.requiresClause = requiresClause
3612        self.declaration = declaration
3613        self.trailingRequiresClause = trailingRequiresClause
3614        self.semicolon = semicolon
3615
3616        self.symbol = None  # type: Symbol
3617        # set by CPPObject._add_enumerator_to_parent
3618        self.enumeratorScopedSymbol = None  # type: Symbol
3619
3620    def clone(self) -> "ASTDeclaration":
3621        templatePrefixClone = self.templatePrefix.clone() if self.templatePrefix else None
3622        requiresClasueClone = self.requiresClause.clone() if self.requiresClause else None
3623        trailingRequiresClasueClone = self.trailingRequiresClause.clone() \
3624            if self.trailingRequiresClause else None
3625        return ASTDeclaration(self.objectType, self.directiveType, self.visibility,
3626                              templatePrefixClone, requiresClasueClone,
3627                              self.declaration.clone(), trailingRequiresClasueClone,
3628                              self.semicolon)
3629
3630    @property
3631    def name(self) -> ASTNestedName:
3632        return self.declaration.name
3633
3634    @property
3635    def function_params(self) -> List[ASTFunctionParameter]:
3636        if self.objectType != 'function':
3637            return None
3638        return self.declaration.function_params
3639
3640    def get_id(self, version: int, prefixed: bool = True) -> str:
3641        if version == 1:
3642            if self.templatePrefix:
3643                raise NoOldIdError()
3644            if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
3645                return self.enumeratorScopedSymbol.declaration.get_id(version)
3646            return self.declaration.get_id(version, self.objectType, self.symbol)
3647        # version >= 2
3648        if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
3649            return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed)
3650        if prefixed:
3651            res = [_id_prefix[version]]
3652        else:
3653            res = []
3654        if self.templatePrefix:
3655            res.append(self.templatePrefix.get_id(version))
3656        if self.requiresClause or self.trailingRequiresClause:
3657            if version < 4:
3658                raise NoOldIdError()
3659            res.append('IQ')
3660            if self.requiresClause and self.trailingRequiresClause:
3661                res.append('aa')
3662            if self.requiresClause:
3663                res.append(self.requiresClause.expr.get_id(version))
3664            if self.trailingRequiresClause:
3665                res.append(self.trailingRequiresClause.expr.get_id(version))
3666            res.append('E')
3667        res.append(self.declaration.get_id(version, self.objectType, self.symbol))
3668        return ''.join(res)
3669
3670    def get_newest_id(self) -> str:
3671        return self.get_id(_max_id, True)
3672
3673    def _stringify(self, transform: StringifyTransform) -> str:
3674        res = []
3675        if self.visibility and self.visibility != "public":
3676            res.append(self.visibility)
3677            res.append(' ')
3678        if self.templatePrefix:
3679            res.append(transform(self.templatePrefix))
3680        if self.requiresClause:
3681            res.append(transform(self.requiresClause))
3682            res.append(' ')
3683        res.append(transform(self.declaration))
3684        if self.trailingRequiresClause:
3685            res.append(' ')
3686            res.append(transform(self.trailingRequiresClause))
3687        if self.semicolon:
3688            res.append(';')
3689        return ''.join(res)
3690
3691    def describe_signature(self, signode: desc_signature, mode: str,
3692                           env: "BuildEnvironment", options: Dict) -> None:
3693        verify_description_mode(mode)
3694        assert self.symbol
3695        # The caller of the domain added a desc_signature node.
3696        # Always enable multiline:
3697        signode['is_multiline'] = True
3698        # Put each line in a desc_signature_line node.
3699        mainDeclNode = addnodes.desc_signature_line()
3700        mainDeclNode.sphinx_line_type = 'declarator'
3701        mainDeclNode['add_permalink'] = not self.symbol.isRedeclaration
3702
3703        if self.templatePrefix:
3704            self.templatePrefix.describe_signature(signode, mode, env,
3705                                                   symbol=self.symbol,
3706                                                   lineSpec=options.get('tparam-line-spec'))
3707        if self.requiresClause:
3708            reqNode = addnodes.desc_signature_line()
3709            reqNode.sphinx_line_type = 'requiresClause'
3710            signode.append(reqNode)
3711            self.requiresClause.describe_signature(reqNode, 'markType', env, self.symbol)
3712        signode += mainDeclNode
3713        if self.visibility and self.visibility != "public":
3714            mainDeclNode += addnodes.desc_annotation(self.visibility + " ",
3715                                                     self.visibility + " ")
3716        if self.objectType == 'type':
3717            prefix = self.declaration.get_type_declaration_prefix()
3718            prefix += ' '
3719            mainDeclNode += addnodes.desc_annotation(prefix, prefix)
3720        elif self.objectType == 'concept':
3721            mainDeclNode += addnodes.desc_annotation('concept ', 'concept ')
3722        elif self.objectType == 'member':
3723            pass
3724        elif self.objectType == 'function':
3725            pass
3726        elif self.objectType == 'class':
3727            assert self.directiveType in ('class', 'struct')
3728            prefix = self.directiveType + ' '
3729            mainDeclNode += addnodes.desc_annotation(prefix, prefix)
3730        elif self.objectType == 'union':
3731            mainDeclNode += addnodes.desc_annotation('union ', 'union ')
3732        elif self.objectType == 'enum':
3733            if self.directiveType == 'enum':
3734                prefix = 'enum '
3735            elif self.directiveType == 'enum-class':
3736                prefix = 'enum class '
3737            elif self.directiveType == 'enum-struct':
3738                prefix = 'enum struct '
3739            else:
3740                assert False  # wrong directiveType used
3741            mainDeclNode += addnodes.desc_annotation(prefix, prefix)
3742        elif self.objectType == 'enumerator':
3743            mainDeclNode += addnodes.desc_annotation('enumerator ', 'enumerator ')
3744        else:
3745            print(self.objectType)
3746            assert False
3747        self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
3748        lastDeclNode = mainDeclNode
3749        if self.trailingRequiresClause:
3750            trailingReqNode = addnodes.desc_signature_line()
3751            trailingReqNode.sphinx_line_type = 'trailingRequiresClause'
3752            signode.append(trailingReqNode)
3753            lastDeclNode = trailingReqNode
3754            self.trailingRequiresClause.describe_signature(
3755                trailingReqNode, 'markType', env, self.symbol)
3756        if self.semicolon:
3757            lastDeclNode += nodes.Text(';')
3758
3759
3760class ASTNamespace(ASTBase):
3761    def __init__(self, nestedName: ASTNestedName,
3762                 templatePrefix: ASTTemplateDeclarationPrefix) -> None:
3763        self.nestedName = nestedName
3764        self.templatePrefix = templatePrefix
3765
3766    def _stringify(self, transform: StringifyTransform) -> str:
3767        res = []
3768        if self.templatePrefix:
3769            res.append(transform(self.templatePrefix))
3770        res.append(transform(self.nestedName))
3771        return ''.join(res)
3772
3773
3774class SymbolLookupResult:
3775    def __init__(self, symbols: Iterator["Symbol"], parentSymbol: "Symbol",
3776                 identOrOp: Union[ASTIdentifier, ASTOperator], templateParams: Any,
3777                 templateArgs: ASTTemplateArgs) -> None:
3778        self.symbols = symbols
3779        self.parentSymbol = parentSymbol
3780        self.identOrOp = identOrOp
3781        self.templateParams = templateParams
3782        self.templateArgs = templateArgs
3783
3784
3785class LookupKey:
3786    def __init__(self, data: List[Tuple[ASTNestedNameElement,
3787                                        Union[ASTTemplateParams,
3788                                              ASTTemplateIntroduction],
3789                                        str]]) -> None:
3790        self.data = data
3791
3792
3793class Symbol:
3794    debug_indent = 0
3795    debug_indent_string = "  "
3796    debug_lookup = False  # overridden by the corresponding config value
3797    debug_show_tree = False  # overridden by the corresponding config value
3798
3799    def __copy__(self):
3800        assert False  # shouldn't happen
3801
3802    def __deepcopy__(self, memo):
3803        if self.parent:
3804            assert False  # shouldn't happen
3805        else:
3806            # the domain base class makes a copy of the initial data, which is fine
3807            return Symbol(None, None, None, None, None, None, None)
3808
3809    @staticmethod
3810    def debug_print(*args: Any) -> None:
3811        print(Symbol.debug_indent_string * Symbol.debug_indent, end="")
3812        print(*args)
3813
3814    def _assert_invariants(self) -> None:
3815        if not self.parent:
3816            # parent == None means global scope, so declaration means a parent
3817            assert not self.identOrOp
3818            assert not self.templateParams
3819            assert not self.templateArgs
3820            assert not self.declaration
3821            assert not self.docname
3822        else:
3823            if self.declaration:
3824                assert self.docname
3825
3826    def __setattr__(self, key: str, value: Any) -> None:
3827        if key == "children":
3828            assert False
3829        else:
3830            return super().__setattr__(key, value)
3831
3832    def __init__(self, parent: "Symbol", identOrOp: Union[ASTIdentifier, ASTOperator],
3833                 templateParams: Union[ASTTemplateParams, ASTTemplateIntroduction],
3834                 templateArgs: Any, declaration: ASTDeclaration,
3835                 docname: str, line: int) -> None:
3836        self.parent = parent
3837        # declarations in a single directive are linked together
3838        self.siblingAbove = None  # type: Symbol
3839        self.siblingBelow = None  # type: Symbol
3840        self.identOrOp = identOrOp
3841        self.templateParams = templateParams  # template<templateParams>
3842        self.templateArgs = templateArgs  # identifier<templateArgs>
3843        self.declaration = declaration
3844        self.docname = docname
3845        self.line = line
3846        self.isRedeclaration = False
3847        self._assert_invariants()
3848
3849        # Remember to modify Symbol.remove if modifications to the parent change.
3850        self._children = []  # type: List[Symbol]
3851        self._anonChildren = []  # type: List[Symbol]
3852        # note: _children includes _anonChildren
3853        if self.parent:
3854            self.parent._children.append(self)
3855        if self.declaration:
3856            self.declaration.symbol = self
3857
3858        # Do symbol addition after self._children has been initialised.
3859        self._add_template_and_function_params()
3860
3861    def _fill_empty(self, declaration: ASTDeclaration, docname: str, line: int) -> None:
3862        self._assert_invariants()
3863        assert self.declaration is None
3864        assert self.docname is None
3865        assert self.line is None
3866        assert declaration is not None
3867        assert docname is not None
3868        assert line is not None
3869        self.declaration = declaration
3870        self.declaration.symbol = self
3871        self.docname = docname
3872        self.line = line
3873        self._assert_invariants()
3874        # and symbol addition should be done as well
3875        self._add_template_and_function_params()
3876
3877    def _add_template_and_function_params(self) -> None:
3878        if Symbol.debug_lookup:
3879            Symbol.debug_indent += 1
3880            Symbol.debug_print("_add_template_and_function_params:")
3881        # Note: we may be called from _fill_empty, so the symbols we want
3882        #       to add may actually already be present (as empty symbols).
3883
3884        # add symbols for the template params
3885        if self.templateParams:
3886            for tp in self.templateParams.params:
3887                if not tp.get_identifier():
3888                    continue
3889                # only add a declaration if we our self are from a declaration
3890                if self.declaration:
3891                    decl = ASTDeclaration('templateParam', None, None, None, None, tp, None)
3892                else:
3893                    decl = None
3894                nne = ASTNestedNameElement(tp.get_identifier(), None)
3895                nn = ASTNestedName([nne], [False], rooted=False)
3896                self._add_symbols(nn, [], decl, self.docname, self.line)
3897        # add symbols for function parameters, if any
3898        if self.declaration is not None and self.declaration.function_params is not None:
3899            for fp in self.declaration.function_params:
3900                if fp.arg is None:
3901                    continue
3902                nn = fp.arg.name
3903                if nn is None:
3904                    continue
3905                # (comparing to the template params: we have checked that we are a declaration)
3906                decl = ASTDeclaration('functionParam', None, None, None, None, fp, None)
3907                assert not nn.rooted
3908                assert len(nn.names) == 1
3909                self._add_symbols(nn, [], decl, self.docname, self.line)
3910        if Symbol.debug_lookup:
3911            Symbol.debug_indent -= 1
3912
3913    def remove(self) -> None:
3914        if self.parent is None:
3915            return
3916        assert self in self.parent._children
3917        self.parent._children.remove(self)
3918        self.parent = None
3919
3920    def clear_doc(self, docname: str) -> None:
3921        newChildren = []  # type: List[Symbol]
3922        for sChild in self._children:
3923            sChild.clear_doc(docname)
3924            if sChild.declaration and sChild.docname == docname:
3925                sChild.declaration = None
3926                sChild.docname = None
3927                sChild.line = None
3928                if sChild.siblingAbove is not None:
3929                    sChild.siblingAbove.siblingBelow = sChild.siblingBelow
3930                if sChild.siblingBelow is not None:
3931                    sChild.siblingBelow.siblingAbove = sChild.siblingAbove
3932                sChild.siblingAbove = None
3933                sChild.siblingBelow = None
3934            newChildren.append(sChild)
3935        self._children = newChildren
3936
3937    def get_all_symbols(self) -> Iterator[Any]:
3938        yield self
3939        for sChild in self._children:
3940            yield from sChild.get_all_symbols()
3941
3942    @property
3943    def children_recurse_anon(self) -> Generator["Symbol", None, None]:
3944        for c in self._children:
3945            yield c
3946            if not c.identOrOp.is_anon():
3947                continue
3948
3949            yield from c.children_recurse_anon
3950
3951    def get_lookup_key(self) -> "LookupKey":
3952        # The pickle files for the environment and for each document are distinct.
3953        # The environment has all the symbols, but the documents has xrefs that
3954        # must know their scope. A lookup key is essentially a specification of
3955        # how to find a specific symbol.
3956        symbols = []
3957        s = self
3958        while s.parent:
3959            symbols.append(s)
3960            s = s.parent
3961        symbols.reverse()
3962        key = []
3963        for s in symbols:
3964            nne = ASTNestedNameElement(s.identOrOp, s.templateArgs)
3965            if s.declaration is not None:
3966                key.append((nne, s.templateParams, s.declaration.get_newest_id()))
3967            else:
3968                key.append((nne, s.templateParams, None))
3969        return LookupKey(key)
3970
3971    def get_full_nested_name(self) -> ASTNestedName:
3972        symbols = []
3973        s = self
3974        while s.parent:
3975            symbols.append(s)
3976            s = s.parent
3977        symbols.reverse()
3978        names = []
3979        templates = []
3980        for s in symbols:
3981            names.append(ASTNestedNameElement(s.identOrOp, s.templateArgs))
3982            templates.append(False)
3983        return ASTNestedName(names, templates, rooted=False)
3984
3985    def _find_first_named_symbol(self, identOrOp: Union[ASTIdentifier, ASTOperator],
3986                                 templateParams: Any, templateArgs: ASTTemplateArgs,
3987                                 templateShorthand: bool, matchSelf: bool,
3988                                 recurseInAnon: bool, correctPrimaryTemplateArgs: bool
3989                                 ) -> "Symbol":
3990        if Symbol.debug_lookup:
3991            Symbol.debug_print("_find_first_named_symbol ->")
3992        res = self._find_named_symbols(identOrOp, templateParams, templateArgs,
3993                                       templateShorthand, matchSelf, recurseInAnon,
3994                                       correctPrimaryTemplateArgs,
3995                                       searchInSiblings=False)
3996        try:
3997            return next(res)
3998        except StopIteration:
3999            return None
4000
4001    def _find_named_symbols(self, identOrOp: Union[ASTIdentifier, ASTOperator],
4002                            templateParams: Any, templateArgs: ASTTemplateArgs,
4003                            templateShorthand: bool, matchSelf: bool,
4004                            recurseInAnon: bool, correctPrimaryTemplateArgs: bool,
4005                            searchInSiblings: bool) -> Iterator["Symbol"]:
4006        if Symbol.debug_lookup:
4007            Symbol.debug_indent += 1
4008            Symbol.debug_print("_find_named_symbols:")
4009            Symbol.debug_indent += 1
4010            Symbol.debug_print("self:")
4011            print(self.to_string(Symbol.debug_indent + 1), end="")
4012            Symbol.debug_print("identOrOp:                  ", identOrOp)
4013            Symbol.debug_print("templateParams:             ", templateParams)
4014            Symbol.debug_print("templateArgs:               ", templateArgs)
4015            Symbol.debug_print("templateShorthand:          ", templateShorthand)
4016            Symbol.debug_print("matchSelf:                  ", matchSelf)
4017            Symbol.debug_print("recurseInAnon:              ", recurseInAnon)
4018            Symbol.debug_print("correctPrimaryTemplateAargs:", correctPrimaryTemplateArgs)
4019            Symbol.debug_print("searchInSiblings:           ", searchInSiblings)
4020
4021        def isSpecialization() -> bool:
4022            # the names of the template parameters must be given exactly as args
4023            # and params that are packs must in the args be the name expanded
4024            if len(templateParams.params) != len(templateArgs.args):
4025                return True
4026            # having no template params and no arguments is also a specialization
4027            if len(templateParams.params) == 0:
4028                return True
4029            for i in range(len(templateParams.params)):
4030                param = templateParams.params[i]
4031                arg = templateArgs.args[i]
4032                # TODO: doing this by string manipulation is probably not the most efficient
4033                paramName = str(param.name)
4034                argTxt = str(arg)
4035                isArgPackExpansion = argTxt.endswith('...')
4036                if param.isPack != isArgPackExpansion:
4037                    return True
4038                argName = argTxt[:-3] if isArgPackExpansion else argTxt
4039                if paramName != argName:
4040                    return True
4041            return False
4042        if correctPrimaryTemplateArgs:
4043            if templateParams is not None and templateArgs is not None:
4044                # If both are given, but it's not a specialization, then do lookup as if
4045                # there is no argument list.
4046                # For example: template<typename T> int A<T>::var;
4047                if not isSpecialization():
4048                    templateArgs = None
4049
4050        def matches(s: "Symbol") -> bool:
4051            if s.identOrOp != identOrOp:
4052                return False
4053            if (s.templateParams is None) != (templateParams is None):
4054                if templateParams is not None:
4055                    # we query with params, they must match params
4056                    return False
4057                if not templateShorthand:
4058                    # we don't query with params, and we do care about them
4059                    return False
4060            if templateParams:
4061                # TODO: do better comparison
4062                if str(s.templateParams) != str(templateParams):
4063                    return False
4064            if (s.templateArgs is None) != (templateArgs is None):
4065                return False
4066            if s.templateArgs:
4067                # TODO: do better comparison
4068                if str(s.templateArgs) != str(templateArgs):
4069                    return False
4070            return True
4071
4072        def candidates() -> Generator[Symbol, None, None]:
4073            s = self
4074            if Symbol.debug_lookup:
4075                Symbol.debug_print("searching in self:")
4076                print(s.to_string(Symbol.debug_indent + 1), end="")
4077            while True:
4078                if matchSelf:
4079                    yield s
4080                if recurseInAnon:
4081                    yield from s.children_recurse_anon
4082                else:
4083                    yield from s._children
4084
4085                if s.siblingAbove is None:
4086                    break
4087                s = s.siblingAbove
4088                if Symbol.debug_lookup:
4089                    Symbol.debug_print("searching in sibling:")
4090                    print(s.to_string(Symbol.debug_indent + 1), end="")
4091
4092        for s in candidates():
4093            if Symbol.debug_lookup:
4094                Symbol.debug_print("candidate:")
4095                print(s.to_string(Symbol.debug_indent + 1), end="")
4096            if matches(s):
4097                if Symbol.debug_lookup:
4098                    Symbol.debug_indent += 1
4099                    Symbol.debug_print("matches")
4100                    Symbol.debug_indent -= 3
4101                yield s
4102                if Symbol.debug_lookup:
4103                    Symbol.debug_indent += 2
4104        if Symbol.debug_lookup:
4105            Symbol.debug_indent -= 2
4106
4107    def _symbol_lookup(self, nestedName: ASTNestedName, templateDecls: List[Any],
4108                       onMissingQualifiedSymbol: Callable[["Symbol", Union[ASTIdentifier, ASTOperator], Any, ASTTemplateArgs], "Symbol"],  # NOQA
4109                       strictTemplateParamArgLists: bool, ancestorLookupType: str,
4110                       templateShorthand: bool, matchSelf: bool,
4111                       recurseInAnon: bool, correctPrimaryTemplateArgs: bool,
4112                       searchInSiblings: bool) -> SymbolLookupResult:
4113        # ancestorLookupType: if not None, specifies the target type of the lookup
4114        if Symbol.debug_lookup:
4115            Symbol.debug_indent += 1
4116            Symbol.debug_print("_symbol_lookup:")
4117            Symbol.debug_indent += 1
4118            Symbol.debug_print("self:")
4119            print(self.to_string(Symbol.debug_indent + 1), end="")
4120            Symbol.debug_print("nestedName:        ", nestedName)
4121            Symbol.debug_print("templateDecls:     ", ",".join(str(t) for t in templateDecls))
4122            Symbol.debug_print("strictTemplateParamArgLists:", strictTemplateParamArgLists)
4123            Symbol.debug_print("ancestorLookupType:", ancestorLookupType)
4124            Symbol.debug_print("templateShorthand: ", templateShorthand)
4125            Symbol.debug_print("matchSelf:         ", matchSelf)
4126            Symbol.debug_print("recurseInAnon:     ", recurseInAnon)
4127            Symbol.debug_print("correctPrimaryTemplateArgs: ", correctPrimaryTemplateArgs)
4128            Symbol.debug_print("searchInSiblings:  ", searchInSiblings)
4129
4130        if strictTemplateParamArgLists:
4131            # Each template argument list must have a template parameter list.
4132            # But to declare a template there must be an additional template parameter list.
4133            assert (nestedName.num_templates() == len(templateDecls) or
4134                    nestedName.num_templates() + 1 == len(templateDecls))
4135        else:
4136            assert len(templateDecls) <= nestedName.num_templates() + 1
4137
4138        names = nestedName.names
4139
4140        # find the right starting point for lookup
4141        parentSymbol = self
4142        if nestedName.rooted:
4143            while parentSymbol.parent:
4144                parentSymbol = parentSymbol.parent
4145        if ancestorLookupType is not None:
4146            # walk up until we find the first identifier
4147            firstName = names[0]
4148            if not firstName.is_operator():
4149                while parentSymbol.parent:
4150                    if parentSymbol.find_identifier(firstName.identOrOp,
4151                                                    matchSelf=matchSelf,
4152                                                    recurseInAnon=recurseInAnon,
4153                                                    searchInSiblings=searchInSiblings):
4154                        # if we are in the scope of a constructor but wants to
4155                        # reference the class we need to walk one extra up
4156                        if (len(names) == 1 and ancestorLookupType == 'class' and matchSelf and
4157                                parentSymbol.parent and
4158                                parentSymbol.parent.identOrOp == firstName.identOrOp):
4159                            pass
4160                        else:
4161                            break
4162                    parentSymbol = parentSymbol.parent
4163
4164        if Symbol.debug_lookup:
4165            Symbol.debug_print("starting point:")
4166            print(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
4167
4168        # and now the actual lookup
4169        iTemplateDecl = 0
4170        for name in names[:-1]:
4171            identOrOp = name.identOrOp
4172            templateArgs = name.templateArgs
4173            if strictTemplateParamArgLists:
4174                # there must be a parameter list
4175                if templateArgs:
4176                    assert iTemplateDecl < len(templateDecls)
4177                    templateParams = templateDecls[iTemplateDecl]
4178                    iTemplateDecl += 1
4179                else:
4180                    templateParams = None
4181            else:
4182                # take the next template parameter list if there is one
4183                # otherwise it's ok
4184                if templateArgs and iTemplateDecl < len(templateDecls):
4185                    templateParams = templateDecls[iTemplateDecl]
4186                    iTemplateDecl += 1
4187                else:
4188                    templateParams = None
4189
4190            symbol = parentSymbol._find_first_named_symbol(
4191                identOrOp,
4192                templateParams, templateArgs,
4193                templateShorthand=templateShorthand,
4194                matchSelf=matchSelf,
4195                recurseInAnon=recurseInAnon,
4196                correctPrimaryTemplateArgs=correctPrimaryTemplateArgs)
4197            if symbol is None:
4198                symbol = onMissingQualifiedSymbol(parentSymbol, identOrOp,
4199                                                  templateParams, templateArgs)
4200                if symbol is None:
4201                    if Symbol.debug_lookup:
4202                        Symbol.debug_indent -= 2
4203                    return None
4204            # We have now matched part of a nested name, and need to match more
4205            # so even if we should matchSelf before, we definitely shouldn't
4206            # even more. (see also issue #2666)
4207            matchSelf = False
4208            parentSymbol = symbol
4209
4210        if Symbol.debug_lookup:
4211            Symbol.debug_print("handle last name from:")
4212            print(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
4213
4214        # handle the last name
4215        name = names[-1]
4216        identOrOp = name.identOrOp
4217        templateArgs = name.templateArgs
4218        if iTemplateDecl < len(templateDecls):
4219            assert iTemplateDecl + 1 == len(templateDecls)
4220            templateParams = templateDecls[iTemplateDecl]
4221        else:
4222            assert iTemplateDecl == len(templateDecls)
4223            templateParams = None
4224
4225        symbols = parentSymbol._find_named_symbols(
4226            identOrOp, templateParams, templateArgs,
4227            templateShorthand=templateShorthand, matchSelf=matchSelf,
4228            recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False,
4229            searchInSiblings=searchInSiblings)
4230        if Symbol.debug_lookup:
4231            symbols = list(symbols)  # type: ignore
4232            Symbol.debug_indent -= 2
4233        return SymbolLookupResult(symbols, parentSymbol,
4234                                  identOrOp, templateParams, templateArgs)
4235
4236    def _add_symbols(self, nestedName: ASTNestedName, templateDecls: List[Any],
4237                     declaration: ASTDeclaration, docname: str, line: int) -> "Symbol":
4238        # Used for adding a whole path of symbols, where the last may or may not
4239        # be an actual declaration.
4240
4241        if Symbol.debug_lookup:
4242            Symbol.debug_indent += 1
4243            Symbol.debug_print("_add_symbols:")
4244            Symbol.debug_indent += 1
4245            Symbol.debug_print("tdecls:", ",".join(str(t) for t in templateDecls))
4246            Symbol.debug_print("nn:       ", nestedName)
4247            Symbol.debug_print("decl:     ", declaration)
4248            Symbol.debug_print("location: {}:{}".format(docname, line))
4249
4250        def onMissingQualifiedSymbol(parentSymbol: "Symbol",
4251                                     identOrOp: Union[ASTIdentifier, ASTOperator],
4252                                     templateParams: Any, templateArgs: ASTTemplateArgs
4253                                     ) -> "Symbol":
4254            if Symbol.debug_lookup:
4255                Symbol.debug_indent += 1
4256                Symbol.debug_print("_add_symbols, onMissingQualifiedSymbol:")
4257                Symbol.debug_indent += 1
4258                Symbol.debug_print("templateParams:", templateParams)
4259                Symbol.debug_print("identOrOp:     ", identOrOp)
4260                Symbol.debug_print("templateARgs:  ", templateArgs)
4261                Symbol.debug_indent -= 2
4262            return Symbol(parent=parentSymbol, identOrOp=identOrOp,
4263                          templateParams=templateParams,
4264                          templateArgs=templateArgs, declaration=None,
4265                          docname=None, line=None)
4266
4267        lookupResult = self._symbol_lookup(nestedName, templateDecls,
4268                                           onMissingQualifiedSymbol,
4269                                           strictTemplateParamArgLists=True,
4270                                           ancestorLookupType=None,
4271                                           templateShorthand=False,
4272                                           matchSelf=False,
4273                                           recurseInAnon=False,
4274                                           correctPrimaryTemplateArgs=True,
4275                                           searchInSiblings=False)
4276        assert lookupResult is not None  # we create symbols all the way, so that can't happen
4277        symbols = list(lookupResult.symbols)
4278        if len(symbols) == 0:
4279            if Symbol.debug_lookup:
4280                Symbol.debug_print("_add_symbols, result, no symbol:")
4281                Symbol.debug_indent += 1
4282                Symbol.debug_print("templateParams:", lookupResult.templateParams)
4283                Symbol.debug_print("identOrOp:     ", lookupResult.identOrOp)
4284                Symbol.debug_print("templateArgs:  ", lookupResult.templateArgs)
4285                Symbol.debug_print("declaration:   ", declaration)
4286                Symbol.debug_print("location:      {}:{}".format(docname, line))
4287                Symbol.debug_indent -= 1
4288            symbol = Symbol(parent=lookupResult.parentSymbol,
4289                            identOrOp=lookupResult.identOrOp,
4290                            templateParams=lookupResult.templateParams,
4291                            templateArgs=lookupResult.templateArgs,
4292                            declaration=declaration,
4293                            docname=docname, line=line)
4294            if Symbol.debug_lookup:
4295                Symbol.debug_indent -= 2
4296            return symbol
4297
4298        if Symbol.debug_lookup:
4299            Symbol.debug_print("_add_symbols, result, symbols:")
4300            Symbol.debug_indent += 1
4301            Symbol.debug_print("number symbols:", len(symbols))
4302            Symbol.debug_indent -= 1
4303
4304        if not declaration:
4305            if Symbol.debug_lookup:
4306                Symbol.debug_print("no declaration")
4307                Symbol.debug_indent -= 2
4308            # good, just a scope creation
4309            # TODO: what if we have more than one symbol?
4310            return symbols[0]
4311
4312        noDecl = []
4313        withDecl = []
4314        dupDecl = []
4315        for s in symbols:
4316            if s.declaration is None:
4317                noDecl.append(s)
4318            elif s.isRedeclaration:
4319                dupDecl.append(s)
4320            else:
4321                withDecl.append(s)
4322        if Symbol.debug_lookup:
4323            Symbol.debug_print("#noDecl:  ", len(noDecl))
4324            Symbol.debug_print("#withDecl:", len(withDecl))
4325            Symbol.debug_print("#dupDecl: ", len(dupDecl))
4326        # With partial builds we may start with a large symbol tree stripped of declarations.
4327        # Essentially any combination of noDecl, withDecl, and dupDecls seems possible.
4328        # TODO: make partial builds fully work. What should happen when the primary symbol gets
4329        #  deleted, and other duplicates exist? The full document should probably be rebuild.
4330
4331        # First check if one of those with a declaration matches.
4332        # If it's a function, we need to compare IDs,
4333        # otherwise there should be only one symbol with a declaration.
4334        def makeCandSymbol() -> "Symbol":
4335            if Symbol.debug_lookup:
4336                Symbol.debug_print("begin: creating candidate symbol")
4337            symbol = Symbol(parent=lookupResult.parentSymbol,
4338                            identOrOp=lookupResult.identOrOp,
4339                            templateParams=lookupResult.templateParams,
4340                            templateArgs=lookupResult.templateArgs,
4341                            declaration=declaration,
4342                            docname=docname, line=line)
4343            if Symbol.debug_lookup:
4344                Symbol.debug_print("end:   creating candidate symbol")
4345            return symbol
4346        if len(withDecl) == 0:
4347            candSymbol = None
4348        else:
4349            candSymbol = makeCandSymbol()
4350
4351            def handleDuplicateDeclaration(symbol: "Symbol", candSymbol: "Symbol") -> None:
4352                if Symbol.debug_lookup:
4353                    Symbol.debug_indent += 1
4354                    Symbol.debug_print("redeclaration")
4355                    Symbol.debug_indent -= 1
4356                    Symbol.debug_indent -= 2
4357                # Redeclaration of the same symbol.
4358                # Let the new one be there, but raise an error to the client
4359                # so it can use the real symbol as subscope.
4360                # This will probably result in a duplicate id warning.
4361                candSymbol.isRedeclaration = True
4362                raise _DuplicateSymbolError(symbol, declaration)
4363
4364            if declaration.objectType != "function":
4365                assert len(withDecl) <= 1
4366                handleDuplicateDeclaration(withDecl[0], candSymbol)
4367                # (not reachable)
4368
4369            # a function, so compare IDs
4370            candId = declaration.get_newest_id()
4371            if Symbol.debug_lookup:
4372                Symbol.debug_print("candId:", candId)
4373            for symbol in withDecl:
4374                # but all existing must be functions as well,
4375                # otherwise we declare it to be a duplicate
4376                if symbol.declaration.objectType != 'function':
4377                    handleDuplicateDeclaration(symbol, candSymbol)
4378                    # (not reachable)
4379                oldId = symbol.declaration.get_newest_id()
4380                if Symbol.debug_lookup:
4381                    Symbol.debug_print("oldId: ", oldId)
4382                if candId == oldId:
4383                    handleDuplicateDeclaration(symbol, candSymbol)
4384                    # (not reachable)
4385            # no candidate symbol found with matching ID
4386        # if there is an empty symbol, fill that one
4387        if len(noDecl) == 0:
4388            if Symbol.debug_lookup:
4389                Symbol.debug_print("no match, no empty")
4390                if candSymbol is not None:
4391                    Symbol.debug_print("result is already created candSymbol")
4392                else:
4393                    Symbol.debug_print("result is makeCandSymbol()")
4394                Symbol.debug_indent -= 2
4395            if candSymbol is not None:
4396                return candSymbol
4397            else:
4398                return makeCandSymbol()
4399        else:
4400            if Symbol.debug_lookup:
4401                Symbol.debug_print("no match, but fill an empty declaration, candSybmol is not None?:", candSymbol is not None)  # NOQA
4402                Symbol.debug_indent -= 2
4403            if candSymbol is not None:
4404                candSymbol.remove()
4405            # assert len(noDecl) == 1
4406            # TODO: enable assertion when we at some point find out how to do cleanup
4407            # for now, just take the first one, it should work fine ... right?
4408            symbol = noDecl[0]
4409            # If someone first opened the scope, and then later
4410            # declares it, e.g,
4411            # .. namespace:: Test
4412            # .. namespace:: nullptr
4413            # .. class:: Test
4414            symbol._fill_empty(declaration, docname, line)
4415            return symbol
4416
4417    def merge_with(self, other: "Symbol", docnames: List[str],
4418                   env: "BuildEnvironment") -> None:
4419        if Symbol.debug_lookup:
4420            Symbol.debug_indent += 1
4421            Symbol.debug_print("merge_with:")
4422        assert other is not None
4423
4424        def unconditionalAdd(self, otherChild):
4425            # TODO: hmm, should we prune by docnames?
4426            self._children.append(otherChild)
4427            otherChild.parent = self
4428            otherChild._assert_invariants()
4429
4430        if Symbol.debug_lookup:
4431            Symbol.debug_indent += 1
4432        for otherChild in other._children:
4433            if Symbol.debug_lookup:
4434                Symbol.debug_print("otherChild:\n", otherChild.to_string(Symbol.debug_indent))
4435                Symbol.debug_indent += 1
4436            if otherChild.isRedeclaration:
4437                unconditionalAdd(self, otherChild)
4438                if Symbol.debug_lookup:
4439                    Symbol.debug_print("isRedeclaration")
4440                    Symbol.debug_indent -= 1
4441                continue
4442            candiateIter = self._find_named_symbols(
4443                identOrOp=otherChild.identOrOp,
4444                templateParams=otherChild.templateParams,
4445                templateArgs=otherChild.templateArgs,
4446                templateShorthand=False, matchSelf=False,
4447                recurseInAnon=False, correctPrimaryTemplateArgs=False,
4448                searchInSiblings=False)
4449            candidates = list(candiateIter)
4450
4451            if Symbol.debug_lookup:
4452                Symbol.debug_print("raw candidate symbols:", len(candidates))
4453            symbols = [s for s in candidates if not s.isRedeclaration]
4454            if Symbol.debug_lookup:
4455                Symbol.debug_print("non-duplicate candidate symbols:", len(symbols))
4456
4457            if len(symbols) == 0:
4458                unconditionalAdd(self, otherChild)
4459                if Symbol.debug_lookup:
4460                    Symbol.debug_indent -= 1
4461                continue
4462
4463            ourChild = None
4464            if otherChild.declaration is None:
4465                if Symbol.debug_lookup:
4466                    Symbol.debug_print("no declaration in other child")
4467                ourChild = symbols[0]
4468            else:
4469                queryId = otherChild.declaration.get_newest_id()
4470                if Symbol.debug_lookup:
4471                    Symbol.debug_print("queryId:  ", queryId)
4472                for symbol in symbols:
4473                    if symbol.declaration is None:
4474                        if Symbol.debug_lookup:
4475                            Symbol.debug_print("empty candidate")
4476                        # if in the end we have non matching, but have an empty one,
4477                        # then just continue with that
4478                        ourChild = symbol
4479                        continue
4480                    candId = symbol.declaration.get_newest_id()
4481                    if Symbol.debug_lookup:
4482                        Symbol.debug_print("candidate:", candId)
4483                    if candId == queryId:
4484                        ourChild = symbol
4485                        break
4486            if Symbol.debug_lookup:
4487                Symbol.debug_indent -= 1
4488            if ourChild is None:
4489                unconditionalAdd(self, otherChild)
4490                continue
4491            if otherChild.declaration and otherChild.docname in docnames:
4492                if not ourChild.declaration:
4493                    ourChild._fill_empty(otherChild.declaration,
4494                                         otherChild.docname, otherChild.line)
4495                elif ourChild.docname != otherChild.docname:
4496                    name = str(ourChild.declaration)
4497                    msg = __("Duplicate C++ declaration, also defined at %s:%s.\n"
4498                             "Declaration is '.. cpp:%s:: %s'.")
4499                    msg = msg % (ourChild.docname, ourChild.line,
4500                                 ourChild.declaration.directiveType, name)
4501                    logger.warning(msg, location=(otherChild.docname, otherChild.line))
4502                else:
4503                    # Both have declarations, and in the same docname.
4504                    # This can apparently happen, it should be safe to
4505                    # just ignore it, right?
4506                    # Hmm, only on duplicate declarations, right?
4507                    msg = "Internal C++ domain error during symbol merging.\n"
4508                    msg += "ourChild:\n" + ourChild.to_string(1)
4509                    msg += "\notherChild:\n" + otherChild.to_string(1)
4510                    logger.warning(msg, location=otherChild.docname)
4511            ourChild.merge_with(otherChild, docnames, env)
4512        if Symbol.debug_lookup:
4513            Symbol.debug_indent -= 2
4514
4515    def add_name(self, nestedName: ASTNestedName,
4516                 templatePrefix: ASTTemplateDeclarationPrefix = None) -> "Symbol":
4517        if Symbol.debug_lookup:
4518            Symbol.debug_indent += 1
4519            Symbol.debug_print("add_name:")
4520        if templatePrefix:
4521            templateDecls = templatePrefix.templates
4522        else:
4523            templateDecls = []
4524        res = self._add_symbols(nestedName, templateDecls,
4525                                declaration=None, docname=None, line=None)
4526        if Symbol.debug_lookup:
4527            Symbol.debug_indent -= 1
4528        return res
4529
4530    def add_declaration(self, declaration: ASTDeclaration,
4531                        docname: str, line: int) -> "Symbol":
4532        if Symbol.debug_lookup:
4533            Symbol.debug_indent += 1
4534            Symbol.debug_print("add_declaration:")
4535        assert declaration is not None
4536        assert docname is not None
4537        assert line is not None
4538        nestedName = declaration.name
4539        if declaration.templatePrefix:
4540            templateDecls = declaration.templatePrefix.templates
4541        else:
4542            templateDecls = []
4543        res = self._add_symbols(nestedName, templateDecls, declaration, docname, line)
4544        if Symbol.debug_lookup:
4545            Symbol.debug_indent -= 1
4546        return res
4547
4548    def find_identifier(self, identOrOp: Union[ASTIdentifier, ASTOperator],
4549                        matchSelf: bool, recurseInAnon: bool, searchInSiblings: bool
4550                        ) -> "Symbol":
4551        if Symbol.debug_lookup:
4552            Symbol.debug_indent += 1
4553            Symbol.debug_print("find_identifier:")
4554            Symbol.debug_indent += 1
4555            Symbol.debug_print("identOrOp:       ", identOrOp)
4556            Symbol.debug_print("matchSelf:       ", matchSelf)
4557            Symbol.debug_print("recurseInAnon:   ", recurseInAnon)
4558            Symbol.debug_print("searchInSiblings:", searchInSiblings)
4559            print(self.to_string(Symbol.debug_indent + 1), end="")
4560            Symbol.debug_indent -= 2
4561        current = self
4562        while current is not None:
4563            if Symbol.debug_lookup:
4564                Symbol.debug_indent += 2
4565                Symbol.debug_print("trying:")
4566                print(current.to_string(Symbol.debug_indent + 1), end="")
4567                Symbol.debug_indent -= 2
4568            if matchSelf and current.identOrOp == identOrOp:
4569                return current
4570            children = current.children_recurse_anon if recurseInAnon else current._children
4571            for s in children:
4572                if s.identOrOp == identOrOp:
4573                    return s
4574            if not searchInSiblings:
4575                break
4576            current = current.siblingAbove
4577        return None
4578
4579    def direct_lookup(self, key: "LookupKey") -> "Symbol":
4580        if Symbol.debug_lookup:
4581            Symbol.debug_indent += 1
4582            Symbol.debug_print("direct_lookup:")
4583            Symbol.debug_indent += 1
4584        s = self
4585        for name, templateParams, id_ in key.data:
4586            if id_ is not None:
4587                res = None
4588                for cand in s._children:
4589                    if cand.declaration is None:
4590                        continue
4591                    if cand.declaration.get_newest_id() == id_:
4592                        res = cand
4593                        break
4594                s = res
4595            else:
4596                identOrOp = name.identOrOp
4597                templateArgs = name.templateArgs
4598                s = s._find_first_named_symbol(identOrOp,
4599                                               templateParams, templateArgs,
4600                                               templateShorthand=False,
4601                                               matchSelf=False,
4602                                               recurseInAnon=False,
4603                                               correctPrimaryTemplateArgs=False)
4604            if Symbol.debug_lookup:
4605                Symbol.debug_print("name:          ", name)
4606                Symbol.debug_print("templateParams:", templateParams)
4607                Symbol.debug_print("id:            ", id_)
4608                if s is not None:
4609                    print(s.to_string(Symbol.debug_indent + 1), end="")
4610                else:
4611                    Symbol.debug_print("not found")
4612            if s is None:
4613                if Symbol.debug_lookup:
4614                    Symbol.debug_indent -= 2
4615                return None
4616        if Symbol.debug_lookup:
4617            Symbol.debug_indent -= 2
4618        return s
4619
4620    def find_name(self, nestedName: ASTNestedName, templateDecls: List[Any],
4621                  typ: str, templateShorthand: bool, matchSelf: bool,
4622                  recurseInAnon: bool, searchInSiblings: bool) -> Tuple[List["Symbol"], str]:
4623        # templateShorthand: missing template parameter lists for templates is ok
4624        # If the first component is None,
4625        # then the second component _may_ be a string explaining why.
4626        if Symbol.debug_lookup:
4627            Symbol.debug_indent += 1
4628            Symbol.debug_print("find_name:")
4629            Symbol.debug_indent += 1
4630            Symbol.debug_print("self:")
4631            print(self.to_string(Symbol.debug_indent + 1), end="")
4632            Symbol.debug_print("nestedName:       ", nestedName)
4633            Symbol.debug_print("templateDecls:    ", templateDecls)
4634            Symbol.debug_print("typ:              ", typ)
4635            Symbol.debug_print("templateShorthand:", templateShorthand)
4636            Symbol.debug_print("matchSelf:        ", matchSelf)
4637            Symbol.debug_print("recurseInAnon:    ", recurseInAnon)
4638            Symbol.debug_print("searchInSiblings: ", searchInSiblings)
4639
4640        class QualifiedSymbolIsTemplateParam(Exception):
4641            pass
4642
4643        def onMissingQualifiedSymbol(parentSymbol: "Symbol",
4644                                     identOrOp: Union[ASTIdentifier, ASTOperator],
4645                                     templateParams: Any,
4646                                     templateArgs: ASTTemplateArgs) -> "Symbol":
4647            # TODO: Maybe search without template args?
4648            #       Though, the correctPrimaryTemplateArgs does
4649            #       that for primary templates.
4650            #       Is there another case where it would be good?
4651            if parentSymbol.declaration is not None:
4652                if parentSymbol.declaration.objectType == 'templateParam':
4653                    raise QualifiedSymbolIsTemplateParam()
4654            return None
4655
4656        try:
4657            lookupResult = self._symbol_lookup(nestedName, templateDecls,
4658                                               onMissingQualifiedSymbol,
4659                                               strictTemplateParamArgLists=False,
4660                                               ancestorLookupType=typ,
4661                                               templateShorthand=templateShorthand,
4662                                               matchSelf=matchSelf,
4663                                               recurseInAnon=recurseInAnon,
4664                                               correctPrimaryTemplateArgs=False,
4665                                               searchInSiblings=searchInSiblings)
4666        except QualifiedSymbolIsTemplateParam:
4667            return None, "templateParamInQualified"
4668
4669        if lookupResult is None:
4670            # if it was a part of the qualification that could not be found
4671            if Symbol.debug_lookup:
4672                Symbol.debug_indent -= 2
4673            return None, None
4674
4675        res = list(lookupResult.symbols)
4676        if len(res) != 0:
4677            if Symbol.debug_lookup:
4678                Symbol.debug_indent -= 2
4679            return res, None
4680
4681        if lookupResult.parentSymbol.declaration is not None:
4682            if lookupResult.parentSymbol.declaration.objectType == 'templateParam':
4683                return None, "templateParamInQualified"
4684
4685        # try without template params and args
4686        symbol = lookupResult.parentSymbol._find_first_named_symbol(
4687            lookupResult.identOrOp, None, None,
4688            templateShorthand=templateShorthand, matchSelf=matchSelf,
4689            recurseInAnon=recurseInAnon, correctPrimaryTemplateArgs=False)
4690        if Symbol.debug_lookup:
4691            Symbol.debug_indent -= 2
4692        if symbol is not None:
4693            return [symbol], None
4694        else:
4695            return None, None
4696
4697    def find_declaration(self, declaration: ASTDeclaration, typ: str, templateShorthand: bool,
4698                         matchSelf: bool, recurseInAnon: bool) -> "Symbol":
4699        # templateShorthand: missing template parameter lists for templates is ok
4700        if Symbol.debug_lookup:
4701            Symbol.debug_indent += 1
4702            Symbol.debug_print("find_declaration:")
4703        nestedName = declaration.name
4704        if declaration.templatePrefix:
4705            templateDecls = declaration.templatePrefix.templates
4706        else:
4707            templateDecls = []
4708
4709        def onMissingQualifiedSymbol(parentSymbol: "Symbol",
4710                                     identOrOp: Union[ASTIdentifier, ASTOperator],
4711                                     templateParams: Any,
4712                                     templateArgs: ASTTemplateArgs) -> "Symbol":
4713            return None
4714
4715        lookupResult = self._symbol_lookup(nestedName, templateDecls,
4716                                           onMissingQualifiedSymbol,
4717                                           strictTemplateParamArgLists=False,
4718                                           ancestorLookupType=typ,
4719                                           templateShorthand=templateShorthand,
4720                                           matchSelf=matchSelf,
4721                                           recurseInAnon=recurseInAnon,
4722                                           correctPrimaryTemplateArgs=False,
4723                                           searchInSiblings=False)
4724        if Symbol.debug_lookup:
4725            Symbol.debug_indent -= 1
4726        if lookupResult is None:
4727            return None
4728
4729        symbols = list(lookupResult.symbols)
4730        if len(symbols) == 0:
4731            return None
4732
4733        querySymbol = Symbol(parent=lookupResult.parentSymbol,
4734                             identOrOp=lookupResult.identOrOp,
4735                             templateParams=lookupResult.templateParams,
4736                             templateArgs=lookupResult.templateArgs,
4737                             declaration=declaration,
4738                             docname='fakeDocnameForQuery',
4739                             line=42)
4740        queryId = declaration.get_newest_id()
4741        for symbol in symbols:
4742            if symbol.declaration is None:
4743                continue
4744            candId = symbol.declaration.get_newest_id()
4745            if candId == queryId:
4746                querySymbol.remove()
4747                return symbol
4748        querySymbol.remove()
4749        return None
4750
4751    def to_string(self, indent: int) -> str:
4752        res = [Symbol.debug_indent_string * indent]
4753        if not self.parent:
4754            res.append('::')
4755        else:
4756            if self.templateParams:
4757                res.append(str(self.templateParams))
4758                res.append('\n')
4759                res.append(Symbol.debug_indent_string * indent)
4760            if self.identOrOp:
4761                res.append(str(self.identOrOp))
4762            else:
4763                res.append(str(self.declaration))
4764            if self.templateArgs:
4765                res.append(str(self.templateArgs))
4766            if self.declaration:
4767                res.append(": ")
4768                if self.isRedeclaration:
4769                    res.append('!!duplicate!! ')
4770                res.append(str(self.declaration))
4771        if self.docname:
4772            res.append('\t(')
4773            res.append(self.docname)
4774            res.append(')')
4775        res.append('\n')
4776        return ''.join(res)
4777
4778    def dump(self, indent: int) -> str:
4779        res = [self.to_string(indent)]
4780        for c in self._children:
4781            res.append(c.dump(indent + 1))
4782        return ''.join(res)
4783
4784
4785class DefinitionParser(BaseParser):
4786    # those without signedness and size modifiers
4787    # see https://en.cppreference.com/w/cpp/language/types
4788    _simple_fundemental_types = (
4789        'void', 'bool', 'char', 'wchar_t', 'char16_t', 'char32_t', 'int',
4790        'float', 'double', 'auto'
4791    )
4792
4793    _prefix_keys = ('class', 'struct', 'enum', 'union', 'typename')
4794
4795    @property
4796    def language(self) -> str:
4797        return 'C++'
4798
4799    @property
4800    def id_attributes(self):
4801        return self.config.cpp_id_attributes
4802
4803    @property
4804    def paren_attributes(self):
4805        return self.config.cpp_paren_attributes
4806
4807    def _parse_string(self) -> str:
4808        if self.current_char != '"':
4809            return None
4810        startPos = self.pos
4811        self.pos += 1
4812        escape = False
4813        while True:
4814            if self.eof:
4815                self.fail("Unexpected end during inside string.")
4816            elif self.current_char == '"' and not escape:
4817                self.pos += 1
4818                break
4819            elif self.current_char == '\\':
4820                escape = True
4821            else:
4822                escape = False
4823            self.pos += 1
4824        return self.definition[startPos:self.pos]
4825
4826    def _parse_literal(self) -> ASTLiteral:
4827        # -> integer-literal
4828        #  | character-literal
4829        #  | floating-literal
4830        #  | string-literal
4831        #  | boolean-literal -> "false" | "true"
4832        #  | pointer-literal -> "nullptr"
4833        #  | user-defined-literal
4834
4835        def _udl(literal: ASTLiteral) -> ASTLiteral:
4836            if not self.match(udl_identifier_re):
4837                return literal
4838            # hmm, should we care if it's a keyword?
4839            # it looks like GCC does not disallow keywords
4840            ident = ASTIdentifier(self.matched_text)
4841            return ASTUserDefinedLiteral(literal, ident)
4842
4843        self.skip_ws()
4844        if self.skip_word('nullptr'):
4845            return ASTPointerLiteral()
4846        if self.skip_word('true'):
4847            return ASTBooleanLiteral(True)
4848        if self.skip_word('false'):
4849            return ASTBooleanLiteral(False)
4850        pos = self.pos
4851        if self.match(float_literal_re):
4852            hasSuffix = self.match(float_literal_suffix_re)
4853            floatLit = ASTNumberLiteral(self.definition[pos:self.pos])
4854            if hasSuffix:
4855                return floatLit
4856            else:
4857                return _udl(floatLit)
4858        for regex in [binary_literal_re, hex_literal_re,
4859                      integer_literal_re, octal_literal_re]:
4860            if self.match(regex):
4861                hasSuffix = self.match(integers_literal_suffix_re)
4862                intLit = ASTNumberLiteral(self.definition[pos:self.pos])
4863                if hasSuffix:
4864                    return intLit
4865                else:
4866                    return _udl(intLit)
4867
4868        string = self._parse_string()
4869        if string is not None:
4870            return _udl(ASTStringLiteral(string))
4871
4872        # character-literal
4873        if self.match(char_literal_re):
4874            prefix = self.last_match.group(1)  # may be None when no prefix
4875            data = self.last_match.group(2)
4876            try:
4877                charLit = ASTCharLiteral(prefix, data)
4878            except UnicodeDecodeError as e:
4879                self.fail("Can not handle character literal. Internal error was: %s" % e)
4880            except UnsupportedMultiCharacterCharLiteral:
4881                self.fail("Can not handle character literal"
4882                          " resulting in multiple decoded characters.")
4883            return _udl(charLit)
4884        return None
4885
4886    def _parse_fold_or_paren_expression(self) -> ASTExpression:
4887        # "(" expression ")"
4888        # fold-expression
4889        # -> ( cast-expression fold-operator ... )
4890        #  | ( ... fold-operator cast-expression )
4891        #  | ( cast-expression fold-operator ... fold-operator cast-expression
4892        if self.current_char != '(':
4893            return None
4894        self.pos += 1
4895        self.skip_ws()
4896        if self.skip_string_and_ws("..."):
4897            # ( ... fold-operator cast-expression )
4898            if not self.match(_fold_operator_re):
4899                self.fail("Expected fold operator after '...' in fold expression.")
4900            op = self.matched_text
4901            rightExpr = self._parse_cast_expression()
4902            if not self.skip_string(')'):
4903                self.fail("Expected ')' in end of fold expression.")
4904            return ASTFoldExpr(None, op, rightExpr)
4905        # try first parsing a unary right fold, or a binary fold
4906        pos = self.pos
4907        try:
4908            self.skip_ws()
4909            leftExpr = self._parse_cast_expression()
4910            self.skip_ws()
4911            if not self.match(_fold_operator_re):
4912                self.fail("Expected fold operator after left expression in fold expression.")
4913            op = self.matched_text
4914            self.skip_ws()
4915            if not self.skip_string_and_ws('...'):
4916                self.fail("Expected '...' after fold operator in fold expression.")
4917        except DefinitionError as eFold:
4918            self.pos = pos
4919            # fall back to a paren expression
4920            try:
4921                res = self._parse_expression()
4922                self.skip_ws()
4923                if not self.skip_string(')'):
4924                    self.fail("Expected ')' in end of parenthesized expression.")
4925            except DefinitionError as eExpr:
4926                raise self._make_multi_error([
4927                    (eFold, "If fold expression"),
4928                    (eExpr, "If parenthesized expression")
4929                ], "Error in fold expression or parenthesized expression.") from eExpr
4930            return ASTParenExpr(res)
4931        # now it definitely is a fold expression
4932        if self.skip_string(')'):
4933            return ASTFoldExpr(leftExpr, op, None)
4934        if not self.match(_fold_operator_re):
4935            self.fail("Expected fold operator or ')' after '...' in fold expression.")
4936        if op != self.matched_text:
4937            self.fail("Operators are different in binary fold: '%s' and '%s'."
4938                      % (op, self.matched_text))
4939        rightExpr = self._parse_cast_expression()
4940        self.skip_ws()
4941        if not self.skip_string(')'):
4942            self.fail("Expected ')' to end binary fold expression.")
4943        return ASTFoldExpr(leftExpr, op, rightExpr)
4944
4945    def _parse_primary_expression(self) -> ASTExpression:
4946        # literal
4947        # "this"
4948        # lambda-expression
4949        # "(" expression ")"
4950        # fold-expression
4951        # id-expression -> we parse this with _parse_nested_name
4952        self.skip_ws()
4953        res = self._parse_literal()  # type: ASTExpression
4954        if res is not None:
4955            return res
4956        self.skip_ws()
4957        if self.skip_word("this"):
4958            return ASTThisLiteral()
4959        # TODO: try lambda expression
4960        res = self._parse_fold_or_paren_expression()
4961        if res is not None:
4962            return res
4963        nn = self._parse_nested_name()
4964        if nn is not None:
4965            return ASTIdExpression(nn)
4966        return None
4967
4968    def _parse_initializer_list(self, name: str, open: str, close: str
4969                                ) -> Tuple[List[Union[ASTExpression,
4970                                                      ASTBracedInitList]],
4971                                           bool]:
4972        # Parse open and close with the actual initializer-list inbetween
4973        # -> initializer-clause '...'[opt]
4974        #  | initializer-list ',' initializer-clause '...'[opt]
4975        self.skip_ws()
4976        if not self.skip_string_and_ws(open):
4977            return None, None
4978        if self.skip_string(close):
4979            return [], False
4980
4981        exprs = []  # type: List[Union[ASTExpression, ASTBracedInitList]]
4982        trailingComma = False
4983        while True:
4984            self.skip_ws()
4985            expr = self._parse_initializer_clause()
4986            self.skip_ws()
4987            if self.skip_string('...'):
4988                exprs.append(ASTPackExpansionExpr(expr))
4989            else:
4990                exprs.append(expr)
4991            self.skip_ws()
4992            if self.skip_string(close):
4993                break
4994            if not self.skip_string_and_ws(','):
4995                self.fail("Error in %s, expected ',' or '%s'." % (name, close))
4996            if self.current_char == close and close == '}':
4997                self.pos += 1
4998                trailingComma = True
4999                break
5000        return exprs, trailingComma
5001
5002    def _parse_paren_expression_list(self) -> ASTParenExprList:
5003        # -> '(' expression-list ')'
5004        # though, we relax it to also allow empty parens
5005        # as it's needed in some cases
5006        #
5007        # expression-list
5008        # -> initializer-list
5009        exprs, trailingComma = self._parse_initializer_list("parenthesized expression-list",
5010                                                            '(', ')')
5011        if exprs is None:
5012            return None
5013        return ASTParenExprList(exprs)
5014
5015    def _parse_initializer_clause(self) -> Union[ASTExpression, ASTBracedInitList]:
5016        bracedInitList = self._parse_braced_init_list()
5017        if bracedInitList is not None:
5018            return bracedInitList
5019        return self._parse_assignment_expression(inTemplate=False)
5020
5021    def _parse_braced_init_list(self) -> ASTBracedInitList:
5022        # -> '{' initializer-list ','[opt] '}'
5023        #  | '{' '}'
5024        exprs, trailingComma = self._parse_initializer_list("braced-init-list", '{', '}')
5025        if exprs is None:
5026            return None
5027        return ASTBracedInitList(exprs, trailingComma)
5028
5029    def _parse_expression_list_or_braced_init_list(
5030        self
5031    ) -> Union[ASTParenExprList, ASTBracedInitList]:
5032        paren = self._parse_paren_expression_list()
5033        if paren is not None:
5034            return paren
5035        return self._parse_braced_init_list()
5036
5037    def _parse_postfix_expression(self) -> ASTPostfixExpr:
5038        # -> primary
5039        #  | postfix "[" expression "]"
5040        #  | postfix "[" braced-init-list [opt] "]"
5041        #  | postfix "(" expression-list [opt] ")"
5042        #  | postfix "." "template" [opt] id-expression
5043        #  | postfix "->" "template" [opt] id-expression
5044        #  | postfix "." pseudo-destructor-name
5045        #  | postfix "->" pseudo-destructor-name
5046        #  | postfix "++"
5047        #  | postfix "--"
5048        #  | simple-type-specifier "(" expression-list [opt] ")"
5049        #  | simple-type-specifier braced-init-list
5050        #  | typename-specifier "(" expression-list [opt] ")"
5051        #  | typename-specifier braced-init-list
5052        #  | "dynamic_cast" "<" type-id ">" "(" expression ")"
5053        #  | "static_cast" "<" type-id ">" "(" expression ")"
5054        #  | "reinterpret_cast" "<" type-id ">" "(" expression ")"
5055        #  | "const_cast" "<" type-id ">" "(" expression ")"
5056        #  | "typeid" "(" expression ")"
5057        #  | "typeid" "(" type-id ")"
5058
5059        prefixType = None
5060        prefix = None  # type: Any
5061        self.skip_ws()
5062
5063        cast = None
5064        for c in _id_explicit_cast:
5065            if self.skip_word_and_ws(c):
5066                cast = c
5067                break
5068        if cast is not None:
5069            prefixType = "cast"
5070            if not self.skip_string("<"):
5071                self.fail("Expected '<' afer '%s'." % cast)
5072            typ = self._parse_type(False)
5073            self.skip_ws()
5074            if not self.skip_string_and_ws(">"):
5075                self.fail("Expected '>' after type in '%s'." % cast)
5076            if not self.skip_string("("):
5077                self.fail("Expected '(' in '%s'." % cast)
5078
5079            def parser() -> ASTExpression:
5080                return self._parse_expression()
5081            expr = self._parse_expression_fallback([')'], parser)
5082            self.skip_ws()
5083            if not self.skip_string(")"):
5084                self.fail("Expected ')' to end '%s'." % cast)
5085            prefix = ASTExplicitCast(cast, typ, expr)
5086        elif self.skip_word_and_ws("typeid"):
5087            prefixType = "typeid"
5088            if not self.skip_string_and_ws('('):
5089                self.fail("Expected '(' after 'typeid'.")
5090            pos = self.pos
5091            try:
5092                typ = self._parse_type(False)
5093                prefix = ASTTypeId(typ, isType=True)
5094                if not self.skip_string(')'):
5095                    self.fail("Expected ')' to end 'typeid' of type.")
5096            except DefinitionError as eType:
5097                self.pos = pos
5098                try:
5099
5100                    def parser() -> ASTExpression:
5101                        return self._parse_expression()
5102                    expr = self._parse_expression_fallback([')'], parser)
5103                    prefix = ASTTypeId(expr, isType=False)
5104                    if not self.skip_string(')'):
5105                        self.fail("Expected ')' to end 'typeid' of expression.")
5106                except DefinitionError as eExpr:
5107                    self.pos = pos
5108                    header = "Error in 'typeid(...)'."
5109                    header += " Expected type or expression."
5110                    errors = []
5111                    errors.append((eType, "If type"))
5112                    errors.append((eExpr, "If expression"))
5113                    raise self._make_multi_error(errors, header) from eExpr
5114        else:  # a primary expression or a type
5115            pos = self.pos
5116            try:
5117                prefix = self._parse_primary_expression()
5118                prefixType = 'expr'
5119            except DefinitionError as eOuter:
5120                self.pos = pos
5121                try:
5122                    # we are potentially casting, so save parens for us
5123                    # TODO: hmm, would we need to try both with operatorCast and with None?
5124                    prefix = self._parse_type(False, 'operatorCast')
5125                    prefixType = 'typeOperatorCast'
5126                    #  | simple-type-specifier "(" expression-list [opt] ")"
5127                    #  | simple-type-specifier braced-init-list
5128                    #  | typename-specifier "(" expression-list [opt] ")"
5129                    #  | typename-specifier braced-init-list
5130                    self.skip_ws()
5131                    if self.current_char != '(' and self.current_char != '{':
5132                        self.fail("Expecting '(' or '{' after type in cast expression.")
5133                except DefinitionError as eInner:
5134                    self.pos = pos
5135                    header = "Error in postfix expression,"
5136                    header += " expected primary expression or type."
5137                    errors = []
5138                    errors.append((eOuter, "If primary expression"))
5139                    errors.append((eInner, "If type"))
5140                    raise self._make_multi_error(errors, header) from eInner
5141
5142        # and now parse postfixes
5143        postFixes = []  # type: List[ASTPostfixOp]
5144        while True:
5145            self.skip_ws()
5146            if prefixType in ['expr', 'cast', 'typeid']:
5147                if self.skip_string_and_ws('['):
5148                    expr = self._parse_expression()
5149                    self.skip_ws()
5150                    if not self.skip_string(']'):
5151                        self.fail("Expected ']' in end of postfix expression.")
5152                    postFixes.append(ASTPostfixArray(expr))
5153                    continue
5154                if self.skip_string('.'):
5155                    if self.skip_string('*'):
5156                        # don't steal the dot
5157                        self.pos -= 2
5158                    elif self.skip_string('..'):
5159                        # don't steal the dot
5160                        self.pos -= 3
5161                    else:
5162                        name = self._parse_nested_name()
5163                        postFixes.append(ASTPostfixMember(name))
5164                        continue
5165                if self.skip_string('->'):
5166                    if self.skip_string('*'):
5167                        # don't steal the arrow
5168                        self.pos -= 3
5169                    else:
5170                        name = self._parse_nested_name()
5171                        postFixes.append(ASTPostfixMemberOfPointer(name))
5172                        continue
5173                if self.skip_string('++'):
5174                    postFixes.append(ASTPostfixInc())
5175                    continue
5176                if self.skip_string('--'):
5177                    postFixes.append(ASTPostfixDec())
5178                    continue
5179            lst = self._parse_expression_list_or_braced_init_list()
5180            if lst is not None:
5181                postFixes.append(ASTPostfixCallExpr(lst))
5182                continue
5183            break
5184        return ASTPostfixExpr(prefix, postFixes)
5185
5186    def _parse_unary_expression(self) -> ASTExpression:
5187        # -> postfix
5188        #  | "++" cast
5189        #  | "--" cast
5190        #  | unary-operator cast -> (* | & | + | - | ! | ~) cast
5191        # The rest:
5192        #  | "sizeof" unary
5193        #  | "sizeof" "(" type-id ")"
5194        #  | "sizeof" "..." "(" identifier ")"
5195        #  | "alignof" "(" type-id ")"
5196        #  | noexcept-expression -> noexcept "(" expression ")"
5197        #  | new-expression
5198        #  | delete-expression
5199        self.skip_ws()
5200        for op in _expression_unary_ops:
5201            # TODO: hmm, should we be able to backtrack here?
5202            if op[0] in 'cn':
5203                res = self.skip_word(op)
5204            else:
5205                res = self.skip_string(op)
5206            if res:
5207                expr = self._parse_cast_expression()
5208                return ASTUnaryOpExpr(op, expr)
5209        if self.skip_word_and_ws('sizeof'):
5210            if self.skip_string_and_ws('...'):
5211                if not self.skip_string_and_ws('('):
5212                    self.fail("Expecting '(' after 'sizeof...'.")
5213                if not self.match(identifier_re):
5214                    self.fail("Expecting identifier for 'sizeof...'.")
5215                ident = ASTIdentifier(self.matched_text)
5216                self.skip_ws()
5217                if not self.skip_string(")"):
5218                    self.fail("Expecting ')' to end 'sizeof...'.")
5219                return ASTSizeofParamPack(ident)
5220            if self.skip_string_and_ws('('):
5221                typ = self._parse_type(named=False)
5222                self.skip_ws()
5223                if not self.skip_string(')'):
5224                    self.fail("Expecting ')' to end 'sizeof'.")
5225                return ASTSizeofType(typ)
5226            expr = self._parse_unary_expression()
5227            return ASTSizeofExpr(expr)
5228        if self.skip_word_and_ws('alignof'):
5229            if not self.skip_string_and_ws('('):
5230                self.fail("Expecting '(' after 'alignof'.")
5231            typ = self._parse_type(named=False)
5232            self.skip_ws()
5233            if not self.skip_string(')'):
5234                self.fail("Expecting ')' to end 'alignof'.")
5235            return ASTAlignofExpr(typ)
5236        if self.skip_word_and_ws('noexcept'):
5237            if not self.skip_string_and_ws('('):
5238                self.fail("Expecting '(' after 'noexcept'.")
5239            expr = self._parse_expression()
5240            self.skip_ws()
5241            if not self.skip_string(')'):
5242                self.fail("Expecting ')' to end 'noexcept'.")
5243            return ASTNoexceptExpr(expr)
5244        # new-expression
5245        pos = self.pos
5246        rooted = self.skip_string('::')
5247        self.skip_ws()
5248        if not self.skip_word_and_ws('new'):
5249            self.pos = pos
5250        else:
5251            # new-placement[opt] new-type-id new-initializer[opt]
5252            # new-placement[opt] ( type-id ) new-initializer[opt]
5253            isNewTypeId = True
5254            if self.skip_string_and_ws('('):
5255                # either this is a new-placement or it's the second production
5256                # without placement, and it's actually the ( type-id ) part
5257                self.fail("Sorry, neither new-placement nor parenthesised type-id "
5258                          "in new-epression is supported yet.")
5259                # set isNewTypeId = False if it's (type-id)
5260            if isNewTypeId:
5261                declSpecs = self._parse_decl_specs(outer=None)
5262                decl = self._parse_declarator(named=False, paramMode="new")
5263            else:
5264                self.fail("Sorry, parenthesised type-id in new expression not yet supported.")
5265            lst = self._parse_expression_list_or_braced_init_list()
5266            return ASTNewExpr(rooted, isNewTypeId, ASTType(declSpecs, decl), lst)
5267        # delete-expression
5268        pos = self.pos
5269        rooted = self.skip_string('::')
5270        self.skip_ws()
5271        if not self.skip_word_and_ws('delete'):
5272            self.pos = pos
5273        else:
5274            array = self.skip_string_and_ws('[')
5275            if array and not self.skip_string_and_ws(']'):
5276                self.fail("Expected ']' in array delete-expression.")
5277            expr = self._parse_cast_expression()
5278            return ASTDeleteExpr(rooted, array, expr)
5279        return self._parse_postfix_expression()
5280
5281    def _parse_cast_expression(self) -> ASTExpression:
5282        # -> unary  | "(" type-id ")" cast
5283        pos = self.pos
5284        self.skip_ws()
5285        if self.skip_string('('):
5286            try:
5287                typ = self._parse_type(False)
5288                if not self.skip_string(')'):
5289                    self.fail("Expected ')' in cast expression.")
5290                expr = self._parse_cast_expression()
5291                return ASTCastExpr(typ, expr)
5292            except DefinitionError as exCast:
5293                self.pos = pos
5294                try:
5295                    return self._parse_unary_expression()
5296                except DefinitionError as exUnary:
5297                    errs = []
5298                    errs.append((exCast, "If type cast expression"))
5299                    errs.append((exUnary, "If unary expression"))
5300                    raise self._make_multi_error(errs,
5301                                                 "Error in cast expression.") from exUnary
5302        else:
5303            return self._parse_unary_expression()
5304
5305    def _parse_logical_or_expression(self, inTemplate: bool) -> ASTExpression:
5306        # logical-or     = logical-and      ||
5307        # logical-and    = inclusive-or     &&
5308        # inclusive-or   = exclusive-or     |
5309        # exclusive-or   = and              ^
5310        # and            = equality         &
5311        # equality       = relational       ==, !=
5312        # relational     = shift            <, >, <=, >=
5313        # shift          = additive         <<, >>
5314        # additive       = multiplicative   +, -
5315        # multiplicative = pm               *, /, %
5316        # pm             = cast             .*, ->*
5317        def _parse_bin_op_expr(self: DefinitionParser,
5318                               opId: int, inTemplate: bool) -> ASTExpression:
5319            if opId + 1 == len(_expression_bin_ops):
5320                def parser(inTemplate: bool) -> ASTExpression:
5321                    return self._parse_cast_expression()
5322            else:
5323                def parser(inTemplate: bool) -> ASTExpression:
5324                    return _parse_bin_op_expr(self, opId + 1, inTemplate=inTemplate)
5325            exprs = []
5326            ops = []
5327            exprs.append(parser(inTemplate=inTemplate))
5328            while True:
5329                self.skip_ws()
5330                if inTemplate and self.current_char == '>':
5331                    break
5332                pos = self.pos
5333                oneMore = False
5334                for op in _expression_bin_ops[opId]:
5335                    if op[0] in 'abcnox':
5336                        if not self.skip_word(op):
5337                            continue
5338                    else:
5339                        if not self.skip_string(op):
5340                            continue
5341                    if op == '&' and self.current_char == '&':
5342                        # don't split the && 'token'
5343                        self.pos -= 1
5344                        # and btw. && has lower precedence, so we are done
5345                        break
5346                    try:
5347                        expr = parser(inTemplate=inTemplate)
5348                        exprs.append(expr)
5349                        ops.append(op)
5350                        oneMore = True
5351                        break
5352                    except DefinitionError:
5353                        self.pos = pos
5354                if not oneMore:
5355                    break
5356            return ASTBinOpExpr(exprs, ops)
5357        return _parse_bin_op_expr(self, 0, inTemplate=inTemplate)
5358
5359    def _parse_conditional_expression_tail(self, orExprHead: Any) -> None:
5360        # -> "?" expression ":" assignment-expression
5361        return None
5362
5363    def _parse_assignment_expression(self, inTemplate: bool) -> ASTExpression:
5364        # -> conditional-expression
5365        #  | logical-or-expression assignment-operator initializer-clause
5366        #  | throw-expression
5367        # TODO: parse throw-expression: "throw" assignment-expression [opt]
5368        # if not a throw expression, then:
5369        # -> conditional-expression ->
5370        #     logical-or-expression
5371        #   | logical-or-expression "?" expression ":" assignment-expression
5372        #   | logical-or-expression assignment-operator initializer-clause
5373        exprs = []  # type: List[Union[ASTExpression, ASTBracedInitList]]
5374        ops = []
5375        orExpr = self._parse_logical_or_expression(inTemplate=inTemplate)
5376        exprs.append(orExpr)
5377        # TODO: handle ternary with _parse_conditional_expression_tail
5378        while True:
5379            oneMore = False
5380            self.skip_ws()
5381            for op in _expression_assignment_ops:
5382                if op[0] in 'anox':
5383                    if not self.skip_word(op):
5384                        continue
5385                else:
5386                    if not self.skip_string(op):
5387                        continue
5388                expr = self._parse_initializer_clause()
5389                exprs.append(expr)
5390                ops.append(op)
5391                oneMore = True
5392            if not oneMore:
5393                break
5394        if len(ops) == 0:
5395            return orExpr
5396        else:
5397            return ASTAssignmentExpr(exprs, ops)
5398
5399    def _parse_constant_expression(self, inTemplate: bool) -> ASTExpression:
5400        # -> conditional-expression
5401        orExpr = self._parse_logical_or_expression(inTemplate=inTemplate)
5402        # TODO: use _parse_conditional_expression_tail
5403        return orExpr
5404
5405    def _parse_expression(self) -> ASTExpression:
5406        # -> assignment-expression
5407        #  | expression "," assignment-expresion
5408        exprs = [self._parse_assignment_expression(inTemplate=False)]
5409        while True:
5410            self.skip_ws()
5411            if not self.skip_string(','):
5412                break
5413            exprs.append(self._parse_assignment_expression(inTemplate=False))
5414        if len(exprs) == 1:
5415            return exprs[0]
5416        else:
5417            return ASTCommaExpr(exprs)
5418
5419    def _parse_expression_fallback(self, end: List[str],
5420                                   parser: Callable[[], ASTExpression],
5421                                   allow: bool = True) -> ASTExpression:
5422        # Stupidly "parse" an expression.
5423        # 'end' should be a list of characters which ends the expression.
5424
5425        # first try to use the provided parser
5426        prevPos = self.pos
5427        try:
5428            return parser()
5429        except DefinitionError as e:
5430            # some places (e.g., template parameters) we really don't want to use fallback,
5431            # and for testing we may want to globally disable it
5432            if not allow or not self.allowFallbackExpressionParsing:
5433                raise
5434            self.warn("Parsing of expression failed. Using fallback parser."
5435                      " Error was:\n%s" % e)
5436            self.pos = prevPos
5437        # and then the fallback scanning
5438        assert end is not None
5439        self.skip_ws()
5440        startPos = self.pos
5441        if self.match(_string_re):
5442            value = self.matched_text
5443        else:
5444            # TODO: add handling of more bracket-like things, and quote handling
5445            brackets = {'(': ')', '{': '}', '[': ']', '<': '>'}
5446            symbols = []  # type: List[str]
5447            while not self.eof:
5448                if (len(symbols) == 0 and self.current_char in end):
5449                    break
5450                if self.current_char in brackets.keys():
5451                    symbols.append(brackets[self.current_char])
5452                elif len(symbols) > 0 and self.current_char == symbols[-1]:
5453                    symbols.pop()
5454                self.pos += 1
5455            if len(end) > 0 and self.eof:
5456                self.fail("Could not find end of expression starting at %d."
5457                          % startPos)
5458            value = self.definition[startPos:self.pos].strip()
5459        return ASTFallbackExpr(value.strip())
5460
5461    # ==========================================================================
5462
5463    def _parse_operator(self) -> ASTOperator:
5464        self.skip_ws()
5465        # adapted from the old code
5466        # yay, a regular operator definition
5467        if self.match(_operator_re):
5468            return ASTOperatorBuildIn(self.matched_text)
5469
5470        # new/delete operator?
5471        for op in 'new', 'delete':
5472            if not self.skip_word(op):
5473                continue
5474            self.skip_ws()
5475            if self.skip_string('['):
5476                self.skip_ws()
5477                if not self.skip_string(']'):
5478                    self.fail('Expected "]" after  "operator ' + op + '["')
5479                op += '[]'
5480            return ASTOperatorBuildIn(op)
5481
5482        # user-defined literal?
5483        if self.skip_string('""'):
5484            self.skip_ws()
5485            if not self.match(identifier_re):
5486                self.fail("Expected user-defined literal suffix.")
5487            identifier = ASTIdentifier(self.matched_text)
5488            return ASTOperatorLiteral(identifier)
5489
5490        # oh well, looks like a cast operator definition.
5491        # In that case, eat another type.
5492        type = self._parse_type(named=False, outer="operatorCast")
5493        return ASTOperatorType(type)
5494
5495    def _parse_template_argument_list(self) -> ASTTemplateArgs:
5496        # template-argument-list: (but we include the < and > here
5497        #    template-argument ...[opt]
5498        #    template-argument-list, template-argument ...[opt]
5499        # template-argument:
5500        #    constant-expression
5501        #    type-id
5502        #    id-expression
5503        self.skip_ws()
5504        if not self.skip_string_and_ws('<'):
5505            return None
5506        if self.skip_string('>'):
5507            return ASTTemplateArgs([], False)
5508        prevErrors = []
5509        templateArgs = []  # type: List[Union[ASTType, ASTTemplateArgConstant]]
5510        packExpansion = False
5511        while 1:
5512            pos = self.pos
5513            parsedComma = False
5514            parsedEnd = False
5515            try:
5516                type = self._parse_type(named=False)
5517                self.skip_ws()
5518                if self.skip_string_and_ws('...'):
5519                    packExpansion = True
5520                    parsedEnd = True
5521                    if not self.skip_string('>'):
5522                        self.fail('Expected ">" after "..." in template argument list.')
5523                elif self.skip_string('>'):
5524                    parsedEnd = True
5525                elif self.skip_string(','):
5526                    parsedComma = True
5527                else:
5528                    self.fail('Expected "...>", ">" or "," in template argument list.')
5529                templateArgs.append(type)
5530            except DefinitionError as e:
5531                prevErrors.append((e, "If type argument"))
5532                self.pos = pos
5533                try:
5534                    value = self._parse_constant_expression(inTemplate=True)
5535                    self.skip_ws()
5536                    if self.skip_string_and_ws('...'):
5537                        packExpansion = True
5538                        parsedEnd = True
5539                        if not self.skip_string('>'):
5540                            self.fail('Expected ">" after "..." in template argument list.')
5541                    elif self.skip_string('>'):
5542                        parsedEnd = True
5543                    elif self.skip_string(','):
5544                        parsedComma = True
5545                    else:
5546                        self.fail('Expected "...>", ">" or "," in template argument list.')
5547                    templateArgs.append(ASTTemplateArgConstant(value))
5548                except DefinitionError as e:
5549                    self.pos = pos
5550                    prevErrors.append((e, "If non-type argument"))
5551                    header = "Error in parsing template argument list."
5552                    raise self._make_multi_error(prevErrors, header) from e
5553            if parsedEnd:
5554                assert not parsedComma
5555                break
5556            else:
5557                assert not packExpansion
5558        return ASTTemplateArgs(templateArgs, packExpansion)
5559
5560    def _parse_nested_name(self, memberPointer: bool = False) -> ASTNestedName:
5561        names = []  # type: List[ASTNestedNameElement]
5562        templates = []  # type: List[bool]
5563
5564        self.skip_ws()
5565        rooted = False
5566        if self.skip_string('::'):
5567            rooted = True
5568        while 1:
5569            self.skip_ws()
5570            if len(names) > 0:
5571                template = self.skip_word_and_ws('template')
5572            else:
5573                template = False
5574            templates.append(template)
5575            identOrOp = None  # type: Union[ASTIdentifier, ASTOperator]
5576            if self.skip_word_and_ws('operator'):
5577                identOrOp = self._parse_operator()
5578            else:
5579                if not self.match(identifier_re):
5580                    if memberPointer and len(names) > 0:
5581                        templates.pop()
5582                        break
5583                    self.fail("Expected identifier in nested name.")
5584                identifier = self.matched_text
5585                # make sure there isn't a keyword
5586                if identifier in _keywords:
5587                    self.fail("Expected identifier in nested name, "
5588                              "got keyword: %s" % identifier)
5589                identOrOp = ASTIdentifier(identifier)
5590            # try greedily to get template arguments,
5591            # but otherwise a < might be because we are in an expression
5592            pos = self.pos
5593            try:
5594                templateArgs = self._parse_template_argument_list()
5595            except DefinitionError as ex:
5596                self.pos = pos
5597                templateArgs = None
5598                self.otherErrors.append(ex)
5599            names.append(ASTNestedNameElement(identOrOp, templateArgs))
5600
5601            self.skip_ws()
5602            if not self.skip_string('::'):
5603                if memberPointer:
5604                    self.fail("Expected '::' in pointer to member (function).")
5605                break
5606        return ASTNestedName(names, templates, rooted)
5607
5608    # ==========================================================================
5609
5610    def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec:
5611        # fundemental types
5612        self.skip_ws()
5613        for t in self._simple_fundemental_types:
5614            if self.skip_word(t):
5615                return ASTTrailingTypeSpecFundamental(t)
5616
5617        # TODO: this could/should be more strict
5618        elements = []
5619        if self.skip_word_and_ws('signed'):
5620            elements.append('signed')
5621        elif self.skip_word_and_ws('unsigned'):
5622            elements.append('unsigned')
5623        while 1:
5624            if self.skip_word_and_ws('short'):
5625                elements.append('short')
5626            elif self.skip_word_and_ws('long'):
5627                elements.append('long')
5628            else:
5629                break
5630        if self.skip_word_and_ws('char'):
5631            elements.append('char')
5632        elif self.skip_word_and_ws('int'):
5633            elements.append('int')
5634        elif self.skip_word_and_ws('double'):
5635            elements.append('double')
5636        if len(elements) > 0:
5637            return ASTTrailingTypeSpecFundamental(' '.join(elements))
5638
5639        # decltype
5640        self.skip_ws()
5641        if self.skip_word_and_ws('decltype'):
5642            if not self.skip_string_and_ws('('):
5643                self.fail("Expected '(' after 'decltype'.")
5644            if self.skip_word_and_ws('auto'):
5645                if not self.skip_string(')'):
5646                    self.fail("Expected ')' after 'decltype(auto'.")
5647                return ASTTrailingTypeSpecDecltypeAuto()
5648            expr = self._parse_expression()
5649            self.skip_ws()
5650            if not self.skip_string(')'):
5651                self.fail("Expected ')' after 'decltype(<expr>'.")
5652            return ASTTrailingTypeSpecDecltype(expr)
5653
5654        # prefixed
5655        prefix = None
5656        self.skip_ws()
5657        for k in self._prefix_keys:
5658            if self.skip_word_and_ws(k):
5659                prefix = k
5660                break
5661        nestedName = self._parse_nested_name()
5662        return ASTTrailingTypeSpecName(prefix, nestedName)
5663
5664    def _parse_parameters_and_qualifiers(self, paramMode: str) -> ASTParametersQualifiers:
5665        if paramMode == 'new':
5666            return None
5667        self.skip_ws()
5668        if not self.skip_string('('):
5669            if paramMode == 'function':
5670                self.fail('Expecting "(" in parameters-and-qualifiers.')
5671            else:
5672                return None
5673        args = []
5674        self.skip_ws()
5675        if not self.skip_string(')'):
5676            while 1:
5677                self.skip_ws()
5678                if self.skip_string('...'):
5679                    args.append(ASTFunctionParameter(None, True))
5680                    self.skip_ws()
5681                    if not self.skip_string(')'):
5682                        self.fail('Expected ")" after "..." in '
5683                                  'parameters-and-qualifiers.')
5684                    break
5685                # note: it seems that function arguments can always be named,
5686                # even in function pointers and similar.
5687                arg = self._parse_type_with_init(outer=None, named='single')
5688                # TODO: parse default parameters # TODO: didn't we just do that?
5689                args.append(ASTFunctionParameter(arg))
5690
5691                self.skip_ws()
5692                if self.skip_string(','):
5693                    continue
5694                elif self.skip_string(')'):
5695                    break
5696                else:
5697                    self.fail(
5698                        'Expecting "," or ")" in parameters-and-qualifiers, '
5699                        'got "%s".' % self.current_char)
5700
5701        # TODO: why did we have this bail-out?
5702        # does it hurt to parse the extra stuff?
5703        # it's needed for pointer to member functions
5704        if paramMode != 'function' and False:
5705            return ASTParametersQualifiers(
5706                args, None, None, None, None, None, None, None)
5707
5708        self.skip_ws()
5709        const = self.skip_word_and_ws('const')
5710        volatile = self.skip_word_and_ws('volatile')
5711        if not const:  # the can be permuted
5712            const = self.skip_word_and_ws('const')
5713
5714        refQual = None
5715        if self.skip_string('&&'):
5716            refQual = '&&'
5717        if not refQual and self.skip_string('&'):
5718            refQual = '&'
5719
5720        exceptionSpec = None
5721        self.skip_ws()
5722        if self.skip_string('noexcept'):
5723            if self.skip_string_and_ws('('):
5724                expr = self._parse_constant_expression(False)
5725                self.skip_ws()
5726                if not self.skip_string(')'):
5727                    self.fail("Expecting ')' to end 'noexcept'.")
5728                exceptionSpec = ASTNoexceptSpec(expr)
5729            else:
5730                exceptionSpec = ASTNoexceptSpec(None)
5731
5732        self.skip_ws()
5733        if self.skip_string('->'):
5734            trailingReturn = self._parse_type(named=False)
5735        else:
5736            trailingReturn = None
5737
5738        self.skip_ws()
5739        override = self.skip_word_and_ws('override')
5740        final = self.skip_word_and_ws('final')
5741        if not override:
5742            override = self.skip_word_and_ws(
5743                'override')  # they can be permuted
5744
5745        attrs = []
5746        while True:
5747            attr = self._parse_attribute()
5748            if attr is None:
5749                break
5750            attrs.append(attr)
5751
5752        self.skip_ws()
5753        initializer = None
5754        if self.skip_string('='):
5755            self.skip_ws()
5756            valid = ('0', 'delete', 'default')
5757            for w in valid:
5758                if self.skip_word_and_ws(w):
5759                    initializer = w
5760                    break
5761            if not initializer:
5762                self.fail(
5763                    'Expected "%s" in initializer-specifier.'
5764                    % '" or "'.join(valid))
5765
5766        return ASTParametersQualifiers(
5767            args, volatile, const, refQual, exceptionSpec, trailingReturn,
5768            override, final, attrs, initializer)
5769
5770    def _parse_decl_specs_simple(self, outer: str, typed: bool) -> ASTDeclSpecsSimple:
5771        """Just parse the simple ones."""
5772        storage = None
5773        threadLocal = None
5774        inline = None
5775        virtual = None
5776        explicit = None
5777        constexpr = None
5778        volatile = None
5779        const = None
5780        friend = None
5781        attrs = []
5782        while 1:  # accept any permutation of a subset of some decl-specs
5783            self.skip_ws()
5784            if not storage:
5785                if outer in ('member', 'function'):
5786                    if self.skip_word('static'):
5787                        storage = 'static'
5788                        continue
5789                    if self.skip_word('extern'):
5790                        storage = 'extern'
5791                        continue
5792                if outer == 'member':
5793                    if self.skip_word('mutable'):
5794                        storage = 'mutable'
5795                        continue
5796                if self.skip_word('register'):
5797                    storage = 'register'
5798                    continue
5799            if not threadLocal and outer == 'member':
5800                threadLocal = self.skip_word('thread_local')
5801                if threadLocal:
5802                    continue
5803
5804            if outer == 'function':
5805                # function-specifiers
5806                if not inline:
5807                    inline = self.skip_word('inline')
5808                    if inline:
5809                        continue
5810                if not friend:
5811                    friend = self.skip_word('friend')
5812                    if friend:
5813                        continue
5814                if not virtual:
5815                    virtual = self.skip_word('virtual')
5816                    if virtual:
5817                        continue
5818                if not explicit:
5819                    explicit = self.skip_word('explicit')
5820                    if explicit:
5821                        continue
5822
5823            if not constexpr and outer in ('member', 'function'):
5824                constexpr = self.skip_word("constexpr")
5825                if constexpr:
5826                    continue
5827            if not volatile and typed:
5828                volatile = self.skip_word('volatile')
5829                if volatile:
5830                    continue
5831            if not const and typed:
5832                const = self.skip_word('const')
5833                if const:
5834                    continue
5835            attr = self._parse_attribute()
5836            if attr:
5837                attrs.append(attr)
5838                continue
5839            break
5840        return ASTDeclSpecsSimple(storage, threadLocal, inline, virtual,
5841                                  explicit, constexpr, volatile, const,
5842                                  friend, attrs)
5843
5844    def _parse_decl_specs(self, outer: str, typed: bool = True) -> ASTDeclSpecs:
5845        if outer:
5846            if outer not in ('type', 'member', 'function', 'templateParam'):
5847                raise Exception('Internal error, unknown outer "%s".' % outer)
5848        """
5849        storage-class-specifier function-specifier "constexpr"
5850        "volatile" "const" trailing-type-specifier
5851
5852        storage-class-specifier ->
5853              "static" (only for member_object and function_object)
5854            | "register"
5855
5856        function-specifier -> "inline" | "virtual" | "explicit" (only for
5857        function_object)
5858
5859        "constexpr" (only for member_object and function_object)
5860        """
5861        leftSpecs = self._parse_decl_specs_simple(outer, typed)
5862        rightSpecs = None
5863
5864        if typed:
5865            trailing = self._parse_trailing_type_spec()
5866            rightSpecs = self._parse_decl_specs_simple(outer, typed)
5867        else:
5868            trailing = None
5869        return ASTDeclSpecs(outer, leftSpecs, rightSpecs, trailing)
5870
5871    def _parse_declarator_name_suffix(
5872        self, named: Union[bool, str], paramMode: str, typed: bool
5873    ) -> Union[ASTDeclaratorNameParamQual, ASTDeclaratorNameBitField]:
5874        # now we should parse the name, and then suffixes
5875        if named == 'maybe':
5876            pos = self.pos
5877            try:
5878                declId = self._parse_nested_name()
5879            except DefinitionError:
5880                self.pos = pos
5881                declId = None
5882        elif named == 'single':
5883            if self.match(identifier_re):
5884                identifier = ASTIdentifier(self.matched_text)
5885                nne = ASTNestedNameElement(identifier, None)
5886                declId = ASTNestedName([nne], [False], rooted=False)
5887                # if it's a member pointer, we may have '::', which should be an error
5888                self.skip_ws()
5889                if self.current_char == ':':
5890                    self.fail("Unexpected ':' after identifier.")
5891            else:
5892                declId = None
5893        elif named:
5894            declId = self._parse_nested_name()
5895        else:
5896            declId = None
5897        arrayOps = []
5898        while 1:
5899            self.skip_ws()
5900            if typed and self.skip_string('['):
5901                self.skip_ws()
5902                if self.skip_string(']'):
5903                    arrayOps.append(ASTArray(None))
5904                    continue
5905
5906                def parser() -> ASTExpression:
5907                    return self._parse_expression()
5908                value = self._parse_expression_fallback([']'], parser)
5909                if not self.skip_string(']'):
5910                    self.fail("Expected ']' in end of array operator.")
5911                arrayOps.append(ASTArray(value))
5912                continue
5913            else:
5914                break
5915        paramQual = self._parse_parameters_and_qualifiers(paramMode)
5916        if paramQual is None and len(arrayOps) == 0:
5917            # perhaps a bit-field
5918            if named and paramMode == 'type' and typed:
5919                self.skip_ws()
5920                if self.skip_string(':'):
5921                    size = self._parse_constant_expression(inTemplate=False)
5922                    return ASTDeclaratorNameBitField(declId=declId, size=size)
5923        return ASTDeclaratorNameParamQual(declId=declId, arrayOps=arrayOps,
5924                                          paramQual=paramQual)
5925
5926    def _parse_declarator(self, named: Union[bool, str], paramMode: str,
5927                          typed: bool = True
5928                          ) -> ASTDeclarator:
5929        # 'typed' here means 'parse return type stuff'
5930        if paramMode not in ('type', 'function', 'operatorCast', 'new'):
5931            raise Exception(
5932                "Internal error, unknown paramMode '%s'." % paramMode)
5933        prevErrors = []
5934        self.skip_ws()
5935        if typed and self.skip_string('*'):
5936            self.skip_ws()
5937            volatile = False
5938            const = False
5939            attrs = []
5940            while 1:
5941                if not volatile:
5942                    volatile = self.skip_word_and_ws('volatile')
5943                    if volatile:
5944                        continue
5945                if not const:
5946                    const = self.skip_word_and_ws('const')
5947                    if const:
5948                        continue
5949                attr = self._parse_attribute()
5950                if attr is not None:
5951                    attrs.append(attr)
5952                    continue
5953                break
5954            next = self._parse_declarator(named, paramMode, typed)
5955            return ASTDeclaratorPtr(next=next, volatile=volatile, const=const, attrs=attrs)
5956        # TODO: shouldn't we parse an R-value ref here first?
5957        if typed and self.skip_string("&"):
5958            attrs = []
5959            while 1:
5960                attr = self._parse_attribute()
5961                if attr is None:
5962                    break
5963                attrs.append(attr)
5964            next = self._parse_declarator(named, paramMode, typed)
5965            return ASTDeclaratorRef(next=next, attrs=attrs)
5966        if typed and self.skip_string("..."):
5967            next = self._parse_declarator(named, paramMode, False)
5968            return ASTDeclaratorParamPack(next=next)
5969        if typed and self.current_char == '(':  # note: peeking, not skipping
5970            if paramMode == "operatorCast":
5971                # TODO: we should be able to parse cast operators which return
5972                # function pointers. For now, just hax it and ignore.
5973                return ASTDeclaratorNameParamQual(declId=None, arrayOps=[],
5974                                                  paramQual=None)
5975            # maybe this is the beginning of params and quals,try that first,
5976            # otherwise assume it's noptr->declarator > ( ptr-declarator )
5977            pos = self.pos
5978            try:
5979                # assume this is params and quals
5980                res = self._parse_declarator_name_suffix(named, paramMode,
5981                                                         typed)
5982                return res
5983            except DefinitionError as exParamQual:
5984                prevErrors.append((exParamQual,
5985                                   "If declarator-id with parameters-and-qualifiers"))
5986                self.pos = pos
5987                try:
5988                    assert self.current_char == '('
5989                    self.skip_string('(')
5990                    # TODO: hmm, if there is a name, it must be in inner, right?
5991                    # TODO: hmm, if there must be parameters, they must be
5992                    #       inside, right?
5993                    inner = self._parse_declarator(named, paramMode, typed)
5994                    if not self.skip_string(')'):
5995                        self.fail("Expected ')' in \"( ptr-declarator )\"")
5996                    next = self._parse_declarator(named=False,
5997                                                  paramMode="type",
5998                                                  typed=typed)
5999                    return ASTDeclaratorParen(inner=inner, next=next)
6000                except DefinitionError as exNoPtrParen:
6001                    self.pos = pos
6002                    prevErrors.append((exNoPtrParen, "If parenthesis in noptr-declarator"))
6003                    header = "Error in declarator"
6004                    raise self._make_multi_error(prevErrors, header) from exNoPtrParen
6005        if typed:  # pointer to member
6006            pos = self.pos
6007            try:
6008                name = self._parse_nested_name(memberPointer=True)
6009                self.skip_ws()
6010                if not self.skip_string('*'):
6011                    self.fail("Expected '*' in pointer to member declarator.")
6012                self.skip_ws()
6013            except DefinitionError as e:
6014                self.pos = pos
6015                prevErrors.append((e, "If pointer to member declarator"))
6016            else:
6017                volatile = False
6018                const = False
6019                while 1:
6020                    if not volatile:
6021                        volatile = self.skip_word_and_ws('volatile')
6022                        if volatile:
6023                            continue
6024                    if not const:
6025                        const = self.skip_word_and_ws('const')
6026                        if const:
6027                            continue
6028                    break
6029                next = self._parse_declarator(named, paramMode, typed)
6030                return ASTDeclaratorMemPtr(name, const, volatile, next=next)
6031        pos = self.pos
6032        try:
6033            res = self._parse_declarator_name_suffix(named, paramMode, typed)
6034            # this is a heuristic for error messages, for when there is a < after a
6035            # nested name, but it was not a successful template argument list
6036            if self.current_char == '<':
6037                self.otherErrors.append(self._make_multi_error(prevErrors, ""))
6038            return res
6039        except DefinitionError as e:
6040            self.pos = pos
6041            prevErrors.append((e, "If declarator-id"))
6042            header = "Error in declarator or parameters-and-qualifiers"
6043            raise self._make_multi_error(prevErrors, header) from e
6044
6045    def _parse_initializer(self, outer: str = None, allowFallback: bool = True
6046                           ) -> ASTInitializer:
6047        # initializer                           # global vars
6048        # -> brace-or-equal-initializer
6049        #  | '(' expression-list ')'
6050        #
6051        # brace-or-equal-initializer            # member vars
6052        # -> '=' initializer-clause
6053        #  | braced-init-list
6054        #
6055        # initializer-clause  # function params, non-type template params (with '=' in front)
6056        # -> assignment-expression
6057        #  | braced-init-list
6058        #
6059        # we don't distinguish between global and member vars, so disallow paren:
6060        #
6061        # -> braced-init-list             # var only
6062        #  | '=' assignment-expression
6063        #  | '=' braced-init-list
6064        self.skip_ws()
6065        if outer == 'member':
6066            bracedInit = self._parse_braced_init_list()
6067            if bracedInit is not None:
6068                return ASTInitializer(bracedInit, hasAssign=False)
6069
6070        if not self.skip_string('='):
6071            return None
6072
6073        bracedInit = self._parse_braced_init_list()
6074        if bracedInit is not None:
6075            return ASTInitializer(bracedInit)
6076
6077        if outer == 'member':
6078            fallbackEnd = []  # type: List[str]
6079        elif outer == 'templateParam':
6080            fallbackEnd = [',', '>']
6081        elif outer is None:  # function parameter
6082            fallbackEnd = [',', ')']
6083        else:
6084            self.fail("Internal error, initializer for outer '%s' not "
6085                      "implemented." % outer)
6086
6087        inTemplate = outer == 'templateParam'
6088
6089        def parser() -> ASTExpression:
6090            return self._parse_assignment_expression(inTemplate=inTemplate)
6091        value = self._parse_expression_fallback(fallbackEnd, parser, allow=allowFallback)
6092        return ASTInitializer(value)
6093
6094    def _parse_type(self, named: Union[bool, str], outer: str = None) -> ASTType:
6095        """
6096        named=False|'maybe'|True: 'maybe' is e.g., for function objects which
6097        doesn't need to name the arguments
6098
6099        outer == operatorCast: annoying case, we should not take the params
6100        """
6101        if outer:  # always named
6102            if outer not in ('type', 'member', 'function',
6103                             'operatorCast', 'templateParam'):
6104                raise Exception('Internal error, unknown outer "%s".' % outer)
6105            if outer != 'operatorCast':
6106                assert named
6107        if outer in ('type', 'function'):
6108            # We allow type objects to just be a name.
6109            # Some functions don't have normal return types: constructors,
6110            # destrutors, cast operators
6111            prevErrors = []
6112            startPos = self.pos
6113            # first try without the type
6114            try:
6115                declSpecs = self._parse_decl_specs(outer=outer, typed=False)
6116                decl = self._parse_declarator(named=True, paramMode=outer,
6117                                              typed=False)
6118                self.assert_end(allowSemicolon=True)
6119            except DefinitionError as exUntyped:
6120                if outer == 'type':
6121                    desc = "If just a name"
6122                elif outer == 'function':
6123                    desc = "If the function has no return type"
6124                else:
6125                    assert False
6126                prevErrors.append((exUntyped, desc))
6127                self.pos = startPos
6128                try:
6129                    declSpecs = self._parse_decl_specs(outer=outer)
6130                    decl = self._parse_declarator(named=True, paramMode=outer)
6131                except DefinitionError as exTyped:
6132                    self.pos = startPos
6133                    if outer == 'type':
6134                        desc = "If typedef-like declaration"
6135                    elif outer == 'function':
6136                        desc = "If the function has a return type"
6137                    else:
6138                        assert False
6139                    prevErrors.append((exTyped, desc))
6140                    # Retain the else branch for easier debugging.
6141                    # TODO: it would be nice to save the previous stacktrace
6142                    #       and output it here.
6143                    if True:
6144                        if outer == 'type':
6145                            header = "Type must be either just a name or a "
6146                            header += "typedef-like declaration."
6147                        elif outer == 'function':
6148                            header = "Error when parsing function declaration."
6149                        else:
6150                            assert False
6151                        raise self._make_multi_error(prevErrors, header) from exTyped
6152                    else:
6153                        # For testing purposes.
6154                        # do it again to get the proper traceback (how do you
6155                        # reliably save a traceback when an exception is
6156                        # constructed?)
6157                        self.pos = startPos
6158                        typed = True
6159                        declSpecs = self._parse_decl_specs(outer=outer, typed=typed)
6160                        decl = self._parse_declarator(named=True, paramMode=outer,
6161                                                      typed=typed)
6162        else:
6163            paramMode = 'type'
6164            if outer == 'member':  # i.e., member
6165                named = True
6166            elif outer == 'operatorCast':
6167                paramMode = 'operatorCast'
6168                outer = None
6169            elif outer == 'templateParam':
6170                named = 'single'
6171            declSpecs = self._parse_decl_specs(outer=outer)
6172            decl = self._parse_declarator(named=named, paramMode=paramMode)
6173        return ASTType(declSpecs, decl)
6174
6175    def _parse_type_with_init(
6176            self, named: Union[bool, str],
6177            outer: str) -> Union[ASTTypeWithInit, ASTTemplateParamConstrainedTypeWithInit]:
6178        if outer:
6179            assert outer in ('type', 'member', 'function', 'templateParam')
6180        type = self._parse_type(outer=outer, named=named)
6181        if outer != 'templateParam':
6182            init = self._parse_initializer(outer=outer)
6183            return ASTTypeWithInit(type, init)
6184        # it could also be a constrained type parameter, e.g., C T = int&
6185        pos = self.pos
6186        eExpr = None
6187        try:
6188            init = self._parse_initializer(outer=outer, allowFallback=False)
6189            # note: init may be None if there is no =
6190            if init is None:
6191                return ASTTypeWithInit(type, None)
6192            # we parsed an expression, so we must have a , or a >,
6193            # otherwise the expression didn't get everything
6194            self.skip_ws()
6195            if self.current_char != ',' and self.current_char != '>':
6196                # pretend it didn't happen
6197                self.pos = pos
6198                init = None
6199            else:
6200                # we assume that it was indeed an expression
6201                return ASTTypeWithInit(type, init)
6202        except DefinitionError as e:
6203            self.pos = pos
6204            eExpr = e
6205        if not self.skip_string("="):
6206            return ASTTypeWithInit(type, None)
6207        try:
6208            typeInit = self._parse_type(named=False, outer=None)
6209            return ASTTemplateParamConstrainedTypeWithInit(type, typeInit)
6210        except DefinitionError as eType:
6211            if eExpr is None:
6212                raise eType
6213            errs = []
6214            errs.append((eExpr, "If default template argument is an expression"))
6215            errs.append((eType, "If default template argument is a type"))
6216            msg = "Error in non-type template parameter"
6217            msg += " or constrained template parameter."
6218            raise self._make_multi_error(errs, msg) from eType
6219
6220    def _parse_type_using(self) -> ASTTypeUsing:
6221        name = self._parse_nested_name()
6222        self.skip_ws()
6223        if not self.skip_string('='):
6224            return ASTTypeUsing(name, None)
6225        type = self._parse_type(False, None)
6226        return ASTTypeUsing(name, type)
6227
6228    def _parse_concept(self) -> ASTConcept:
6229        nestedName = self._parse_nested_name()
6230        self.skip_ws()
6231        initializer = self._parse_initializer('member')
6232        return ASTConcept(nestedName, initializer)
6233
6234    def _parse_class(self) -> ASTClass:
6235        name = self._parse_nested_name()
6236        self.skip_ws()
6237        final = self.skip_word_and_ws('final')
6238        bases = []
6239        self.skip_ws()
6240        if self.skip_string(':'):
6241            while 1:
6242                self.skip_ws()
6243                visibility = None
6244                virtual = False
6245                pack = False
6246                if self.skip_word_and_ws('virtual'):
6247                    virtual = True
6248                if self.match(_visibility_re):
6249                    visibility = self.matched_text
6250                    self.skip_ws()
6251                if not virtual and self.skip_word_and_ws('virtual'):
6252                    virtual = True
6253                baseName = self._parse_nested_name()
6254                self.skip_ws()
6255                pack = self.skip_string('...')
6256                bases.append(ASTBaseClass(baseName, visibility, virtual, pack))
6257                self.skip_ws()
6258                if self.skip_string(','):
6259                    continue
6260                else:
6261                    break
6262        return ASTClass(name, final, bases)
6263
6264    def _parse_union(self) -> ASTUnion:
6265        name = self._parse_nested_name()
6266        return ASTUnion(name)
6267
6268    def _parse_enum(self) -> ASTEnum:
6269        scoped = None  # is set by CPPEnumObject
6270        self.skip_ws()
6271        name = self._parse_nested_name()
6272        self.skip_ws()
6273        underlyingType = None
6274        if self.skip_string(':'):
6275            underlyingType = self._parse_type(named=False)
6276        return ASTEnum(name, scoped, underlyingType)
6277
6278    def _parse_enumerator(self) -> ASTEnumerator:
6279        name = self._parse_nested_name()
6280        self.skip_ws()
6281        init = None
6282        if self.skip_string('='):
6283            self.skip_ws()
6284
6285            def parser() -> ASTExpression:
6286                return self._parse_constant_expression(inTemplate=False)
6287            initVal = self._parse_expression_fallback([], parser)
6288            init = ASTInitializer(initVal)
6289        return ASTEnumerator(name, init)
6290
6291    # ==========================================================================
6292
6293    def _parse_template_paramter(self) -> ASTTemplateParam:
6294        self.skip_ws()
6295        if self.skip_word('template'):
6296            # declare a tenplate template parameter
6297            nestedParams = self._parse_template_parameter_list()
6298        else:
6299            nestedParams = None
6300
6301        pos = self.pos
6302        try:
6303            # Unconstrained type parameter or template type parameter
6304            key = None
6305            self.skip_ws()
6306            if self.skip_word_and_ws('typename'):
6307                key = 'typename'
6308            elif self.skip_word_and_ws('class'):
6309                key = 'class'
6310            elif nestedParams:
6311                self.fail("Expected 'typename' or 'class' after "
6312                          "template template parameter list.")
6313            else:
6314                self.fail("Expected 'typename' or 'class' in tbe "
6315                          "beginning of template type parameter.")
6316            self.skip_ws()
6317            parameterPack = self.skip_string('...')
6318            self.skip_ws()
6319            if self.match(identifier_re):
6320                identifier = ASTIdentifier(self.matched_text)
6321            else:
6322                identifier = None
6323            self.skip_ws()
6324            if not parameterPack and self.skip_string('='):
6325                default = self._parse_type(named=False, outer=None)
6326            else:
6327                default = None
6328                if self.current_char not in ',>':
6329                    self.fail('Expected "," or ">" after (template) type parameter.')
6330            data = ASTTemplateKeyParamPackIdDefault(key, identifier,
6331                                                    parameterPack, default)
6332            if nestedParams:
6333                return ASTTemplateParamTemplateType(nestedParams, data)
6334            else:
6335                return ASTTemplateParamType(data)
6336        except DefinitionError as eType:
6337            if nestedParams:
6338                raise
6339            try:
6340                # non-type parameter or constrained type parameter
6341                self.pos = pos
6342                param = self._parse_type_with_init('maybe', 'templateParam')
6343                return ASTTemplateParamNonType(param)
6344            except DefinitionError as eNonType:
6345                self.pos = pos
6346                header = "Error when parsing template parameter."
6347                errs = []
6348                errs.append(
6349                    (eType, "If unconstrained type parameter or template type parameter"))
6350                errs.append(
6351                    (eNonType, "If constrained type parameter or non-type parameter"))
6352                raise self._make_multi_error(errs, header)
6353
6354    def _parse_template_parameter_list(self) -> ASTTemplateParams:
6355        # only: '<' parameter-list '>'
6356        # we assume that 'template' has just been parsed
6357        templateParams = []  # type: List[ASTTemplateParam]
6358        self.skip_ws()
6359        if not self.skip_string("<"):
6360            self.fail("Expected '<' after 'template'")
6361        while 1:
6362            pos = self.pos
6363            err = None
6364            try:
6365                param = self._parse_template_paramter()
6366                templateParams.append(param)
6367            except DefinitionError as eParam:
6368                self.pos = pos
6369                err = eParam
6370            self.skip_ws()
6371            if self.skip_string('>'):
6372                return ASTTemplateParams(templateParams)
6373            elif self.skip_string(','):
6374                continue
6375            else:
6376                header = "Error in template parameter list."
6377                errs = []
6378                if err:
6379                    errs.append((err, "If parameter"))
6380                try:
6381                    self.fail('Expected "," or ">".')
6382                except DefinitionError as e:
6383                    errs.append((e, "If no parameter"))
6384                print(errs)
6385                raise self._make_multi_error(errs, header)
6386
6387    def _parse_template_introduction(self) -> ASTTemplateIntroduction:
6388        pos = self.pos
6389        try:
6390            concept = self._parse_nested_name()
6391        except Exception:
6392            self.pos = pos
6393            return None
6394        self.skip_ws()
6395        if not self.skip_string('{'):
6396            self.pos = pos
6397            return None
6398
6399        # for sure it must be a template introduction now
6400        params = []
6401        while 1:
6402            self.skip_ws()
6403            parameterPack = self.skip_string('...')
6404            self.skip_ws()
6405            if not self.match(identifier_re):
6406                self.fail("Expected identifier in template introduction list.")
6407            txt_identifier = self.matched_text
6408            # make sure there isn't a keyword
6409            if txt_identifier in _keywords:
6410                self.fail("Expected identifier in template introduction list, "
6411                          "got keyword: %s" % txt_identifier)
6412            identifier = ASTIdentifier(txt_identifier)
6413            params.append(ASTTemplateIntroductionParameter(identifier, parameterPack))
6414
6415            self.skip_ws()
6416            if self.skip_string('}'):
6417                break
6418            elif self.skip_string(','):
6419                continue
6420            else:
6421                self.fail("Error in template introduction list. "
6422                          'Expected ",", or "}".')
6423        return ASTTemplateIntroduction(concept, params)
6424
6425    def _parse_requires_clause(self) -> Optional[ASTRequiresClause]:
6426        # requires-clause -> 'requires' constraint-logical-or-expression
6427        # constraint-logical-or-expression
6428        #   -> constraint-logical-and-expression
6429        #    | constraint-logical-or-expression '||' constraint-logical-and-expression
6430        # constraint-logical-and-expression
6431        #   -> primary-expression
6432        #    | constraint-logical-and-expression '&&' primary-expression
6433        self.skip_ws()
6434        if not self.skip_word('requires'):
6435            return None
6436
6437        def parse_and_expr(self: DefinitionParser) -> ASTExpression:
6438            andExprs = []
6439            ops = []
6440            andExprs.append(self._parse_primary_expression())
6441            while True:
6442                self.skip_ws()
6443                oneMore = False
6444                if self.skip_string('&&'):
6445                    oneMore = True
6446                    ops.append('&&')
6447                elif self.skip_word('and'):
6448                    oneMore = True
6449                    ops.append('and')
6450                if not oneMore:
6451                    break
6452                andExprs.append(self._parse_primary_expression())
6453            if len(andExprs) == 1:
6454                return andExprs[0]
6455            else:
6456                return ASTBinOpExpr(andExprs, ops)
6457
6458        orExprs = []
6459        ops = []
6460        orExprs.append(parse_and_expr(self))
6461        while True:
6462            self.skip_ws()
6463            oneMore = False
6464            if self.skip_string('||'):
6465                oneMore = True
6466                ops.append('||')
6467            elif self.skip_word('or'):
6468                oneMore = True
6469                ops.append('or')
6470            if not oneMore:
6471                break
6472            orExprs.append(parse_and_expr(self))
6473        if len(orExprs) == 1:
6474            return ASTRequiresClause(orExprs[0])
6475        else:
6476            return ASTRequiresClause(ASTBinOpExpr(orExprs, ops))
6477
6478    def _parse_template_declaration_prefix(self, objectType: str
6479                                           ) -> Optional[ASTTemplateDeclarationPrefix]:
6480        templates = []  # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]]
6481        while 1:
6482            self.skip_ws()
6483            # the saved position is only used to provide a better error message
6484            params = None  # type: Union[ASTTemplateParams, ASTTemplateIntroduction]
6485            pos = self.pos
6486            if self.skip_word("template"):
6487                try:
6488                    params = self._parse_template_parameter_list()
6489                except DefinitionError as e:
6490                    if objectType == 'member' and len(templates) == 0:
6491                        return ASTTemplateDeclarationPrefix(None)
6492                    else:
6493                        raise e
6494            else:
6495                params = self._parse_template_introduction()
6496                if not params:
6497                    break
6498            if objectType == 'concept' and len(templates) > 0:
6499                self.pos = pos
6500                self.fail("More than 1 template parameter list for concept.")
6501            templates.append(params)
6502        if len(templates) == 0 and objectType == 'concept':
6503            self.fail('Missing template parameter list for concept.')
6504        if len(templates) == 0:
6505            return None
6506        else:
6507            return ASTTemplateDeclarationPrefix(templates)
6508
6509    def _check_template_consistency(self, nestedName: ASTNestedName,
6510                                    templatePrefix: ASTTemplateDeclarationPrefix,
6511                                    fullSpecShorthand: bool, isMember: bool = False
6512                                    ) -> ASTTemplateDeclarationPrefix:
6513        numArgs = nestedName.num_templates()
6514        isMemberInstantiation = False
6515        if not templatePrefix:
6516            numParams = 0
6517        else:
6518            if isMember and templatePrefix.templates is None:
6519                numParams = 0
6520                isMemberInstantiation = True
6521            else:
6522                numParams = len(templatePrefix.templates)
6523        if numArgs + 1 < numParams:
6524            self.fail("Too few template argument lists comapred to parameter"
6525                      " lists. Argument lists: %d, Parameter lists: %d."
6526                      % (numArgs, numParams))
6527        if numArgs > numParams:
6528            numExtra = numArgs - numParams
6529            if not fullSpecShorthand and not isMemberInstantiation:
6530                msg = "Too many template argument lists compared to parameter" \
6531                    " lists. Argument lists: %d, Parameter lists: %d," \
6532                    " Extra empty parameters lists prepended: %d." \
6533                    % (numArgs, numParams, numExtra)
6534                msg += " Declaration:\n\t"
6535                if templatePrefix:
6536                    msg += "%s\n\t" % templatePrefix
6537                msg += str(nestedName)
6538                self.warn(msg)
6539
6540            newTemplates = []  # type: List[Union[ASTTemplateParams, ASTTemplateIntroduction]]
6541            for i in range(numExtra):
6542                newTemplates.append(ASTTemplateParams([]))
6543            if templatePrefix and not isMemberInstantiation:
6544                newTemplates.extend(templatePrefix.templates)
6545            templatePrefix = ASTTemplateDeclarationPrefix(newTemplates)
6546        return templatePrefix
6547
6548    def parse_declaration(self, objectType: str, directiveType: str) -> ASTDeclaration:
6549        if objectType not in ('class', 'union', 'function', 'member', 'type',
6550                              'concept', 'enum', 'enumerator'):
6551            raise Exception('Internal error, unknown objectType "%s".' % objectType)
6552        if directiveType not in ('class', 'struct', 'union', 'function', 'member', 'var',
6553                                 'type', 'concept',
6554                                 'enum', 'enum-struct', 'enum-class', 'enumerator'):
6555            raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
6556        visibility = None
6557        templatePrefix = None
6558        requiresClause = None
6559        trailingRequiresClause = None
6560        declaration = None  # type: Any
6561
6562        self.skip_ws()
6563        if self.match(_visibility_re):
6564            visibility = self.matched_text
6565
6566        if objectType in ('type', 'concept', 'member', 'function', 'class'):
6567            templatePrefix = self._parse_template_declaration_prefix(objectType)
6568            if objectType == 'function' and templatePrefix is not None:
6569                requiresClause = self._parse_requires_clause()
6570
6571        if objectType == 'type':
6572            prevErrors = []
6573            pos = self.pos
6574            try:
6575                if not templatePrefix:
6576                    declaration = self._parse_type(named=True, outer='type')
6577            except DefinitionError as e:
6578                prevErrors.append((e, "If typedef-like declaration"))
6579                self.pos = pos
6580            pos = self.pos
6581            try:
6582                if not declaration:
6583                    declaration = self._parse_type_using()
6584            except DefinitionError as e:
6585                self.pos = pos
6586                prevErrors.append((e, "If type alias or template alias"))
6587                header = "Error in type declaration."
6588                raise self._make_multi_error(prevErrors, header) from e
6589        elif objectType == 'concept':
6590            declaration = self._parse_concept()
6591        elif objectType == 'member':
6592            declaration = self._parse_type_with_init(named=True, outer='member')
6593        elif objectType == 'function':
6594            declaration = self._parse_type(named=True, outer='function')
6595            if templatePrefix is not None:
6596                trailingRequiresClause = self._parse_requires_clause()
6597        elif objectType == 'class':
6598            declaration = self._parse_class()
6599        elif objectType == 'union':
6600            declaration = self._parse_union()
6601        elif objectType == 'enum':
6602            declaration = self._parse_enum()
6603        elif objectType == 'enumerator':
6604            declaration = self._parse_enumerator()
6605        else:
6606            assert False
6607        templatePrefix = self._check_template_consistency(declaration.name,
6608                                                          templatePrefix,
6609                                                          fullSpecShorthand=False,
6610                                                          isMember=objectType == 'member')
6611        self.skip_ws()
6612        semicolon = self.skip_string(';')
6613        return ASTDeclaration(objectType, directiveType, visibility,
6614                              templatePrefix, requiresClause, declaration,
6615                              trailingRequiresClause, semicolon)
6616
6617    def parse_namespace_object(self) -> ASTNamespace:
6618        templatePrefix = self._parse_template_declaration_prefix(objectType="namespace")
6619        name = self._parse_nested_name()
6620        templatePrefix = self._check_template_consistency(name, templatePrefix,
6621                                                          fullSpecShorthand=False)
6622        res = ASTNamespace(name, templatePrefix)
6623        res.objectType = 'namespace'  # type: ignore
6624        return res
6625
6626    def parse_xref_object(self) -> Tuple[Union[ASTNamespace, ASTDeclaration], bool]:
6627        pos = self.pos
6628        try:
6629            templatePrefix = self._parse_template_declaration_prefix(objectType="xref")
6630            name = self._parse_nested_name()
6631            # if there are '()' left, just skip them
6632            self.skip_ws()
6633            self.skip_string('()')
6634            self.assert_end()
6635            templatePrefix = self._check_template_consistency(name, templatePrefix,
6636                                                              fullSpecShorthand=True)
6637            res1 = ASTNamespace(name, templatePrefix)
6638            res1.objectType = 'xref'  # type: ignore
6639            return res1, True
6640        except DefinitionError as e1:
6641            try:
6642                self.pos = pos
6643                res2 = self.parse_declaration('function', 'function')
6644                # if there are '()' left, just skip them
6645                self.skip_ws()
6646                self.skip_string('()')
6647                self.assert_end()
6648                return res2, False
6649            except DefinitionError as e2:
6650                errs = []
6651                errs.append((e1, "If shorthand ref"))
6652                errs.append((e2, "If full function ref"))
6653                msg = "Error in cross-reference."
6654                raise self._make_multi_error(errs, msg) from e2
6655
6656    def parse_expression(self) -> Union[ASTExpression, ASTType]:
6657        pos = self.pos
6658        try:
6659            expr = self._parse_expression()
6660            self.skip_ws()
6661            self.assert_end()
6662            return expr
6663        except DefinitionError as exExpr:
6664            self.pos = pos
6665            try:
6666                typ = self._parse_type(False)
6667                self.skip_ws()
6668                self.assert_end()
6669                return typ
6670            except DefinitionError as exType:
6671                header = "Error when parsing (type) expression."
6672                errs = []
6673                errs.append((exExpr, "If expression"))
6674                errs.append((exType, "If type"))
6675                raise self._make_multi_error(errs, header) from exType
6676
6677
6678def _make_phony_error_name() -> ASTNestedName:
6679    nne = ASTNestedNameElement(ASTIdentifier("PhonyNameDueToError"), None)
6680    return ASTNestedName([nne], [False], rooted=False)
6681
6682
6683class CPPObject(ObjectDescription[ASTDeclaration]):
6684    """Description of a C++ language object."""
6685
6686    doc_field_types = [
6687        GroupedField('parameter', label=_('Parameters'),
6688                     names=('param', 'parameter', 'arg', 'argument'),
6689                     can_collapse=True),
6690        GroupedField('template parameter', label=_('Template Parameters'),
6691                     names=('tparam', 'template parameter'),
6692                     can_collapse=True),
6693        GroupedField('exceptions', label=_('Throws'), rolename='cpp:class',
6694                     names=('throws', 'throw', 'exception'),
6695                     can_collapse=True),
6696        Field('returnvalue', label=_('Returns'), has_arg=False,
6697              names=('returns', 'return')),
6698    ]
6699
6700    option_spec = {
6701        'noindexentry': directives.flag,
6702        'tparam-line-spec': directives.flag,
6703    }
6704
6705    def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None:
6706        assert ast.objectType == 'enumerator'
6707        # find the parent, if it exists && is an enum
6708        #                     && it's unscoped,
6709        #                  then add the name to the parent scope
6710        symbol = ast.symbol
6711        assert symbol
6712        assert symbol.identOrOp is not None
6713        assert symbol.templateParams is None
6714        assert symbol.templateArgs is None
6715        parentSymbol = symbol.parent
6716        assert parentSymbol
6717        if parentSymbol.parent is None:
6718            # TODO: we could warn, but it is somewhat equivalent to unscoped
6719            # enums, without the enum
6720            return  # no parent
6721        parentDecl = parentSymbol.declaration
6722        if parentDecl is None:
6723            # the parent is not explicitly declared
6724            # TODO: we could warn, but it could be a style to just assume
6725            # enumerator parents to be scoped
6726            return
6727        if parentDecl.objectType != 'enum':
6728            # TODO: maybe issue a warning, enumerators in non-enums is weird,
6729            # but it is somewhat equivalent to unscoped enums, without the enum
6730            return
6731        if parentDecl.directiveType != 'enum':
6732            return
6733
6734        targetSymbol = parentSymbol.parent
6735        s = targetSymbol.find_identifier(symbol.identOrOp, matchSelf=False, recurseInAnon=True,
6736                                         searchInSiblings=False)
6737        if s is not None:
6738            # something is already declared with that name
6739            return
6740        declClone = symbol.declaration.clone()
6741        declClone.enumeratorScopedSymbol = symbol
6742        Symbol(parent=targetSymbol, identOrOp=symbol.identOrOp,
6743               templateParams=None, templateArgs=None,
6744               declaration=declClone,
6745               docname=self.env.docname, line=self.get_source_info()[1])
6746
6747    def add_target_and_index(self, ast: ASTDeclaration, sig: str,
6748                             signode: TextElement) -> None:
6749        # general note: name must be lstrip(':')'ed, to remove "::"
6750        ids = []
6751        for i in range(1, _max_id + 1):
6752            try:
6753                id = ast.get_id(version=i)
6754                ids.append(id)
6755            except NoOldIdError:
6756                assert i < _max_id
6757        # let's keep the newest first
6758        ids = list(reversed(ids))
6759        newestId = ids[0]
6760        assert newestId  # shouldn't be None
6761        if not re.compile(r'^[a-zA-Z0-9_]*$').match(newestId):
6762            logger.warning('Index id generation for C++ object "%s" failed, please '
6763                           'report as bug (id=%s).', ast, newestId,
6764                           location=self.get_source_info())
6765
6766        name = ast.symbol.get_full_nested_name().get_display_string().lstrip(':')
6767        # Add index entry, but not if it's a declaration inside a concept
6768        isInConcept = False
6769        s = ast.symbol.parent
6770        while s is not None:
6771            decl = s.declaration
6772            s = s.parent
6773            if decl is None:
6774                continue
6775            if decl.objectType == 'concept':
6776                isInConcept = True
6777                break
6778        if not isInConcept and 'noindexentry' not in self.options:
6779            strippedName = name
6780            for prefix in self.env.config.cpp_index_common_prefix:
6781                if name.startswith(prefix):
6782                    strippedName = strippedName[len(prefix):]
6783                    break
6784            indexText = self.get_index_text(strippedName)
6785            self.indexnode['entries'].append(('single', indexText, newestId, '', None))
6786
6787        if newestId not in self.state.document.ids:
6788            # if the name is not unique, the first one will win
6789            names = self.env.domaindata['cpp']['names']
6790            if name not in names:
6791                names[name] = ast.symbol.docname
6792            # always add the newest id
6793            assert newestId
6794            signode['ids'].append(newestId)
6795            # only add compatibility ids when there are no conflicts
6796            for id in ids[1:]:
6797                if not id:  # is None when the element didn't exist in that version
6798                    continue
6799                if id not in self.state.document.ids:
6800                    signode['ids'].append(id)
6801            self.state.document.note_explicit_target(signode)
6802
6803    @property
6804    def object_type(self) -> str:
6805        raise NotImplementedError()
6806
6807    @property
6808    def display_object_type(self) -> str:
6809        return self.object_type
6810
6811    def get_index_text(self, name: str) -> str:
6812        return _('%s (C++ %s)') % (name, self.display_object_type)
6813
6814    def parse_definition(self, parser: DefinitionParser) -> ASTDeclaration:
6815        return parser.parse_declaration(self.object_type, self.objtype)
6816
6817    def describe_signature(self, signode: desc_signature,
6818                           ast: ASTDeclaration, options: Dict) -> None:
6819        ast.describe_signature(signode, 'lastIsName', self.env, options)
6820
6821    def run(self) -> List[Node]:
6822        env = self.state.document.settings.env  # from ObjectDescription.run
6823        if 'cpp:parent_symbol' not in env.temp_data:
6824            root = env.domaindata['cpp']['root_symbol']
6825            env.temp_data['cpp:parent_symbol'] = root
6826            env.ref_context['cpp:parent_key'] = root.get_lookup_key()
6827
6828        # The lookup keys assume that no nested scopes exists inside overloaded functions.
6829        # (see also #5191)
6830        # Example:
6831        # .. cpp:function:: void f(int)
6832        # .. cpp:function:: void f(double)
6833        #
6834        #    .. cpp:function:: void g()
6835        #
6836        #       :cpp:any:`boom`
6837        #
6838        # So we disallow any signatures inside functions.
6839        parentSymbol = env.temp_data['cpp:parent_symbol']
6840        parentDecl = parentSymbol.declaration
6841        if parentDecl is not None and parentDecl.objectType == 'function':
6842            msg = "C++ declarations inside functions are not supported." \
6843                  " Parent function: {}\nDirective name: {}\nDirective arg: {}"
6844            logger.warning(msg.format(
6845                str(parentSymbol.get_full_nested_name()),
6846                self.name, self.arguments[0]
6847            ), location=self.get_source_info())
6848            name = _make_phony_error_name()
6849            symbol = parentSymbol.add_name(name)
6850            env.temp_data['cpp:last_symbol'] = symbol
6851            return []
6852        # When multiple declarations are made in the same directive
6853        # they need to know about each other to provide symbol lookup for function parameters.
6854        # We use last_symbol to store the latest added declaration in a directive.
6855        env.temp_data['cpp:last_symbol'] = None
6856        return super().run()
6857
6858    def handle_signature(self, sig: str, signode: desc_signature) -> ASTDeclaration:
6859        parentSymbol = self.env.temp_data['cpp:parent_symbol']  # type: Symbol
6860
6861        parser = DefinitionParser(sig, location=signode, config=self.env.config)
6862        try:
6863            ast = self.parse_definition(parser)
6864            parser.assert_end()
6865        except DefinitionError as e:
6866            logger.warning(e, location=signode)
6867            # It is easier to assume some phony name than handling the error in
6868            # the possibly inner declarations.
6869            name = _make_phony_error_name()
6870            symbol = parentSymbol.add_name(name)
6871            self.env.temp_data['cpp:last_symbol'] = symbol
6872            raise ValueError from e
6873
6874        try:
6875            symbol = parentSymbol.add_declaration(
6876                ast, docname=self.env.docname, line=self.get_source_info()[1])
6877            # append the new declaration to the sibling list
6878            assert symbol.siblingAbove is None
6879            assert symbol.siblingBelow is None
6880            symbol.siblingAbove = self.env.temp_data['cpp:last_symbol']
6881            if symbol.siblingAbove is not None:
6882                assert symbol.siblingAbove.siblingBelow is None
6883                symbol.siblingAbove.siblingBelow = symbol
6884            self.env.temp_data['cpp:last_symbol'] = symbol
6885        except _DuplicateSymbolError as e:
6886            # Assume we are actually in the old symbol,
6887            # instead of the newly created duplicate.
6888            self.env.temp_data['cpp:last_symbol'] = e.symbol
6889            msg = __("Duplicate C++ declaration, also defined at %s:%s.\n"
6890                     "Declaration is '.. cpp:%s:: %s'.")
6891            msg = msg % (e.symbol.docname, e.symbol.line,
6892                         self.display_object_type, sig)
6893            logger.warning(msg, location=signode)
6894
6895        if ast.objectType == 'enumerator':
6896            self._add_enumerator_to_parent(ast)
6897
6898        # note: handle_signature may be called multiple time per directive,
6899        # if it has multiple signatures, so don't mess with the original options.
6900        options = dict(self.options)
6901        options['tparam-line-spec'] = 'tparam-line-spec' in self.options
6902        self.describe_signature(signode, ast, options)
6903        return ast
6904
6905    def before_content(self) -> None:
6906        lastSymbol = self.env.temp_data['cpp:last_symbol']  # type: Symbol
6907        assert lastSymbol
6908        self.oldParentSymbol = self.env.temp_data['cpp:parent_symbol']
6909        self.oldParentKey = self.env.ref_context['cpp:parent_key']  # type: LookupKey
6910        self.env.temp_data['cpp:parent_symbol'] = lastSymbol
6911        self.env.ref_context['cpp:parent_key'] = lastSymbol.get_lookup_key()
6912
6913    def after_content(self) -> None:
6914        self.env.temp_data['cpp:parent_symbol'] = self.oldParentSymbol
6915        self.env.ref_context['cpp:parent_key'] = self.oldParentKey
6916
6917
6918class CPPTypeObject(CPPObject):
6919    object_type = 'type'
6920
6921
6922class CPPConceptObject(CPPObject):
6923    object_type = 'concept'
6924
6925
6926class CPPMemberObject(CPPObject):
6927    object_type = 'member'
6928
6929
6930class CPPFunctionObject(CPPObject):
6931    object_type = 'function'
6932
6933
6934class CPPClassObject(CPPObject):
6935    object_type = 'class'
6936
6937    @property
6938    def display_object_type(self) -> str:
6939        # the distinction between class and struct is only cosmetic
6940        assert self.objtype in ('class', 'struct')
6941        return self.objtype
6942
6943
6944class CPPUnionObject(CPPObject):
6945    object_type = 'union'
6946
6947
6948class CPPEnumObject(CPPObject):
6949    object_type = 'enum'
6950
6951
6952class CPPEnumeratorObject(CPPObject):
6953    object_type = 'enumerator'
6954
6955
6956class CPPNamespaceObject(SphinxDirective):
6957    """
6958    This directive is just to tell Sphinx that we're documenting stuff in
6959    namespace foo.
6960    """
6961
6962    has_content = False
6963    required_arguments = 1
6964    optional_arguments = 0
6965    final_argument_whitespace = True
6966    option_spec = {}  # type: Dict
6967
6968    def run(self) -> List[Node]:
6969        rootSymbol = self.env.domaindata['cpp']['root_symbol']
6970        if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
6971            symbol = rootSymbol
6972            stack = []  # type: List[Symbol]
6973        else:
6974            parser = DefinitionParser(self.arguments[0],
6975                                      location=self.get_source_info(),
6976                                      config=self.config)
6977            try:
6978                ast = parser.parse_namespace_object()
6979                parser.assert_end()
6980            except DefinitionError as e:
6981                logger.warning(e, location=self.get_source_info())
6982                name = _make_phony_error_name()
6983                ast = ASTNamespace(name, None)
6984            symbol = rootSymbol.add_name(ast.nestedName, ast.templatePrefix)
6985            stack = [symbol]
6986        self.env.temp_data['cpp:parent_symbol'] = symbol
6987        self.env.temp_data['cpp:namespace_stack'] = stack
6988        self.env.ref_context['cpp:parent_key'] = symbol.get_lookup_key()
6989        return []
6990
6991
6992class CPPNamespacePushObject(SphinxDirective):
6993    has_content = False
6994    required_arguments = 1
6995    optional_arguments = 0
6996    final_argument_whitespace = True
6997    option_spec = {}  # type: Dict
6998
6999    def run(self) -> List[Node]:
7000        if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
7001            return []
7002        parser = DefinitionParser(self.arguments[0],
7003                                  location=self.get_source_info(),
7004                                  config=self.config)
7005        try:
7006            ast = parser.parse_namespace_object()
7007            parser.assert_end()
7008        except DefinitionError as e:
7009            logger.warning(e, location=self.get_source_info())
7010            name = _make_phony_error_name()
7011            ast = ASTNamespace(name, None)
7012        oldParent = self.env.temp_data.get('cpp:parent_symbol', None)
7013        if not oldParent:
7014            oldParent = self.env.domaindata['cpp']['root_symbol']
7015        symbol = oldParent.add_name(ast.nestedName, ast.templatePrefix)
7016        stack = self.env.temp_data.get('cpp:namespace_stack', [])
7017        stack.append(symbol)
7018        self.env.temp_data['cpp:parent_symbol'] = symbol
7019        self.env.temp_data['cpp:namespace_stack'] = stack
7020        self.env.ref_context['cpp:parent_key'] = symbol.get_lookup_key()
7021        return []
7022
7023
7024class CPPNamespacePopObject(SphinxDirective):
7025    has_content = False
7026    required_arguments = 0
7027    optional_arguments = 0
7028    final_argument_whitespace = True
7029    option_spec = {}  # type: Dict
7030
7031    def run(self) -> List[Node]:
7032        stack = self.env.temp_data.get('cpp:namespace_stack', None)
7033        if not stack or len(stack) == 0:
7034            logger.warning("C++ namespace pop on empty stack. Defaulting to gobal scope.",
7035                           location=self.get_source_info())
7036            stack = []
7037        else:
7038            stack.pop()
7039        if len(stack) > 0:
7040            symbol = stack[-1]
7041        else:
7042            symbol = self.env.domaindata['cpp']['root_symbol']
7043        self.env.temp_data['cpp:parent_symbol'] = symbol
7044        self.env.temp_data['cpp:namespace_stack'] = stack
7045        self.env.ref_context['cpp:parent_key'] = symbol.get_lookup_key()
7046        return []
7047
7048
7049class AliasNode(nodes.Element):
7050    def __init__(self, sig: str, aliasOptions: dict,
7051                 env: "BuildEnvironment" = None,
7052                 parentKey: LookupKey = None) -> None:
7053        super().__init__()
7054        self.sig = sig
7055        self.aliasOptions = aliasOptions
7056        if env is not None:
7057            if 'cpp:parent_symbol' not in env.temp_data:
7058                root = env.domaindata['cpp']['root_symbol']
7059                env.temp_data['cpp:parent_symbol'] = root
7060            self.parentKey = env.temp_data['cpp:parent_symbol'].get_lookup_key()
7061        else:
7062            assert parentKey is not None
7063            self.parentKey = parentKey
7064
7065    def copy(self) -> 'AliasNode':
7066        return self.__class__(self.sig, self.aliasOptions,
7067                              env=None, parentKey=self.parentKey)
7068
7069
7070class AliasTransform(SphinxTransform):
7071    default_priority = ReferencesResolver.default_priority - 1
7072
7073    def _render_symbol(self, s: Symbol, maxdepth: int, skipThis: bool,
7074                       aliasOptions: dict, renderOptions: dict,
7075                       document: Any) -> List[Node]:
7076        if maxdepth == 0:
7077            recurse = True
7078        elif maxdepth == 1:
7079            recurse = False
7080        else:
7081            maxdepth -= 1
7082            recurse = True
7083
7084        nodes = []  # type: List[Node]
7085        if not skipThis:
7086            signode = addnodes.desc_signature('', '')
7087            nodes.append(signode)
7088            s.declaration.describe_signature(signode, 'markName', self.env, renderOptions)
7089
7090        if recurse:
7091            if skipThis:
7092                childContainer = nodes  # type: Union[List[Node], addnodes.desc]
7093            else:
7094                content = addnodes.desc_content()
7095                desc = addnodes.desc()
7096                content.append(desc)
7097                desc.document = document
7098                desc['domain'] = 'cpp'
7099                # 'desctype' is a backwards compatible attribute
7100                desc['objtype'] = desc['desctype'] = 'alias'
7101                desc['noindex'] = True
7102                childContainer = desc
7103
7104            for sChild in s._children:
7105                if sChild.declaration is None:
7106                    continue
7107                if sChild.declaration.objectType in ("templateParam", "functionParam"):
7108                    continue
7109                childNodes = self._render_symbol(
7110                    sChild, maxdepth=maxdepth, skipThis=False,
7111                    aliasOptions=aliasOptions, renderOptions=renderOptions,
7112                    document=document)
7113                childContainer.extend(childNodes)
7114
7115            if not skipThis and len(desc.children) != 0:
7116                nodes.append(content)
7117        return nodes
7118
7119    def apply(self, **kwargs: Any) -> None:
7120        for node in self.document.traverse(AliasNode):
7121            node = cast(AliasNode, node)
7122            sig = node.sig
7123            parentKey = node.parentKey
7124            try:
7125                parser = DefinitionParser(sig, location=node,
7126                                          config=self.env.config)
7127                ast, isShorthand = parser.parse_xref_object()
7128                parser.assert_end()
7129            except DefinitionError as e:
7130                logger.warning(e, location=node)
7131                ast, isShorthand = None, None
7132
7133            if ast is None:
7134                # could not be parsed, so stop here
7135                signode = addnodes.desc_signature(sig, '')
7136                signode.clear()
7137                signode += addnodes.desc_name(sig, sig)
7138                node.replace_self(signode)
7139                continue
7140
7141            rootSymbol = self.env.domains['cpp'].data['root_symbol']  # type: Symbol
7142            parentSymbol = rootSymbol.direct_lookup(parentKey)  # type: Symbol
7143            if not parentSymbol:
7144                print("Target: ", sig)
7145                print("ParentKey: ", parentKey)
7146                print(rootSymbol.dump(1))
7147            assert parentSymbol  # should be there
7148
7149            symbols = []  # type: List[Symbol]
7150            if isShorthand:
7151                assert isinstance(ast, ASTNamespace)
7152                ns = ast
7153                name = ns.nestedName
7154                if ns.templatePrefix:
7155                    templateDecls = ns.templatePrefix.templates
7156                else:
7157                    templateDecls = []
7158                symbols, failReason = parentSymbol.find_name(
7159                    nestedName=name,
7160                    templateDecls=templateDecls,
7161                    typ='any',
7162                    templateShorthand=True,
7163                    matchSelf=True, recurseInAnon=True,
7164                    searchInSiblings=False)
7165                if symbols is None:
7166                    symbols = []
7167            else:
7168                assert isinstance(ast, ASTDeclaration)
7169                decl = ast
7170                name = decl.name
7171                s = parentSymbol.find_declaration(decl, 'any',
7172                                                  templateShorthand=True,
7173                                                  matchSelf=True, recurseInAnon=True)
7174                if s is not None:
7175                    symbols.append(s)
7176
7177            symbols = [s for s in symbols if s.declaration is not None]
7178
7179            if len(symbols) == 0:
7180                signode = addnodes.desc_signature(sig, '')
7181                node.append(signode)
7182                signode.clear()
7183                signode += addnodes.desc_name(sig, sig)
7184
7185                logger.warning("Can not find C++ declaration for alias '%s'." % ast,
7186                               location=node)
7187                node.replace_self(signode)
7188            else:
7189                nodes = []
7190                renderOptions = {
7191                    'tparam-line-spec': False,
7192                }
7193                for s in symbols:
7194                    assert s.declaration is not None
7195                    res = self._render_symbol(
7196                        s, maxdepth=node.aliasOptions['maxdepth'],
7197                        skipThis=node.aliasOptions['noroot'],
7198                        aliasOptions=node.aliasOptions,
7199                        renderOptions=renderOptions,
7200                        document=node.document)
7201                    nodes.extend(res)
7202                node.replace_self(nodes)
7203
7204
7205class CPPAliasObject(ObjectDescription):
7206    option_spec = {
7207        'maxdepth': directives.nonnegative_int,
7208        'noroot': directives.flag,
7209    }  # type: Dict
7210
7211    def run(self) -> List[Node]:
7212        """
7213        On purpose this doesn't call the ObjectDescription version, but is based on it.
7214        Each alias signature may expand into multiple real signatures (an overload set).
7215        The code is therefore based on the ObjectDescription version.
7216        """
7217        if ':' in self.name:
7218            self.domain, self.objtype = self.name.split(':', 1)
7219        else:
7220            self.domain, self.objtype = '', self.name
7221
7222        node = addnodes.desc()
7223        node.document = self.state.document
7224        node['domain'] = self.domain
7225        # 'desctype' is a backwards compatible attribute
7226        node['objtype'] = node['desctype'] = self.objtype
7227
7228        self.names = []  # type: List[str]
7229        aliasOptions = {
7230            'maxdepth': self.options.get('maxdepth', 1),
7231            'noroot': 'noroot' in self.options,
7232        }
7233        if aliasOptions['noroot'] and aliasOptions['maxdepth'] == 1:
7234            logger.warning("Error in C++ alias declaration."
7235                           " Requested 'noroot' but 'maxdepth' 1."
7236                           " When skipping the root declaration,"
7237                           " need 'maxdepth' 0 for infinite or at least 2.",
7238                           location=self.get_source_info())
7239        signatures = self.get_signatures()
7240        for i, sig in enumerate(signatures):
7241            node.append(AliasNode(sig, aliasOptions, env=self.env))
7242
7243        contentnode = addnodes.desc_content()
7244        node.append(contentnode)
7245        self.before_content()
7246        self.state.nested_parse(self.content, self.content_offset, contentnode)
7247        self.env.temp_data['object'] = None
7248        self.after_content()
7249        return [node]
7250
7251
7252class CPPXRefRole(XRefRole):
7253    def process_link(self, env: BuildEnvironment, refnode: Element, has_explicit_title: bool,
7254                     title: str, target: str) -> Tuple[str, str]:
7255        refnode.attributes.update(env.ref_context)
7256
7257        if not has_explicit_title:
7258            # major hax: replace anon names via simple string manipulation.
7259            # Can this actually fail?
7260            title = anon_identifier_re.sub("[anonymous]", str(title))
7261
7262        if refnode['reftype'] == 'any':
7263            # Assume the removal part of fix_parens for :any: refs.
7264            # The addition part is done with the reference is resolved.
7265            if not has_explicit_title and title.endswith('()'):
7266                title = title[:-2]
7267            if target.endswith('()'):
7268                target = target[:-2]
7269        # TODO: should this really be here?
7270        if not has_explicit_title:
7271            target = target.lstrip('~')  # only has a meaning for the title
7272            # if the first character is a tilde, don't display the module/class
7273            # parts of the contents
7274            if title[:1] == '~':
7275                title = title[1:]
7276                dcolon = title.rfind('::')
7277                if dcolon != -1:
7278                    title = title[dcolon + 2:]
7279        return title, target
7280
7281
7282class CPPExprRole(SphinxRole):
7283    def __init__(self, asCode: bool) -> None:
7284        super().__init__()
7285        if asCode:
7286            # render the expression as inline code
7287            self.class_type = 'cpp-expr'
7288            self.node_type = nodes.literal  # type: Type[TextElement]
7289        else:
7290            # render the expression as inline text
7291            self.class_type = 'cpp-texpr'
7292            self.node_type = nodes.inline
7293
7294    def run(self) -> Tuple[List[Node], List[system_message]]:
7295        text = self.text.replace('\n', ' ')
7296        parser = DefinitionParser(text,
7297                                  location=self.get_source_info(),
7298                                  config=self.config)
7299        # attempt to mimic XRefRole classes, except that...
7300        classes = ['xref', 'cpp', self.class_type]
7301        try:
7302            ast = parser.parse_expression()
7303        except DefinitionError as ex:
7304            logger.warning('Unparseable C++ expression: %r\n%s', text, ex,
7305                           location=self.get_source_info())
7306            # see below
7307            return [self.node_type(text, text, classes=classes)], []
7308        parentSymbol = self.env.temp_data.get('cpp:parent_symbol', None)
7309        if parentSymbol is None:
7310            parentSymbol = self.env.domaindata['cpp']['root_symbol']
7311        # ...most if not all of these classes should really apply to the individual references,
7312        # not the container node
7313        signode = self.node_type(classes=classes)
7314        ast.describe_signature(signode, 'markType', self.env, parentSymbol)
7315        return [signode], []
7316
7317
7318class CPPDomain(Domain):
7319    """C++ language domain.
7320
7321    There are two 'object type' attributes being used::
7322
7323    - Each object created from directives gets an assigned .objtype from ObjectDescription.run.
7324      This is simply the directive name.
7325    - Each declaration (see the distinction in the directives dict below) has a nested .ast of
7326      type ASTDeclaration. That object has .objectType which corresponds to the keys in the
7327      object_types dict below. They are the core different types of declarations in C++ that
7328      one can document.
7329    """
7330    name = 'cpp'
7331    label = 'C++'
7332    object_types = {
7333        'class':      ObjType(_('class'),      'class', 'struct',   'identifier', 'type'),
7334        'union':      ObjType(_('union'),      'union',             'identifier', 'type'),
7335        'function':   ObjType(_('function'),   'func',              'identifier', 'type'),
7336        'member':     ObjType(_('member'),     'member', 'var',     'identifier'),
7337        'type':       ObjType(_('type'),                            'identifier', 'type'),
7338        'concept':    ObjType(_('concept'),    'concept',           'identifier'),
7339        'enum':       ObjType(_('enum'),       'enum',              'identifier', 'type'),
7340        'enumerator': ObjType(_('enumerator'), 'enumerator',        'identifier'),
7341        # generated object types
7342        'functionParam': ObjType(_('function parameter'),           'identifier', 'member', 'var'),  # noqa
7343        'templateParam': ObjType(_('template parameter'),
7344                                 'identifier', 'class', 'struct', 'union', 'member', 'var', 'type'),  # noqa
7345    }
7346
7347    directives = {
7348        # declarations
7349        'class': CPPClassObject,
7350        'struct': CPPClassObject,
7351        'union': CPPUnionObject,
7352        'function': CPPFunctionObject,
7353        'member': CPPMemberObject,
7354        'var': CPPMemberObject,
7355        'type': CPPTypeObject,
7356        'concept': CPPConceptObject,
7357        'enum': CPPEnumObject,
7358        'enum-struct': CPPEnumObject,
7359        'enum-class': CPPEnumObject,
7360        'enumerator': CPPEnumeratorObject,
7361        # scope control
7362        'namespace': CPPNamespaceObject,
7363        'namespace-push': CPPNamespacePushObject,
7364        'namespace-pop': CPPNamespacePopObject,
7365        # other
7366        'alias': CPPAliasObject
7367    }
7368    roles = {
7369        'any': CPPXRefRole(),
7370        'class': CPPXRefRole(),
7371        'struct': CPPXRefRole(),
7372        'union': CPPXRefRole(),
7373        'func': CPPXRefRole(fix_parens=True),
7374        'member': CPPXRefRole(),
7375        'var': CPPXRefRole(),
7376        'type': CPPXRefRole(),
7377        'concept': CPPXRefRole(),
7378        'enum': CPPXRefRole(),
7379        'enumerator': CPPXRefRole(),
7380        'expr': CPPExprRole(asCode=True),
7381        'texpr': CPPExprRole(asCode=False)
7382    }
7383    initial_data = {
7384        'root_symbol': Symbol(None, None, None, None, None, None, None),
7385        'names': {}  # full name for indexing -> docname
7386    }
7387
7388    def clear_doc(self, docname: str) -> None:
7389        if Symbol.debug_show_tree:
7390            print("clear_doc:", docname)
7391            print("\tbefore:")
7392            print(self.data['root_symbol'].dump(1))
7393            print("\tbefore end")
7394
7395        rootSymbol = self.data['root_symbol']
7396        rootSymbol.clear_doc(docname)
7397
7398        if Symbol.debug_show_tree:
7399            print("\tafter:")
7400            print(self.data['root_symbol'].dump(1))
7401            print("\tafter end")
7402            print("clear_doc end:", docname)
7403        for name, nDocname in list(self.data['names'].items()):
7404            if nDocname == docname:
7405                del self.data['names'][name]
7406
7407    def process_doc(self, env: BuildEnvironment, docname: str,
7408                    document: nodes.document) -> None:
7409        if Symbol.debug_show_tree:
7410            print("process_doc:", docname)
7411            print(self.data['root_symbol'].dump(0))
7412            print("process_doc end:", docname)
7413
7414    def process_field_xref(self, pnode: pending_xref) -> None:
7415        pnode.attributes.update(self.env.ref_context)
7416
7417    def merge_domaindata(self, docnames: List[str], otherdata: Dict) -> None:
7418        if Symbol.debug_show_tree:
7419            print("merge_domaindata:")
7420            print("\tself:")
7421            print(self.data['root_symbol'].dump(1))
7422            print("\tself end")
7423            print("\tother:")
7424            print(otherdata['root_symbol'].dump(1))
7425            print("\tother end")
7426
7427        self.data['root_symbol'].merge_with(otherdata['root_symbol'],
7428                                            docnames, self.env)
7429        ourNames = self.data['names']
7430        for name, docname in otherdata['names'].items():
7431            if docname in docnames:
7432                if name not in ourNames:
7433                    ourNames[name] = docname
7434                # no need to warn on duplicates, the symbol merge already does that
7435        if Symbol.debug_show_tree:
7436            print("\tresult:")
7437            print(self.data['root_symbol'].dump(1))
7438            print("\tresult end")
7439            print("merge_domaindata end")
7440
7441    def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
7442                            typ: str, target: str, node: pending_xref,
7443                            contnode: Element) -> Tuple[Element, str]:
7444        # add parens again for those that could be functions
7445        if typ == 'any' or typ == 'func':
7446            target += '()'
7447        parser = DefinitionParser(target, location=node, config=env.config)
7448        try:
7449            ast, isShorthand = parser.parse_xref_object()
7450        except DefinitionError as e:
7451            # as arg to stop flake8 from complaining
7452            def findWarning(e: Exception) -> Tuple[str, Exception]:
7453                if typ != 'any' and typ != 'func':
7454                    return target, e
7455                # hax on top of the paren hax to try to get correct errors
7456                parser2 = DefinitionParser(target[:-2],
7457                                           location=node,
7458                                           config=env.config)
7459                try:
7460                    parser2.parse_xref_object()
7461                except DefinitionError as e2:
7462                    return target[:-2], e2
7463                # strange, that we don't get the error now, use the original
7464                return target, e
7465            t, ex = findWarning(e)
7466            logger.warning('Unparseable C++ cross-reference: %r\n%s', t, ex,
7467                           location=node)
7468            return None, None
7469        parentKey = node.get("cpp:parent_key", None)  # type: LookupKey
7470        rootSymbol = self.data['root_symbol']
7471        if parentKey:
7472            parentSymbol = rootSymbol.direct_lookup(parentKey)  # type: Symbol
7473            if not parentSymbol:
7474                print("Target: ", target)
7475                print("ParentKey: ", parentKey.data)
7476                print(rootSymbol.dump(1))
7477            assert parentSymbol  # should be there
7478        else:
7479            parentSymbol = rootSymbol
7480
7481        if isShorthand:
7482            assert isinstance(ast, ASTNamespace)
7483            ns = ast
7484            name = ns.nestedName
7485            if ns.templatePrefix:
7486                templateDecls = ns.templatePrefix.templates
7487            else:
7488                templateDecls = []
7489            # let's be conservative with the sibling lookup for now
7490            searchInSiblings = (not name.rooted) and len(name.names) == 1
7491            symbols, failReason = parentSymbol.find_name(
7492                name, templateDecls, typ,
7493                templateShorthand=True,
7494                matchSelf=True, recurseInAnon=True,
7495                searchInSiblings=searchInSiblings)
7496            if symbols is None:
7497                if typ == 'identifier':
7498                    if failReason == 'templateParamInQualified':
7499                        # this is an xref we created as part of a signature,
7500                        # so don't warn for names nested in template parameters
7501                        raise NoUri(str(name), typ)
7502                s = None
7503            else:
7504                # just refer to the arbitrarily first symbol
7505                s = symbols[0]
7506        else:
7507            assert isinstance(ast, ASTDeclaration)
7508            decl = ast
7509            name = decl.name
7510            s = parentSymbol.find_declaration(decl, typ,
7511                                              templateShorthand=True,
7512                                              matchSelf=True, recurseInAnon=True)
7513        if s is None or s.declaration is None:
7514            txtName = str(name)
7515            if txtName.startswith('std::') or txtName == 'std':
7516                raise NoUri(txtName, typ)
7517            return None, None
7518
7519        if typ.startswith('cpp:'):
7520            typ = typ[4:]
7521        declTyp = s.declaration.objectType
7522
7523        def checkType() -> bool:
7524            if typ == 'any':
7525                return True
7526            objtypes = self.objtypes_for_role(typ)
7527            if objtypes:
7528                return declTyp in objtypes
7529            print("Type is %s, declaration type is %s" % (typ, declTyp))
7530            assert False
7531        if not checkType():
7532            logger.warning("cpp:%s targets a %s (%s).",
7533                           typ, s.declaration.objectType,
7534                           s.get_full_nested_name(),
7535                           location=node)
7536
7537        declaration = s.declaration
7538        if isShorthand:
7539            fullNestedName = s.get_full_nested_name()
7540            displayName = fullNestedName.get_display_string().lstrip(':')
7541        else:
7542            displayName = decl.get_display_string()
7543        docname = s.docname
7544        assert docname
7545
7546        # the non-identifier refs are cross-references, which should be processed:
7547        # - fix parenthesis due to operator() and add_function_parentheses
7548        if typ != "identifier":
7549            title = contnode.pop(0).astext()
7550            # If it's operator(), we need to add '()' if explicit function parens
7551            # are requested. Then the Sphinx machinery will add another pair.
7552            # Also, if it's an 'any' ref that resolves to a function, we need to add
7553            # parens as well.
7554            # However, if it's a non-shorthand function ref, for a function that
7555            # takes no arguments, then we may need to add parens again as well.
7556            addParen = 0
7557            if not node.get('refexplicit', False) and declaration.objectType == 'function':
7558                if isShorthand:
7559                    # this is just the normal haxing for 'any' roles
7560                    if env.config.add_function_parentheses and typ == 'any':
7561                        addParen += 1
7562                    # and now this stuff for operator()
7563                    if (env.config.add_function_parentheses and typ == 'func' and
7564                            title.endswith('operator()')):
7565                        addParen += 1
7566                    if ((typ == 'any' or typ == 'func') and
7567                            title.endswith('operator') and
7568                            displayName.endswith('operator()')):
7569                        addParen += 1
7570                else:
7571                    # our job here is to essentially nullify add_function_parentheses
7572                    if env.config.add_function_parentheses:
7573                        if typ == 'any' and displayName.endswith('()'):
7574                            addParen += 1
7575                        elif typ == 'func':
7576                            if title.endswith('()') and not displayName.endswith('()'):
7577                                title = title[:-2]
7578                    else:
7579                        if displayName.endswith('()'):
7580                            addParen += 1
7581            if addParen > 0:
7582                title += '()' * addParen
7583            # and reconstruct the title again
7584            contnode += nodes.Text(title)
7585        return make_refnode(builder, fromdocname, docname,
7586                            declaration.get_newest_id(), contnode, displayName
7587                            ), declaration.objectType
7588
7589    def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
7590                     typ: str, target: str, node: pending_xref, contnode: Element
7591                     ) -> Element:
7592        return self._resolve_xref_inner(env, fromdocname, builder, typ,
7593                                        target, node, contnode)[0]
7594
7595    def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
7596                         target: str, node: pending_xref, contnode: Element
7597                         ) -> List[Tuple[str, Element]]:
7598        with logging.suppress_logging():
7599            retnode, objtype = self._resolve_xref_inner(env, fromdocname, builder,
7600                                                        'any', target, node, contnode)
7601        if retnode:
7602            if objtype == 'templateParam':
7603                return [('cpp:templateParam', retnode)]
7604            else:
7605                return [('cpp:' + self.role_for_objtype(objtype), retnode)]
7606        return []
7607
7608    def get_objects(self) -> Iterator[Tuple[str, str, str, str, str, int]]:
7609        rootSymbol = self.data['root_symbol']
7610        for symbol in rootSymbol.get_all_symbols():
7611            if symbol.declaration is None:
7612                continue
7613            assert symbol.docname
7614            fullNestedName = symbol.get_full_nested_name()
7615            name = str(fullNestedName).lstrip(':')
7616            dispname = fullNestedName.get_display_string().lstrip(':')
7617            objectType = symbol.declaration.objectType
7618            docname = symbol.docname
7619            newestId = symbol.declaration.get_newest_id()
7620            yield (name, dispname, objectType, docname, newestId, 1)
7621
7622    def get_full_qualified_name(self, node: Element) -> str:
7623        target = node.get('reftarget', None)
7624        if target is None:
7625            return None
7626        parentKey = node.get("cpp:parent_key", None)  # type: LookupKey
7627        if parentKey is None or len(parentKey.data) <= 0:
7628            return None
7629
7630        rootSymbol = self.data['root_symbol']
7631        parentSymbol = rootSymbol.direct_lookup(parentKey)
7632        parentName = parentSymbol.get_full_nested_name()
7633        return '::'.join([str(parentName), target])
7634
7635
7636def setup(app: Sphinx) -> Dict[str, Any]:
7637    app.add_domain(CPPDomain)
7638    app.add_config_value("cpp_index_common_prefix", [], 'env')
7639    app.add_config_value("cpp_id_attributes", [], 'env')
7640    app.add_config_value("cpp_paren_attributes", [], 'env')
7641    app.add_post_transform(AliasTransform)
7642
7643    # debug stuff
7644    app.add_config_value("cpp_debug_lookup", False, '')
7645    app.add_config_value("cpp_debug_show_tree", False, '')
7646
7647    def setDebugFlags(app):
7648        Symbol.debug_lookup = app.config.cpp_debug_lookup
7649        Symbol.debug_show_tree = app.config.cpp_debug_show_tree
7650    app.connect("builder-inited", setDebugFlags)
7651
7652    return {
7653        'version': 'builtin',
7654        'env_version': 3,
7655        'parallel_read_safe': True,
7656        'parallel_write_safe': True,
7657    }
7658