|
9 | 9 | """ |
10 | 10 |
|
11 | 11 | import re |
12 | | -from typing import (Any, Callable, Dict, Generator, Iterator, List, Tuple, Type, TypeVar, |
13 | | - Union, cast) |
| 12 | +from typing import (Any, Callable, Dict, Generator, Iterator, List, Optional, Tuple, Type, |
| 13 | + TypeVar, Union, cast) |
14 | 14 |
|
15 | 15 | from docutils import nodes |
16 | 16 | from docutils.nodes import Element, Node, TextElement, system_message |
|
78 | 78 | _expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=", |
79 | 79 | ">>=", "<<=", "&=", "and_eq", "^=", "xor_eq", "|=", "or_eq"] |
80 | 80 |
|
81 | | -_max_id = 1 |
82 | | -_id_prefix = [None, 'c.', 'Cv2.'] |
| 81 | +_max_id = 2 |
| 82 | +_id_prefix = [None, 'c.', 'C2-'] |
83 | 83 | # Ids are used in lookup keys which are used across pickled files, |
84 | 84 | # so when _max_id changes, make sure to update the ENV_VERSION. |
85 | 85 |
|
@@ -108,31 +108,54 @@ def describe_signature(self, signode: TextElement, mode: str, |
108 | 108 | ################################################################################ |
109 | 109 |
|
110 | 110 | class ASTIdentifier(ASTBaseBase): |
111 | | - def __init__(self, identifier: str) -> None: |
| 111 | + def __init__(self, identifier: str, tag: Optional[str]) -> None: |
| 112 | + # tag: |
| 113 | + # - len > 0: it's a struct/union/enum |
| 114 | + # - len == 0: it's not a struct/union/enum |
| 115 | + # - None: for matching, can be either |
112 | 116 | assert identifier is not None |
113 | 117 | assert len(identifier) != 0 |
114 | 118 | self.identifier = identifier |
| 119 | + self.tag = tag |
115 | 120 |
|
116 | 121 | def __eq__(self, other: Any) -> bool: |
117 | | - return type(other) is ASTIdentifier and self.identifier == other.identifier |
| 122 | + return type(other) is ASTIdentifier \ |
| 123 | + and self.identifier == other.identifier \ |
| 124 | + and self.tag == other.tag |
118 | 125 |
|
119 | 126 | def is_anon(self) -> bool: |
120 | 127 | return self.identifier[0] == '@' |
121 | 128 |
|
| 129 | + def get_id(self, version: int) -> str: |
| 130 | + if version <= 1: |
| 131 | + return self.identifier |
| 132 | + if self.tag: |
| 133 | + assert len(self.tag) != 0 |
| 134 | + return '-' + self.identifier |
| 135 | + else: |
| 136 | + return self.identifier |
| 137 | + |
122 | 138 | # and this is where we finally make a difference between __str__ and the display string |
123 | 139 |
|
124 | 140 | def __str__(self) -> str: |
125 | | - return self.identifier |
| 141 | + return self.tag + " " + self.identifier if self.tag else self.identifier |
126 | 142 |
|
127 | 143 | def get_display_string(self) -> str: |
128 | | - return "[anonymous]" if self.is_anon() else self.identifier |
| 144 | + id = "[anonymous]" if self.is_anon() else self.identifier |
| 145 | + return self.tag + " " + id if self.tag else id |
129 | 146 |
|
130 | 147 | def describe_signature(self, signode: TextElement, mode: str, env: "BuildEnvironment", |
131 | 148 | prefix: str, symbol: "Symbol") -> None: |
132 | 149 | # note: slightly different signature of describe_signature due to the prefix |
133 | 150 | verify_description_mode(mode) |
| 151 | + if self.tag: |
| 152 | + signode += nodes.Text(self.tag) |
| 153 | + signode += nodes.Text(' ') |
134 | 154 | if mode == 'markType': |
135 | | - targetText = prefix + self.identifier |
| 155 | + if self.tag: |
| 156 | + targetText = prefix + self.tag + ' ' + self.identifier |
| 157 | + else: |
| 158 | + targetText = prefix + self.identifier |
136 | 159 | pnode = addnodes.pending_xref('', refdomain='c', |
137 | 160 | reftype='identifier', |
138 | 161 | reftarget=targetText, modname=None, |
@@ -164,12 +187,17 @@ def __init__(self, names: List[ASTIdentifier], rooted: bool) -> None: |
164 | 187 | self.names = names |
165 | 188 | self.rooted = rooted |
166 | 189 |
|
| 190 | + def setTagsToPattern(self) -> None: |
| 191 | + for n in self.names: |
| 192 | + if n.tag == '': |
| 193 | + n.tag = None |
| 194 | + |
167 | 195 | @property |
168 | 196 | def name(self) -> "ASTNestedName": |
169 | 197 | return self |
170 | 198 |
|
171 | 199 | def get_id(self, version: int) -> str: |
172 | | - return '.'.join(str(n) for n in self.names) |
| 200 | + return '.'.join(n.get_id(version) for n in self.names) |
173 | 201 |
|
174 | 202 | def _stringify(self, transform: StringifyTransform) -> str: |
175 | 203 | res = '.'.join(transform(n) for n in self.names) |
@@ -595,27 +623,18 @@ def describe_signature(self, signode: TextElement, mode: str, |
595 | 623 |
|
596 | 624 |
|
597 | 625 | class ASTTrailingTypeSpecName(ASTTrailingTypeSpec): |
598 | | - def __init__(self, prefix: str, nestedName: ASTNestedName) -> None: |
599 | | - self.prefix = prefix |
| 626 | + def __init__(self, nestedName: ASTNestedName) -> None: |
600 | 627 | self.nestedName = nestedName |
601 | 628 |
|
602 | 629 | @property |
603 | 630 | def name(self) -> ASTNestedName: |
604 | 631 | return self.nestedName |
605 | 632 |
|
606 | 633 | def _stringify(self, transform: StringifyTransform) -> str: |
607 | | - res = [] |
608 | | - if self.prefix: |
609 | | - res.append(self.prefix) |
610 | | - res.append(' ') |
611 | | - res.append(transform(self.nestedName)) |
612 | | - return ''.join(res) |
| 634 | + return transform(self.nestedName) |
613 | 635 |
|
614 | 636 | def describe_signature(self, signode: TextElement, mode: str, |
615 | 637 | env: "BuildEnvironment", symbol: "Symbol") -> None: |
616 | | - if self.prefix: |
617 | | - signode += addnodes.desc_annotation(self.prefix, self.prefix) |
618 | | - signode += nodes.Text(' ') |
619 | 638 | self.nestedName.describe_signature(signode, mode, env, symbol=symbol) |
620 | 639 |
|
621 | 640 |
|
@@ -1863,23 +1882,9 @@ def handleDuplicateDeclaration(symbol: "Symbol", candSymbol: "Symbol") -> None: |
1863 | 1882 | candSymbol.isRedeclaration = True |
1864 | 1883 | raise _DuplicateSymbolError(symbol, declaration) |
1865 | 1884 |
|
1866 | | - if declaration.objectType != "function": |
1867 | | - assert len(withDecl) <= 1 |
1868 | | - handleDuplicateDeclaration(withDecl[0], candSymbol) |
1869 | | - # (not reachable) |
1870 | | - |
1871 | | - # a function, so compare IDs |
1872 | | - candId = declaration.get_newest_id() |
1873 | | - if Symbol.debug_lookup: |
1874 | | - Symbol.debug_print("candId:", candId) |
1875 | | - for symbol in withDecl: |
1876 | | - oldId = symbol.declaration.get_newest_id() |
1877 | | - if Symbol.debug_lookup: |
1878 | | - Symbol.debug_print("oldId: ", oldId) |
1879 | | - if candId == oldId: |
1880 | | - handleDuplicateDeclaration(symbol, candSymbol) |
1881 | | - # (not reachable) |
1882 | | - # no candidate symbol found with matching ID |
| 1885 | + assert len(withDecl) <= 1 |
| 1886 | + handleDuplicateDeclaration(withDecl[0], candSymbol) |
| 1887 | + # (not reachable) |
1883 | 1888 | # if there is an empty symbol, fill that one |
1884 | 1889 | if len(noDecl) == 0: |
1885 | 1890 | if Symbol.debug_lookup: |
@@ -1961,8 +1966,12 @@ def add_declaration(self, declaration: ASTDeclaration, |
1961 | 1966 | Symbol.debug_print("add_declaration:") |
1962 | 1967 | assert declaration is not None |
1963 | 1968 | assert docname is not None |
1964 | | - assert line is not None |
1965 | | - nestedName = declaration.name |
| 1969 | + assert declaration.name.names[-1].tag == '' |
| 1970 | + if declaration.objectType in ('struct', 'union', 'enum'): |
| 1971 | + nestedName = declaration.name.clone() |
| 1972 | + nestedName.names[-1].tag = declaration.objectType |
| 1973 | + else: |
| 1974 | + nestedName = declaration.name |
1966 | 1975 | res = self._add_symbols(nestedName, declaration, docname, line) |
1967 | 1976 | if Symbol.debug_lookup: |
1968 | 1977 | Symbol.debug_indent -= 1 |
@@ -2090,8 +2099,6 @@ class DefinitionParser(BaseParser): |
2090 | 2099 | '__int64', |
2091 | 2100 | ) |
2092 | 2101 |
|
2093 | | - _prefix_keys = ('struct', 'enum', 'union') |
2094 | | - |
2095 | 2102 | @property |
2096 | 2103 | def language(self) -> str: |
2097 | 2104 | return 'C' |
@@ -2183,10 +2190,9 @@ def _parse_primary_expression(self) -> ASTExpression: |
2183 | 2190 | res = self._parse_paren_expression() |
2184 | 2191 | if res is not None: |
2185 | 2192 | return res |
2186 | | - nn = self._parse_nested_name() |
2187 | | - if nn is not None: |
2188 | | - return ASTIdExpression(nn) |
2189 | | - return None |
| 2193 | + nn = self._parse_nested_name(allowTags=None) |
| 2194 | + assert nn is not None |
| 2195 | + return ASTIdExpression(nn) |
2190 | 2196 |
|
2191 | 2197 | def _parse_initializer_list(self, name: str, open: str, close: str |
2192 | 2198 | ) -> Tuple[List[ASTExpression], bool]: |
@@ -2267,7 +2273,7 @@ def _parse_postfix_expression(self) -> ASTPostfixExpr: |
2267 | 2273 | # don't steal the arrow |
2268 | 2274 | self.pos -= 3 |
2269 | 2275 | else: |
2270 | | - name = self._parse_nested_name() |
| 2276 | + name = self._parse_nested_name(allowTags=None) |
2271 | 2277 | postFixes.append(ASTPostfixMemberOfPointer(name)) |
2272 | 2278 | continue |
2273 | 2279 | if self.skip_string('++'): |
@@ -2485,29 +2491,46 @@ def _parse_expression_fallback( |
2485 | 2491 | value = self.definition[startPos:self.pos].strip() |
2486 | 2492 | return ASTFallbackExpr(value.strip()) |
2487 | 2493 |
|
2488 | | - def _parse_nested_name(self) -> ASTNestedName: |
2489 | | - names = [] # type: List[Any] |
| 2494 | + def _parse_nested_name(self, *, allowTags: Optional[str] = 'not last') -> ASTNestedName: |
| 2495 | + # A dot-separated list of identifiers, each with an optional struct/union/enum tag. |
| 2496 | + # A leading dot makes the name rooted at global scope. |
| 2497 | + |
| 2498 | + assert allowTags in (None, 'all', 'not last') |
| 2499 | + |
| 2500 | + names = [] # type: List[ASTIdentifier] |
2490 | 2501 |
|
2491 | | - self.skip_ws() |
2492 | 2502 | rooted = False |
2493 | 2503 | if self.skip_string('.'): |
2494 | 2504 | rooted = True |
2495 | 2505 | while 1: |
2496 | 2506 | self.skip_ws() |
| 2507 | + tag = '' |
| 2508 | + if allowTags is not None: |
| 2509 | + for k in ('struct', 'union', 'enum'): |
| 2510 | + if self.skip_word_and_ws(k): |
| 2511 | + tag = k |
| 2512 | + break |
| 2513 | + afterTagPos = self.pos |
2497 | 2514 | if not self.match(identifier_re): |
2498 | 2515 | self.fail("Expected identifier in nested name.") |
2499 | 2516 | identifier = self.matched_text |
2500 | 2517 | # make sure there isn't a keyword |
2501 | 2518 | if identifier in _keywords: |
2502 | 2519 | self.fail("Expected identifier in nested name, " |
2503 | 2520 | "got keyword: %s" % identifier) |
2504 | | - ident = ASTIdentifier(identifier) |
| 2521 | + ident = ASTIdentifier(identifier, tag) |
2505 | 2522 | names.append(ident) |
2506 | 2523 |
|
2507 | 2524 | self.skip_ws() |
2508 | 2525 | if not self.skip_string('.'): |
2509 | 2526 | break |
2510 | | - return ASTNestedName(names, rooted) |
| 2527 | + |
| 2528 | + # post hax to explicitly forbid the last one to have a tag |
| 2529 | + if allowTags == 'not last' and names[-1].tag != '': |
| 2530 | + self.pos = afterTagPos |
| 2531 | + self.fail("Expected identifier in nested name, " |
| 2532 | + "got keyword: %s" % names[-1].tag) |
| 2533 | + return ASTNestedName(names, rooted=rooted) |
2511 | 2534 |
|
2512 | 2535 | def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec: |
2513 | 2536 | # fundamental types |
@@ -2540,16 +2563,8 @@ def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec: |
2540 | 2563 | if len(elements) > 0: |
2541 | 2564 | return ASTTrailingTypeSpecFundamental(' '.join(elements)) |
2542 | 2565 |
|
2543 | | - # prefixed |
2544 | | - prefix = None |
2545 | | - self.skip_ws() |
2546 | | - for k in self._prefix_keys: |
2547 | | - if self.skip_word_and_ws(k): |
2548 | | - prefix = k |
2549 | | - break |
2550 | | - |
2551 | | - nestedName = self._parse_nested_name() |
2552 | | - return ASTTrailingTypeSpecName(prefix, nestedName) |
| 2566 | + nestedName = self._parse_nested_name(allowTags='all') |
| 2567 | + return ASTTrailingTypeSpecName(nestedName) |
2553 | 2568 |
|
2554 | 2569 | def _parse_parameters(self, paramMode: str) -> ASTParameters: |
2555 | 2570 | self.skip_ws() |
@@ -2677,7 +2692,7 @@ def _parse_declarator_name_suffix( |
2677 | 2692 | if self.matched_text in _keywords: |
2678 | 2693 | self.fail("Expected identifier, " |
2679 | 2694 | "got keyword: %s" % self.matched_text) |
2680 | | - identifier = ASTIdentifier(self.matched_text) |
| 2695 | + identifier = ASTIdentifier(self.matched_text, '') |
2681 | 2696 | declId = ASTNestedName([identifier], rooted=False) |
2682 | 2697 | else: |
2683 | 2698 | declId = None |
@@ -2939,7 +2954,7 @@ def _parse_macro(self) -> ASTMacro: |
2939 | 2954 | break |
2940 | 2955 | if not self.match(identifier_re): |
2941 | 2956 | self.fail("Expected identifier in macro parameters.") |
2942 | | - nn = ASTNestedName([ASTIdentifier(self.matched_text)], rooted=False) |
| 2957 | + nn = ASTNestedName([ASTIdentifier(self.matched_text, '')], rooted=False) |
2943 | 2958 | # Allow named variadic args: |
2944 | 2959 | # https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html |
2945 | 2960 | self.skip_ws() |
@@ -3036,10 +3051,10 @@ def parse_declaration(self, objectType: str, directiveType: str) -> ASTDeclarati |
3036 | 3051 | return ASTDeclaration(objectType, directiveType, declaration, semicolon) |
3037 | 3052 |
|
3038 | 3053 | def parse_namespace_object(self) -> ASTNestedName: |
3039 | | - return self._parse_nested_name() |
| 3054 | + return self._parse_nested_name(allowTags='all') |
3040 | 3055 |
|
3041 | 3056 | def parse_xref_object(self) -> ASTNestedName: |
3042 | | - name = self._parse_nested_name() |
| 3057 | + name = self._parse_nested_name(allowTags='all') |
3043 | 3058 | # if there are '()' left, just skip them |
3044 | 3059 | self.skip_ws() |
3045 | 3060 | self.skip_string('()') |
@@ -3069,7 +3084,7 @@ def parse_expression(self) -> Union[ASTExpression, ASTType]: |
3069 | 3084 |
|
3070 | 3085 |
|
3071 | 3086 | def _make_phony_error_name() -> ASTNestedName: |
3072 | | - return ASTNestedName([ASTIdentifier("PhonyNameDueToError")], rooted=False) |
| 3087 | + return ASTNestedName([ASTIdentifier("PhonyNameDueToError", None)], rooted=False) |
3073 | 3088 |
|
3074 | 3089 |
|
3075 | 3090 | class CObject(ObjectDescription[ASTDeclaration]): |
@@ -3800,9 +3815,18 @@ def setup(app: Sphinx) -> Dict[str, Any]: |
3800 | 3815 | app.add_config_value("c_allow_pre_v3", False, 'env') |
3801 | 3816 | app.add_config_value("c_warn_on_allowed_pre_v3", True, 'env') |
3802 | 3817 |
|
| 3818 | + # debug stuff |
| 3819 | + app.add_config_value("c_debug_lookup", False, '') |
| 3820 | + app.add_config_value("c_debug_show_tree", False, '') |
| 3821 | + |
| 3822 | + def setDebugFlags(app): |
| 3823 | + Symbol.debug_lookup = app.config.c_debug_lookup |
| 3824 | + Symbol.debug_show_tree = app.config.c_debug_show_tree |
| 3825 | + app.connect("builder-inited", setDebugFlags) |
| 3826 | + |
3803 | 3827 | return { |
3804 | 3828 | 'version': 'builtin', |
3805 | | - 'env_version': 2, |
| 3829 | + 'env_version': 3, |
3806 | 3830 | 'parallel_read_safe': True, |
3807 | 3831 | 'parallel_write_safe': True, |
3808 | 3832 | } |
0 commit comments