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