python-delphi-lsp 3.6.0__cp310-abi3-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- delphi_lsp/__init__.py +184 -0
- delphi_lsp/_version.py +1 -0
- delphi_lsp/agent_cache.py +1574 -0
- delphi_lsp/agent_cli.py +902 -0
- delphi_lsp/agent_context.py +3907 -0
- delphi_lsp/agent_cpg.py +349 -0
- delphi_lsp/agent_cpg_builder.py +666 -0
- delphi_lsp/agent_layers.py +1410 -0
- delphi_lsp/agent_metrics.py +526 -0
- delphi_lsp/agent_protocol.py +445 -0
- delphi_lsp/agent_relations.py +1881 -0
- delphi_lsp/agent_templates.py +614 -0
- delphi_lsp/agent_wiki.py +2853 -0
- delphi_lsp/agent_workspace.py +860 -0
- delphi_lsp/ast_serialize.py +126 -0
- delphi_lsp/binary.py +250 -0
- delphi_lsp/comment_builder.py +28 -0
- delphi_lsp/consts.py +345 -0
- delphi_lsp/delphi_lsp_native.pyd +0 -0
- delphi_lsp/delphiast_lexer.py +399 -0
- delphi_lsp/delphiast_parser.py +2189 -0
- delphi_lsp/delphiast_tokens.py +41 -0
- delphi_lsp/grammar.py +459 -0
- delphi_lsp/incremental.py +1139 -0
- delphi_lsp/lark_builder.py +2684 -0
- delphi_lsp/lark_tokens.py +236 -0
- delphi_lsp/lexical_scanner.py +30 -0
- delphi_lsp/lsp_server.py +6698 -0
- delphi_lsp/metrics.py +1452 -0
- delphi_lsp/native_bridge.py +300 -0
- delphi_lsp/navigation_cache.py +189 -0
- delphi_lsp/nodes.py +462 -0
- delphi_lsp/parallel_outline.py +662 -0
- delphi_lsp/parse_artifacts.py +969 -0
- delphi_lsp/parser.py +342 -0
- delphi_lsp/parser_backend.py +139 -0
- delphi_lsp/preprocessor.py +1122 -0
- delphi_lsp/progress.py +26 -0
- delphi_lsp/project_config.py +285 -0
- delphi_lsp/project_discovery.py +932 -0
- delphi_lsp/project_indexer.py +470 -0
- delphi_lsp/py.typed +1 -0
- delphi_lsp/response_cache.py +465 -0
- delphi_lsp/semantic.py +394 -0
- delphi_lsp/semantic_builder.py +2302 -0
- delphi_lsp/source_reader.py +17 -0
- delphi_lsp/wiki_external_sort.py +392 -0
- delphi_lsp/wiki_memory.py +548 -0
- delphi_lsp/workspace.py +83 -0
- delphi_lsp/writer.py +73 -0
- python_delphi_lsp-3.6.0.dist-info/METADATA +766 -0
- python_delphi_lsp-3.6.0.dist-info/RECORD +56 -0
- python_delphi_lsp-3.6.0.dist-info/WHEEL +4 -0
- python_delphi_lsp-3.6.0.dist-info/entry_points.txt +3 -0
- python_delphi_lsp-3.6.0.dist-info/licenses/LICENSE +373 -0
- python_delphi_lsp-3.6.0.dist-info/sboms/python-delphi-lsp.cyclonedx.json +770 -0
|
@@ -0,0 +1,2302 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Iterable, Mapping
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
from .consts import ATTRIBUTE_NAME_STRINGS, AttributeName, SyntaxNodeType
|
|
8
|
+
from .delphiast_parser import _without_cyclic_gc
|
|
9
|
+
from .nodes import CompoundSyntaxNode, SyntaxNode, ValuedSyntaxNode
|
|
10
|
+
from .preprocessor import PreprocessedSource
|
|
11
|
+
from .semantic import (
|
|
12
|
+
ArrayTypeRef,
|
|
13
|
+
ClassOfTypeRef,
|
|
14
|
+
ClassTypeRef,
|
|
15
|
+
EnumTypeRef,
|
|
16
|
+
FileTypeRef,
|
|
17
|
+
GenericInstanceTypeRef,
|
|
18
|
+
InterfaceTypeRef,
|
|
19
|
+
Modifier,
|
|
20
|
+
NamedTypeRef,
|
|
21
|
+
PointerTypeRef,
|
|
22
|
+
ProcTypeRef,
|
|
23
|
+
RecordTypeRef,
|
|
24
|
+
ReferenceKind,
|
|
25
|
+
ReferenceTypeRef,
|
|
26
|
+
Scope,
|
|
27
|
+
ScopeKind,
|
|
28
|
+
SetTypeRef,
|
|
29
|
+
SourceRange,
|
|
30
|
+
SubrangeTypeRef,
|
|
31
|
+
Symbol,
|
|
32
|
+
SymbolIndex,
|
|
33
|
+
SymbolKind,
|
|
34
|
+
SymbolReference,
|
|
35
|
+
TypeParameterRef,
|
|
36
|
+
TypeRef,
|
|
37
|
+
UnknownTypeRef,
|
|
38
|
+
Visibility,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class SemanticProblem:
|
|
44
|
+
message: str
|
|
45
|
+
range: SourceRange
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class SemanticModel:
|
|
50
|
+
unit_scope: Scope
|
|
51
|
+
index: SymbolIndex
|
|
52
|
+
problems: list[SemanticProblem] = field(default_factory=list)
|
|
53
|
+
references: list[SymbolReference] = field(default_factory=list)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SemanticBuilder:
|
|
57
|
+
def __init__(
|
|
58
|
+
self,
|
|
59
|
+
*,
|
|
60
|
+
collect_references: bool = True,
|
|
61
|
+
source_maps: Mapping[str, PreprocessedSource] | None = None,
|
|
62
|
+
) -> None:
|
|
63
|
+
self._collect_references_enabled = collect_references
|
|
64
|
+
self._source_maps = source_maps or {}
|
|
65
|
+
self._problems: list[SemanticProblem] = []
|
|
66
|
+
self._references: list[SymbolReference] = []
|
|
67
|
+
self._index: SymbolIndex = SymbolIndex()
|
|
68
|
+
self._node_scopes: dict[int, Scope] = {}
|
|
69
|
+
self._builtin_types = {
|
|
70
|
+
'string', 'ansistring', 'widestring', 'unicodestring',
|
|
71
|
+
'char', 'widechar',
|
|
72
|
+
'integer', 'smallint', 'shortint', 'longint', 'int64', 'uint64',
|
|
73
|
+
'byte', 'word', 'cardinal', 'nativeint', 'nativeuint',
|
|
74
|
+
'boolean', 'bytebool', 'wordbool', 'longbool',
|
|
75
|
+
'single', 'double', 'extended', 'currency', 'real', 'real48',
|
|
76
|
+
'variant', 'olevariant', 'pointer', 'pchar', 'tobject', 'tclass',
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@_without_cyclic_gc
|
|
80
|
+
def build(self, root: SyntaxNode, *, index: SymbolIndex | None = None) -> SemanticModel:
|
|
81
|
+
self._reset_state(index=index, clear_scopes=True)
|
|
82
|
+
unit_scope = self.declare(root, index=self._index)
|
|
83
|
+
return self.resolve(root, unit_scope)
|
|
84
|
+
|
|
85
|
+
def declare(
|
|
86
|
+
self,
|
|
87
|
+
root: SyntaxNode,
|
|
88
|
+
*,
|
|
89
|
+
index: SymbolIndex | None = None,
|
|
90
|
+
unit_scope: Scope | None = None,
|
|
91
|
+
reset_state: bool = False,
|
|
92
|
+
) -> Scope:
|
|
93
|
+
if reset_state:
|
|
94
|
+
self._reset_state(index=index, clear_scopes=True)
|
|
95
|
+
elif index is not None:
|
|
96
|
+
self._index = index
|
|
97
|
+
|
|
98
|
+
unit_name = self._attr(root, AttributeName.anName) or 'unit'
|
|
99
|
+
if unit_scope is None:
|
|
100
|
+
unit_scope = Scope(kind=ScopeKind.UNIT, name=unit_name)
|
|
101
|
+
if unit_scope.owner is None:
|
|
102
|
+
unit_symbol = Symbol(
|
|
103
|
+
name=unit_name,
|
|
104
|
+
kind=SymbolKind.UNIT,
|
|
105
|
+
decl_range=self._node_range(root),
|
|
106
|
+
name_range=self._node_range(root),
|
|
107
|
+
scope=unit_scope,
|
|
108
|
+
)
|
|
109
|
+
unit_scope.owner = unit_symbol
|
|
110
|
+
self._define_symbol(unit_scope, unit_symbol)
|
|
111
|
+
|
|
112
|
+
self._declare_symbols(root, unit_scope, Visibility.PUBLIC)
|
|
113
|
+
self._index.register_unit(unit_name, unit_scope)
|
|
114
|
+
return unit_scope
|
|
115
|
+
|
|
116
|
+
def resolve(self, root: SyntaxNode, unit_scope: Scope) -> SemanticModel:
|
|
117
|
+
self._references = []
|
|
118
|
+
self._resolve_type_refs(unit_scope)
|
|
119
|
+
|
|
120
|
+
if self._collect_references_enabled:
|
|
121
|
+
self._collect_references_pass(root, unit_scope, Visibility.PUBLIC)
|
|
122
|
+
|
|
123
|
+
return SemanticModel(
|
|
124
|
+
unit_scope=unit_scope,
|
|
125
|
+
index=self._index,
|
|
126
|
+
problems=list(self._problems),
|
|
127
|
+
references=list(self._references),
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def _reset_state(self, *, index: SymbolIndex | None, clear_scopes: bool) -> None:
|
|
131
|
+
self._problems = []
|
|
132
|
+
self._references = []
|
|
133
|
+
if clear_scopes:
|
|
134
|
+
self._node_scopes = {}
|
|
135
|
+
if index is not None:
|
|
136
|
+
self._index = index
|
|
137
|
+
elif self._index is None:
|
|
138
|
+
self._index = SymbolIndex()
|
|
139
|
+
|
|
140
|
+
def _declare_symbols(self, node: SyntaxNode, scope: Scope, visibility: Visibility) -> None:
|
|
141
|
+
pending = [(node, scope, visibility)]
|
|
142
|
+
while pending:
|
|
143
|
+
current, current_scope, current_visibility = pending.pop()
|
|
144
|
+
if current.typ == SyntaxNodeType.ntUses:
|
|
145
|
+
self._handle_uses(current, current_scope)
|
|
146
|
+
continue
|
|
147
|
+
if current.typ == SyntaxNodeType.ntTypeSection:
|
|
148
|
+
self._handle_type_section(current, current_scope)
|
|
149
|
+
continue
|
|
150
|
+
if current.typ == SyntaxNodeType.ntVariables:
|
|
151
|
+
self._handle_variable_section(
|
|
152
|
+
current,
|
|
153
|
+
current_scope,
|
|
154
|
+
current_visibility,
|
|
155
|
+
)
|
|
156
|
+
continue
|
|
157
|
+
if current.typ == SyntaxNodeType.ntVariable:
|
|
158
|
+
self._handle_inline_variable(
|
|
159
|
+
current,
|
|
160
|
+
current_scope,
|
|
161
|
+
current_visibility,
|
|
162
|
+
)
|
|
163
|
+
continue
|
|
164
|
+
if current.typ == SyntaxNodeType.ntConstants:
|
|
165
|
+
self._handle_constant_section(current, current_scope)
|
|
166
|
+
continue
|
|
167
|
+
if current.typ == SyntaxNodeType.ntMethod:
|
|
168
|
+
routine_scope = self._handle_method(
|
|
169
|
+
current,
|
|
170
|
+
current_scope,
|
|
171
|
+
current_visibility,
|
|
172
|
+
)
|
|
173
|
+
if routine_scope is not None:
|
|
174
|
+
excluded = {
|
|
175
|
+
SyntaxNodeType.ntParameters,
|
|
176
|
+
SyntaxNodeType.ntReturnType,
|
|
177
|
+
SyntaxNodeType.ntTypeParams,
|
|
178
|
+
}
|
|
179
|
+
pending.extend(
|
|
180
|
+
(child, routine_scope, current_visibility)
|
|
181
|
+
for child in reversed(current.child_nodes)
|
|
182
|
+
if child.typ not in excluded
|
|
183
|
+
)
|
|
184
|
+
continue
|
|
185
|
+
if current.typ == SyntaxNodeType.ntProperty:
|
|
186
|
+
self._handle_property(
|
|
187
|
+
current,
|
|
188
|
+
current_scope,
|
|
189
|
+
current_visibility,
|
|
190
|
+
)
|
|
191
|
+
continue
|
|
192
|
+
if current.typ == SyntaxNodeType.ntField:
|
|
193
|
+
self._handle_field(
|
|
194
|
+
current,
|
|
195
|
+
current_scope,
|
|
196
|
+
current_visibility,
|
|
197
|
+
)
|
|
198
|
+
continue
|
|
199
|
+
if current.typ == SyntaxNodeType.ntLabel:
|
|
200
|
+
self._handle_label(current, current_scope)
|
|
201
|
+
continue
|
|
202
|
+
|
|
203
|
+
pending.extend(
|
|
204
|
+
(child, current_scope, current_visibility)
|
|
205
|
+
for child in reversed(current.child_nodes)
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def _handle_uses(self, node: SyntaxNode, scope: Scope) -> None:
|
|
209
|
+
for child in node.child_nodes:
|
|
210
|
+
if child.typ != SyntaxNodeType.ntUnit:
|
|
211
|
+
continue
|
|
212
|
+
name = self._attr(child, AttributeName.anName)
|
|
213
|
+
if not name:
|
|
214
|
+
continue
|
|
215
|
+
imported_scope = self._index.lookup_unit(name)
|
|
216
|
+
if imported_scope is None:
|
|
217
|
+
imported_scope = Scope(kind=ScopeKind.UNIT, name=name)
|
|
218
|
+
self._add_problem(f'Unresolved unit {name}', child)
|
|
219
|
+
scope.add_import(imported_scope)
|
|
220
|
+
|
|
221
|
+
def _handle_type_section(self, node: SyntaxNode, scope: Scope) -> None:
|
|
222
|
+
for child in node.child_nodes:
|
|
223
|
+
if child.typ != SyntaxNodeType.ntTypeDecl:
|
|
224
|
+
continue
|
|
225
|
+
self._handle_type_decl(child, scope)
|
|
226
|
+
|
|
227
|
+
def _handle_type_decl(self, node: SyntaxNode, scope: Scope) -> None:
|
|
228
|
+
name = self._attr(node, AttributeName.anName)
|
|
229
|
+
if not name:
|
|
230
|
+
return
|
|
231
|
+
type_node = node.find_node(SyntaxNodeType.ntType)
|
|
232
|
+
type_ref, kind = self._infer_type_ref_and_kind(name, type_node)
|
|
233
|
+
type_symbol = Symbol(
|
|
234
|
+
name=name,
|
|
235
|
+
kind=kind,
|
|
236
|
+
decl_range=self._node_range(node),
|
|
237
|
+
name_range=self._node_name_range(node, name),
|
|
238
|
+
scope=scope,
|
|
239
|
+
type_ref=type_ref,
|
|
240
|
+
)
|
|
241
|
+
type_scope = Scope(kind=ScopeKind.TYPE, name=name, parent=scope, owner=type_symbol)
|
|
242
|
+
type_symbol.member_scope = type_scope
|
|
243
|
+
type_symbol.base_types = self._type_bases(type_node)
|
|
244
|
+
self._define_symbol(scope, type_symbol)
|
|
245
|
+
self._node_scopes[id(node)] = type_scope
|
|
246
|
+
|
|
247
|
+
type_params = node.find_node(SyntaxNodeType.ntTypeParams)
|
|
248
|
+
if type_params is not None:
|
|
249
|
+
self._define_type_parameters(type_params, type_scope)
|
|
250
|
+
|
|
251
|
+
if kind == SymbolKind.ENUM and type_node is not None:
|
|
252
|
+
self._define_enum_values(type_node, scope, type_symbol)
|
|
253
|
+
|
|
254
|
+
if type_node is not None:
|
|
255
|
+
self._visit_type_members(type_node, type_scope)
|
|
256
|
+
|
|
257
|
+
def _define_enum_values(self, type_node: SyntaxNode, scope: Scope, type_symbol: Symbol) -> None:
|
|
258
|
+
for child in type_node.child_nodes:
|
|
259
|
+
if child.typ != SyntaxNodeType.ntEnum:
|
|
260
|
+
continue
|
|
261
|
+
name = self._child_name(child)
|
|
262
|
+
if not name:
|
|
263
|
+
continue
|
|
264
|
+
symbol = Symbol(
|
|
265
|
+
name=name,
|
|
266
|
+
kind=SymbolKind.ENUM_VALUE,
|
|
267
|
+
decl_range=self._node_range(child),
|
|
268
|
+
name_range=self._node_range(child),
|
|
269
|
+
scope=scope,
|
|
270
|
+
type_ref=NamedTypeRef(name=type_symbol.name),
|
|
271
|
+
)
|
|
272
|
+
self._define_symbol(scope, symbol)
|
|
273
|
+
|
|
274
|
+
def _visit_type_members(self, node: SyntaxNode, scope: Scope) -> None:
|
|
275
|
+
visibility = Visibility.PUBLIC
|
|
276
|
+
visibility_map = self._visibility_map()
|
|
277
|
+
for child in node.child_nodes:
|
|
278
|
+
if child.typ in visibility_map:
|
|
279
|
+
visibility = visibility_map[child.typ]
|
|
280
|
+
for member in child.child_nodes:
|
|
281
|
+
self._declare_symbols(member, scope, visibility)
|
|
282
|
+
continue
|
|
283
|
+
self._declare_symbols(child, scope, visibility)
|
|
284
|
+
|
|
285
|
+
def _handle_variable_section(self, node: SyntaxNode, scope: Scope, visibility: Visibility) -> None:
|
|
286
|
+
section_kind = self._attr(node, AttributeName.anKind)
|
|
287
|
+
for child in node.child_nodes:
|
|
288
|
+
if child.typ != SyntaxNodeType.ntVariable:
|
|
289
|
+
continue
|
|
290
|
+
name = self._child_name(child)
|
|
291
|
+
if not name:
|
|
292
|
+
continue
|
|
293
|
+
type_ref = self._type_from_node(child.find_node(SyntaxNodeType.ntType))
|
|
294
|
+
symbol = Symbol(
|
|
295
|
+
name=name,
|
|
296
|
+
kind=SymbolKind.VARIABLE,
|
|
297
|
+
decl_range=self._node_range(child),
|
|
298
|
+
name_range=self._node_range(child),
|
|
299
|
+
scope=scope,
|
|
300
|
+
visibility=visibility,
|
|
301
|
+
type_ref=type_ref,
|
|
302
|
+
)
|
|
303
|
+
if section_kind:
|
|
304
|
+
symbol.attributes['section'] = section_kind
|
|
305
|
+
self._bind_anonymous_structured_type(child, symbol)
|
|
306
|
+
self._define_symbol(scope, symbol)
|
|
307
|
+
|
|
308
|
+
def _handle_constant_section(self, node: SyntaxNode, scope: Scope) -> None:
|
|
309
|
+
section_kind = self._attr(node, AttributeName.anKind)
|
|
310
|
+
for child in node.child_nodes:
|
|
311
|
+
if child.typ != SyntaxNodeType.ntConstant:
|
|
312
|
+
continue
|
|
313
|
+
name = self._child_name(child)
|
|
314
|
+
if not name:
|
|
315
|
+
continue
|
|
316
|
+
type_ref = self._type_from_node(child.find_node(SyntaxNodeType.ntType))
|
|
317
|
+
symbol = Symbol(
|
|
318
|
+
name=name,
|
|
319
|
+
kind=SymbolKind.CONSTANT,
|
|
320
|
+
decl_range=self._node_range(child),
|
|
321
|
+
name_range=self._node_range(child),
|
|
322
|
+
scope=scope,
|
|
323
|
+
type_ref=type_ref,
|
|
324
|
+
)
|
|
325
|
+
if section_kind:
|
|
326
|
+
symbol.attributes['section'] = section_kind
|
|
327
|
+
self._define_symbol(scope, symbol)
|
|
328
|
+
|
|
329
|
+
def _handle_field(self, node: SyntaxNode, scope: Scope, visibility: Visibility) -> None:
|
|
330
|
+
name = self._child_name(node)
|
|
331
|
+
if not name:
|
|
332
|
+
return
|
|
333
|
+
type_ref = self._type_from_node(node.find_node(SyntaxNodeType.ntType))
|
|
334
|
+
symbol = Symbol(
|
|
335
|
+
name=name,
|
|
336
|
+
kind=SymbolKind.FIELD,
|
|
337
|
+
decl_range=self._node_range(node),
|
|
338
|
+
name_range=self._node_range(node),
|
|
339
|
+
scope=scope,
|
|
340
|
+
visibility=visibility,
|
|
341
|
+
type_ref=type_ref,
|
|
342
|
+
)
|
|
343
|
+
self._bind_anonymous_structured_type(node, symbol)
|
|
344
|
+
self._define_symbol(scope, symbol)
|
|
345
|
+
|
|
346
|
+
def _handle_inline_variable(self, node: SyntaxNode, scope: Scope, visibility: Visibility) -> None:
|
|
347
|
+
name = self._child_name(node)
|
|
348
|
+
if not name:
|
|
349
|
+
return
|
|
350
|
+
type_ref = self._type_from_node(node.find_node(SyntaxNodeType.ntType))
|
|
351
|
+
if isinstance(type_ref, UnknownTypeRef):
|
|
352
|
+
type_ref = UnknownTypeRef('inferred')
|
|
353
|
+
symbol = Symbol(
|
|
354
|
+
name=name,
|
|
355
|
+
kind=SymbolKind.VARIABLE,
|
|
356
|
+
decl_range=self._node_range(node),
|
|
357
|
+
name_range=self._node_range(node),
|
|
358
|
+
scope=scope,
|
|
359
|
+
visibility=visibility,
|
|
360
|
+
type_ref=type_ref,
|
|
361
|
+
)
|
|
362
|
+
symbol.attributes['section'] = 'inline'
|
|
363
|
+
self._bind_anonymous_structured_type(node, symbol)
|
|
364
|
+
self._define_symbol(scope, symbol)
|
|
365
|
+
|
|
366
|
+
def _bind_anonymous_structured_type(
|
|
367
|
+
self,
|
|
368
|
+
declaration: SyntaxNode,
|
|
369
|
+
symbol: Symbol,
|
|
370
|
+
) -> None:
|
|
371
|
+
type_node = declaration.find_node(SyntaxNodeType.ntType)
|
|
372
|
+
structured = self._anonymous_structured_type_node(type_node)
|
|
373
|
+
if structured is None:
|
|
374
|
+
return
|
|
375
|
+
member_scope = Scope(
|
|
376
|
+
kind=ScopeKind.TYPE,
|
|
377
|
+
name=symbol.name,
|
|
378
|
+
parent=symbol.scope,
|
|
379
|
+
owner=symbol,
|
|
380
|
+
)
|
|
381
|
+
symbol.member_scope = member_scope
|
|
382
|
+
self._node_scopes[id(declaration)] = member_scope
|
|
383
|
+
self._node_scopes[id(structured)] = member_scope
|
|
384
|
+
type_tag = self._attr(structured, AttributeName.anType)
|
|
385
|
+
packed = self._type_wrapper_contains(type_node, {'packed', 'bitpacked'})
|
|
386
|
+
if type_tag == 'record':
|
|
387
|
+
replacement: TypeRef = RecordTypeRef(
|
|
388
|
+
name='',
|
|
389
|
+
scope=member_scope,
|
|
390
|
+
packed=packed,
|
|
391
|
+
)
|
|
392
|
+
elif type_tag == 'class':
|
|
393
|
+
replacement = ClassTypeRef(
|
|
394
|
+
name='',
|
|
395
|
+
scope=member_scope,
|
|
396
|
+
bases=self._type_bases(structured),
|
|
397
|
+
)
|
|
398
|
+
else:
|
|
399
|
+
replacement = InterfaceTypeRef(
|
|
400
|
+
name='',
|
|
401
|
+
scope=member_scope,
|
|
402
|
+
bases=self._type_bases(structured),
|
|
403
|
+
)
|
|
404
|
+
symbol.type_ref = self._replace_unknown_type_ref(
|
|
405
|
+
symbol.type_ref,
|
|
406
|
+
replacement,
|
|
407
|
+
)
|
|
408
|
+
self._visit_type_members(structured, member_scope)
|
|
409
|
+
|
|
410
|
+
def _anonymous_structured_type_node(
|
|
411
|
+
self,
|
|
412
|
+
node: SyntaxNode | None,
|
|
413
|
+
) -> SyntaxNode | None:
|
|
414
|
+
if node is None:
|
|
415
|
+
return None
|
|
416
|
+
if self._attr(node, AttributeName.anType) in {
|
|
417
|
+
'record',
|
|
418
|
+
'class',
|
|
419
|
+
'interface',
|
|
420
|
+
'dispinterface',
|
|
421
|
+
}:
|
|
422
|
+
return node
|
|
423
|
+
for child in node.child_nodes:
|
|
424
|
+
if child.typ != SyntaxNodeType.ntType:
|
|
425
|
+
continue
|
|
426
|
+
structured = self._anonymous_structured_type_node(child)
|
|
427
|
+
if structured is not None:
|
|
428
|
+
return structured
|
|
429
|
+
return None
|
|
430
|
+
|
|
431
|
+
def _type_wrapper_contains(
|
|
432
|
+
self,
|
|
433
|
+
node: SyntaxNode | None,
|
|
434
|
+
wrappers: set[str],
|
|
435
|
+
) -> bool:
|
|
436
|
+
current = node
|
|
437
|
+
while current is not None:
|
|
438
|
+
if self._attr(current, AttributeName.anType) in wrappers:
|
|
439
|
+
return True
|
|
440
|
+
current = next(
|
|
441
|
+
(
|
|
442
|
+
child
|
|
443
|
+
for child in current.child_nodes
|
|
444
|
+
if child.typ == SyntaxNodeType.ntType
|
|
445
|
+
),
|
|
446
|
+
None,
|
|
447
|
+
)
|
|
448
|
+
return False
|
|
449
|
+
|
|
450
|
+
def _replace_unknown_type_ref(
|
|
451
|
+
self,
|
|
452
|
+
type_ref: TypeRef,
|
|
453
|
+
replacement: TypeRef,
|
|
454
|
+
) -> TypeRef:
|
|
455
|
+
if isinstance(type_ref, UnknownTypeRef):
|
|
456
|
+
return replacement
|
|
457
|
+
if isinstance(type_ref, ArrayTypeRef):
|
|
458
|
+
return ArrayTypeRef(
|
|
459
|
+
element_type=self._replace_unknown_type_ref(
|
|
460
|
+
type_ref.element_type,
|
|
461
|
+
replacement,
|
|
462
|
+
),
|
|
463
|
+
dimensions=type_ref.dimensions,
|
|
464
|
+
is_dynamic=type_ref.is_dynamic,
|
|
465
|
+
)
|
|
466
|
+
if isinstance(type_ref, PointerTypeRef):
|
|
467
|
+
return PointerTypeRef(
|
|
468
|
+
self._replace_unknown_type_ref(type_ref.target, replacement)
|
|
469
|
+
)
|
|
470
|
+
if isinstance(type_ref, SetTypeRef):
|
|
471
|
+
return SetTypeRef(
|
|
472
|
+
self._replace_unknown_type_ref(
|
|
473
|
+
type_ref.element_type,
|
|
474
|
+
replacement,
|
|
475
|
+
)
|
|
476
|
+
)
|
|
477
|
+
if isinstance(type_ref, ClassOfTypeRef):
|
|
478
|
+
return ClassOfTypeRef(
|
|
479
|
+
self._replace_unknown_type_ref(type_ref.target, replacement)
|
|
480
|
+
)
|
|
481
|
+
if isinstance(type_ref, ReferenceTypeRef):
|
|
482
|
+
return ReferenceTypeRef(
|
|
483
|
+
self._replace_unknown_type_ref(type_ref.target, replacement)
|
|
484
|
+
)
|
|
485
|
+
if isinstance(type_ref, FileTypeRef) and type_ref.element_type is not None:
|
|
486
|
+
return FileTypeRef(
|
|
487
|
+
self._replace_unknown_type_ref(
|
|
488
|
+
type_ref.element_type,
|
|
489
|
+
replacement,
|
|
490
|
+
)
|
|
491
|
+
)
|
|
492
|
+
return type_ref
|
|
493
|
+
|
|
494
|
+
def _handle_label(self, node: SyntaxNode, scope: Scope) -> None:
|
|
495
|
+
name = self._attr(node, AttributeName.anName)
|
|
496
|
+
if not name and isinstance(node, ValuedSyntaxNode):
|
|
497
|
+
name = node.value
|
|
498
|
+
if not name:
|
|
499
|
+
return
|
|
500
|
+
symbol = Symbol(
|
|
501
|
+
name=name,
|
|
502
|
+
kind=SymbolKind.LABEL,
|
|
503
|
+
decl_range=self._node_range(node),
|
|
504
|
+
name_range=self._node_range(node),
|
|
505
|
+
scope=scope,
|
|
506
|
+
)
|
|
507
|
+
self._define_symbol(scope, symbol)
|
|
508
|
+
|
|
509
|
+
def _handle_method(
|
|
510
|
+
self,
|
|
511
|
+
node: SyntaxNode,
|
|
512
|
+
scope: Scope,
|
|
513
|
+
visibility: Visibility,
|
|
514
|
+
) -> Scope | None:
|
|
515
|
+
name = self._attr(node, AttributeName.anName)
|
|
516
|
+
if not name:
|
|
517
|
+
return None
|
|
518
|
+
kind = self._method_kind(node)
|
|
519
|
+
type_ref = self._method_type_ref(node, name)
|
|
520
|
+
symbol = Symbol(
|
|
521
|
+
name=name,
|
|
522
|
+
kind=kind,
|
|
523
|
+
decl_range=self._node_tree_range(node),
|
|
524
|
+
name_range=self._node_range(node),
|
|
525
|
+
scope=scope,
|
|
526
|
+
visibility=visibility,
|
|
527
|
+
type_ref=type_ref,
|
|
528
|
+
modifiers=self._method_modifiers(node),
|
|
529
|
+
attributes=self._symbol_attributes(node),
|
|
530
|
+
)
|
|
531
|
+
routine_parent = scope
|
|
532
|
+
name_parts = self._normalized_reference_parts(name)
|
|
533
|
+
if len(name_parts) > 1 and scope.kind == ScopeKind.UNIT:
|
|
534
|
+
declaring_type = self._declaring_type_for_method(name_parts, scope)
|
|
535
|
+
if declaring_type is not None and declaring_type.member_scope is not None:
|
|
536
|
+
routine_parent = declaring_type.member_scope
|
|
537
|
+
routine_scope = Scope(
|
|
538
|
+
kind=ScopeKind.ROUTINE,
|
|
539
|
+
name=name,
|
|
540
|
+
parent=routine_parent,
|
|
541
|
+
owner=symbol,
|
|
542
|
+
)
|
|
543
|
+
symbol.member_scope = routine_scope
|
|
544
|
+
self._define_symbol(scope, symbol)
|
|
545
|
+
self._node_scopes[id(node)] = routine_scope
|
|
546
|
+
|
|
547
|
+
params = node.find_node(SyntaxNodeType.ntParameters)
|
|
548
|
+
if params is not None:
|
|
549
|
+
self._define_parameters(params, routine_scope)
|
|
550
|
+
|
|
551
|
+
type_params = node.find_node(SyntaxNodeType.ntTypeParams)
|
|
552
|
+
if type_params is not None:
|
|
553
|
+
self._define_type_parameters(type_params, routine_scope)
|
|
554
|
+
|
|
555
|
+
return routine_scope
|
|
556
|
+
|
|
557
|
+
def _declaring_type_for_method(
|
|
558
|
+
self,
|
|
559
|
+
name_parts: list[str],
|
|
560
|
+
scope: Scope,
|
|
561
|
+
) -> Symbol | None:
|
|
562
|
+
type_name = '.'.join(name_parts[:-1])
|
|
563
|
+
if len(name_parts) == 2:
|
|
564
|
+
candidates = scope.resolve(type_name)
|
|
565
|
+
else:
|
|
566
|
+
resolved = self._resolve_reference(
|
|
567
|
+
type_name,
|
|
568
|
+
ReferenceKind.TYPE,
|
|
569
|
+
scope,
|
|
570
|
+
)
|
|
571
|
+
candidates = [resolved] if resolved is not None else []
|
|
572
|
+
type_kinds = {
|
|
573
|
+
SymbolKind.CLASS,
|
|
574
|
+
SymbolKind.INTERFACE,
|
|
575
|
+
SymbolKind.RECORD,
|
|
576
|
+
SymbolKind.TYPE,
|
|
577
|
+
}
|
|
578
|
+
candidates = [
|
|
579
|
+
candidate
|
|
580
|
+
for candidate in candidates
|
|
581
|
+
if candidate.kind in type_kinds and candidate.member_scope is not None
|
|
582
|
+
]
|
|
583
|
+
member_name = name_parts[-1]
|
|
584
|
+
member_matches = [
|
|
585
|
+
candidate
|
|
586
|
+
for candidate in candidates
|
|
587
|
+
if candidate.member_scope is not None
|
|
588
|
+
and candidate.member_scope.lookup_local(member_name)
|
|
589
|
+
]
|
|
590
|
+
if len(member_matches) == 1:
|
|
591
|
+
return member_matches[0]
|
|
592
|
+
populated = [
|
|
593
|
+
candidate
|
|
594
|
+
for candidate in candidates
|
|
595
|
+
if candidate.member_scope is not None and candidate.member_scope.symbols
|
|
596
|
+
]
|
|
597
|
+
if len(populated) == 1:
|
|
598
|
+
return populated[0]
|
|
599
|
+
return candidates[0] if len(candidates) == 1 else None
|
|
600
|
+
|
|
601
|
+
def _handle_property(self, node: SyntaxNode, scope: Scope, visibility: Visibility) -> None:
|
|
602
|
+
name = self._attr(node, AttributeName.anName)
|
|
603
|
+
if not name:
|
|
604
|
+
return
|
|
605
|
+
type_ref = self._type_from_node(node.find_node(SyntaxNodeType.ntType))
|
|
606
|
+
symbol = Symbol(
|
|
607
|
+
name=name,
|
|
608
|
+
kind=SymbolKind.PROPERTY,
|
|
609
|
+
decl_range=self._node_range(node),
|
|
610
|
+
name_range=self._node_range(node),
|
|
611
|
+
scope=scope,
|
|
612
|
+
visibility=visibility,
|
|
613
|
+
type_ref=type_ref,
|
|
614
|
+
attributes=self._symbol_attributes(node),
|
|
615
|
+
)
|
|
616
|
+
self._define_symbol(scope, symbol)
|
|
617
|
+
|
|
618
|
+
def _define_parameters(self, node: SyntaxNode, scope: Scope) -> None:
|
|
619
|
+
for child in node.child_nodes:
|
|
620
|
+
if child.typ != SyntaxNodeType.ntParameter:
|
|
621
|
+
continue
|
|
622
|
+
name = self._child_name(child)
|
|
623
|
+
if not name:
|
|
624
|
+
continue
|
|
625
|
+
type_ref = self._type_from_node(child.find_node(SyntaxNodeType.ntType))
|
|
626
|
+
symbol = Symbol(
|
|
627
|
+
name=name,
|
|
628
|
+
kind=SymbolKind.PARAMETER,
|
|
629
|
+
decl_range=self._node_range(child),
|
|
630
|
+
name_range=self._node_range(child),
|
|
631
|
+
scope=scope,
|
|
632
|
+
type_ref=type_ref,
|
|
633
|
+
)
|
|
634
|
+
kind = self._attr(child, AttributeName.anKind)
|
|
635
|
+
if kind:
|
|
636
|
+
symbol.attributes['modifier'] = kind
|
|
637
|
+
self._define_symbol(scope, symbol)
|
|
638
|
+
|
|
639
|
+
def _define_type_parameters(self, node: SyntaxNode, scope: Scope) -> None:
|
|
640
|
+
for child in node.child_nodes:
|
|
641
|
+
if child.typ != SyntaxNodeType.ntTypeParam:
|
|
642
|
+
continue
|
|
643
|
+
name = self._child_name(child)
|
|
644
|
+
if not name:
|
|
645
|
+
continue
|
|
646
|
+
symbol = Symbol(
|
|
647
|
+
name=name,
|
|
648
|
+
kind=SymbolKind.TYPE_PARAMETER,
|
|
649
|
+
decl_range=self._node_range(child),
|
|
650
|
+
name_range=self._node_range(child),
|
|
651
|
+
scope=scope,
|
|
652
|
+
type_ref=TypeParameterRef(name=name),
|
|
653
|
+
)
|
|
654
|
+
constraints = self._constraint_names(child)
|
|
655
|
+
if constraints:
|
|
656
|
+
symbol.attributes['constraints'] = ', '.join(constraints)
|
|
657
|
+
self._define_symbol(scope, symbol)
|
|
658
|
+
|
|
659
|
+
def _method_kind(self, node: SyntaxNode) -> SymbolKind:
|
|
660
|
+
kind = self._attr(node, AttributeName.anKind)
|
|
661
|
+
if kind == 'function':
|
|
662
|
+
return SymbolKind.FUNCTION
|
|
663
|
+
if kind == 'procedure':
|
|
664
|
+
return SymbolKind.PROCEDURE
|
|
665
|
+
if kind == 'constructor':
|
|
666
|
+
return SymbolKind.CONSTRUCTOR
|
|
667
|
+
if kind == 'destructor':
|
|
668
|
+
return SymbolKind.DESTRUCTOR
|
|
669
|
+
return SymbolKind.METHOD
|
|
670
|
+
|
|
671
|
+
def _method_modifiers(self, node: SyntaxNode) -> set[Modifier]:
|
|
672
|
+
modifiers: set[Modifier] = set()
|
|
673
|
+
if self._attr(node, AttributeName.anClass) == 'true':
|
|
674
|
+
modifiers.add(Modifier.CLASS)
|
|
675
|
+
binding = self._attr(node, AttributeName.anMethodBinding)
|
|
676
|
+
if binding in {'virtual', 'dynamic'}:
|
|
677
|
+
modifiers.add(Modifier.VIRTUAL)
|
|
678
|
+
if binding == 'override':
|
|
679
|
+
modifiers.add(Modifier.OVERRIDE)
|
|
680
|
+
if self._attr(node, AttributeName.anReintroduce) == 'true':
|
|
681
|
+
modifiers.add(Modifier.REINTRODUCE)
|
|
682
|
+
if self._attr(node, AttributeName.anOverload) == 'true':
|
|
683
|
+
modifiers.add(Modifier.OVERLOAD)
|
|
684
|
+
if self._attr(node, AttributeName.anAbstract) == 'true':
|
|
685
|
+
modifiers.add(Modifier.ABSTRACT)
|
|
686
|
+
if self._attr(node, AttributeName.anInline) == 'true':
|
|
687
|
+
modifiers.add(Modifier.INLINE)
|
|
688
|
+
if self._attr(node, AttributeName.anExternal) == 'true':
|
|
689
|
+
modifiers.add(Modifier.EXTERNAL)
|
|
690
|
+
if self._attr(node, AttributeName.anForwarded) == 'true':
|
|
691
|
+
modifiers.add(Modifier.FORWARD)
|
|
692
|
+
if self._attr(node, AttributeName.anDeprecated):
|
|
693
|
+
modifiers.add(Modifier.DEPRECATED)
|
|
694
|
+
if self._attr(node, AttributeName.anNoReturn) == 'true':
|
|
695
|
+
modifiers.add(Modifier.NORETURN)
|
|
696
|
+
if self._attr(node, AttributeName.anStatic) == 'true':
|
|
697
|
+
modifiers.add(Modifier.STATIC)
|
|
698
|
+
if self._attr(node, AttributeName.anFinal) == 'true':
|
|
699
|
+
modifiers.add(Modifier.FINAL)
|
|
700
|
+
if self._attr(node, AttributeName.anSealed) == 'true':
|
|
701
|
+
modifiers.add(Modifier.SEALED)
|
|
702
|
+
return modifiers
|
|
703
|
+
|
|
704
|
+
def _method_type_ref(self, node: SyntaxNode, name: str) -> TypeRef:
|
|
705
|
+
params = node.find_node(SyntaxNodeType.ntParameters)
|
|
706
|
+
param_types = tuple(
|
|
707
|
+
self._type_from_node(p.find_node(SyntaxNodeType.ntType))
|
|
708
|
+
for p in params.child_nodes
|
|
709
|
+
if p.typ == SyntaxNodeType.ntParameter
|
|
710
|
+
) if params else ()
|
|
711
|
+
return_type_node = node.find_node(SyntaxNodeType.ntReturnType)
|
|
712
|
+
return_type = None
|
|
713
|
+
if return_type_node is not None:
|
|
714
|
+
return_type = self._type_from_node(return_type_node.find_node(SyntaxNodeType.ntType))
|
|
715
|
+
return ProcTypeRef(name=name, params=param_types, return_type=return_type)
|
|
716
|
+
|
|
717
|
+
def _infer_type_ref_and_kind(self, name: str, type_node: SyntaxNode | None) -> tuple[TypeRef, SymbolKind]:
|
|
718
|
+
if type_node is None:
|
|
719
|
+
return UnknownTypeRef('missing type'), SymbolKind.TYPE
|
|
720
|
+
while self._attr(type_node, AttributeName.anType) in {'packed', 'bitpacked'}:
|
|
721
|
+
nested_type = next(
|
|
722
|
+
(
|
|
723
|
+
child
|
|
724
|
+
for child in type_node.child_nodes
|
|
725
|
+
if child.typ == SyntaxNodeType.ntType
|
|
726
|
+
),
|
|
727
|
+
None,
|
|
728
|
+
)
|
|
729
|
+
if nested_type is None:
|
|
730
|
+
break
|
|
731
|
+
type_node = nested_type
|
|
732
|
+
type_tag = self._attr(type_node, AttributeName.anType)
|
|
733
|
+
name_tag = self._attr(type_node, AttributeName.anName)
|
|
734
|
+
if type_tag in {'class', 'objcclass'}:
|
|
735
|
+
return ClassTypeRef(name=name), SymbolKind.CLASS
|
|
736
|
+
if type_tag == 'record':
|
|
737
|
+
return RecordTypeRef(name=name), SymbolKind.RECORD
|
|
738
|
+
if type_tag in {'interface', 'dispinterface'}:
|
|
739
|
+
return InterfaceTypeRef(name=name), SymbolKind.INTERFACE
|
|
740
|
+
if name_tag == 'enum':
|
|
741
|
+
members = tuple(self._enum_members(type_node))
|
|
742
|
+
return EnumTypeRef(name=name, members=members), SymbolKind.ENUM
|
|
743
|
+
if type_tag == 'set':
|
|
744
|
+
elem = self._type_from_node(type_node.find_node(SyntaxNodeType.ntType))
|
|
745
|
+
return SetTypeRef(element_type=elem), SymbolKind.TYPE
|
|
746
|
+
if type_tag == 'array':
|
|
747
|
+
return self._type_from_node(type_node), SymbolKind.TYPE
|
|
748
|
+
if type_tag == 'distinct':
|
|
749
|
+
return self._type_from_node(type_node), SymbolKind.TYPE
|
|
750
|
+
if name_tag == 'subrange':
|
|
751
|
+
lower, upper = self._subrange_bounds(type_node)
|
|
752
|
+
return SubrangeTypeRef(lower=lower, upper=upper), SymbolKind.TYPE
|
|
753
|
+
if name_tag:
|
|
754
|
+
return NamedTypeRef(name=name_tag), SymbolKind.TYPE
|
|
755
|
+
return UnknownTypeRef('unresolved type'), SymbolKind.TYPE
|
|
756
|
+
|
|
757
|
+
def _type_bases(self, type_node: SyntaxNode | None) -> tuple[TypeRef, ...]:
|
|
758
|
+
if type_node is None:
|
|
759
|
+
return ()
|
|
760
|
+
bases = []
|
|
761
|
+
for child in type_node.child_nodes:
|
|
762
|
+
if child.typ == SyntaxNodeType.ntType:
|
|
763
|
+
bases.append(self._type_from_node(child))
|
|
764
|
+
return tuple(bases)
|
|
765
|
+
|
|
766
|
+
def _resolve_type_refs(self, scope: Scope) -> None:
|
|
767
|
+
pending = [scope]
|
|
768
|
+
while pending:
|
|
769
|
+
current = pending.pop()
|
|
770
|
+
member_scopes: list[Scope] = []
|
|
771
|
+
for symbols in current.symbols.values():
|
|
772
|
+
for symbol in symbols:
|
|
773
|
+
symbol.type_ref = self._resolve_type_ref(
|
|
774
|
+
symbol.type_ref,
|
|
775
|
+
current,
|
|
776
|
+
)
|
|
777
|
+
if symbol.member_scope is not None:
|
|
778
|
+
member_scopes.append(symbol.member_scope)
|
|
779
|
+
pending.extend(reversed(member_scopes))
|
|
780
|
+
|
|
781
|
+
def _resolve_type_ref(self, type_ref: TypeRef, scope: Scope) -> TypeRef:
|
|
782
|
+
if isinstance(type_ref, GenericInstanceTypeRef):
|
|
783
|
+
base = self._resolve_type_ref(type_ref.base, scope)
|
|
784
|
+
args = tuple(self._resolve_type_ref(arg, scope) for arg in type_ref.args)
|
|
785
|
+
if isinstance(base, NamedTypeRef):
|
|
786
|
+
return GenericInstanceTypeRef(base=base, args=args)
|
|
787
|
+
return GenericInstanceTypeRef(base=NamedTypeRef(name=type_ref.base.name), args=args)
|
|
788
|
+
if isinstance(type_ref, ArrayTypeRef):
|
|
789
|
+
elem = self._resolve_type_ref(type_ref.element_type, scope)
|
|
790
|
+
dims = tuple(self._resolve_type_ref(dim, scope) for dim in type_ref.dimensions)
|
|
791
|
+
return ArrayTypeRef(element_type=elem, dimensions=dims, is_dynamic=type_ref.is_dynamic)
|
|
792
|
+
if isinstance(type_ref, SetTypeRef):
|
|
793
|
+
elem = self._resolve_type_ref(type_ref.element_type, scope)
|
|
794
|
+
return SetTypeRef(element_type=elem)
|
|
795
|
+
if isinstance(type_ref, PointerTypeRef):
|
|
796
|
+
return PointerTypeRef(self._resolve_type_ref(type_ref.target, scope))
|
|
797
|
+
if isinstance(type_ref, ClassOfTypeRef):
|
|
798
|
+
return ClassOfTypeRef(self._resolve_type_ref(type_ref.target, scope))
|
|
799
|
+
if isinstance(type_ref, ReferenceTypeRef):
|
|
800
|
+
return ReferenceTypeRef(self._resolve_type_ref(type_ref.target, scope))
|
|
801
|
+
if isinstance(type_ref, FileTypeRef) and type_ref.element_type is not None:
|
|
802
|
+
return FileTypeRef(self._resolve_type_ref(type_ref.element_type, scope))
|
|
803
|
+
if isinstance(type_ref, ProcTypeRef):
|
|
804
|
+
return ProcTypeRef(
|
|
805
|
+
name=type_ref.name,
|
|
806
|
+
params=tuple(
|
|
807
|
+
self._resolve_type_ref(parameter, scope)
|
|
808
|
+
for parameter in type_ref.params
|
|
809
|
+
),
|
|
810
|
+
return_type=(
|
|
811
|
+
self._resolve_type_ref(type_ref.return_type, scope)
|
|
812
|
+
if type_ref.return_type is not None
|
|
813
|
+
else None
|
|
814
|
+
),
|
|
815
|
+
)
|
|
816
|
+
if isinstance(type_ref, NamedTypeRef):
|
|
817
|
+
return self._normalize_named_type(type_ref, scope)
|
|
818
|
+
return type_ref
|
|
819
|
+
|
|
820
|
+
def _normalize_named_type(self, type_ref: NamedTypeRef, scope: Scope) -> NamedTypeRef:
|
|
821
|
+
if type_ref.unit_name:
|
|
822
|
+
return type_ref
|
|
823
|
+
if '.' in type_ref.name:
|
|
824
|
+
unit_name, name = self._split_qualified_name(type_ref.name)
|
|
825
|
+
return NamedTypeRef(name=name, unit_name=unit_name)
|
|
826
|
+
resolved = scope.resolve(type_ref.name)
|
|
827
|
+
if resolved:
|
|
828
|
+
symbol = resolved[0]
|
|
829
|
+
unit_scope = self._unit_scope_for_symbol(symbol)
|
|
830
|
+
if unit_scope is not None and unit_scope.name != scope.name:
|
|
831
|
+
return NamedTypeRef(name=type_ref.name, unit_name=unit_scope.name)
|
|
832
|
+
return type_ref
|
|
833
|
+
|
|
834
|
+
def _type_from_node(self, node: SyntaxNode | None) -> TypeRef:
|
|
835
|
+
if node is None:
|
|
836
|
+
return UnknownTypeRef('missing type')
|
|
837
|
+
name = self._attr(node, AttributeName.anName)
|
|
838
|
+
type_tag = self._attr(node, AttributeName.anType)
|
|
839
|
+
if type_tag == 'pointer':
|
|
840
|
+
target = node.find_node(SyntaxNodeType.ntType)
|
|
841
|
+
return PointerTypeRef(self._type_from_node(target))
|
|
842
|
+
if type_tag == 'array':
|
|
843
|
+
element = self._type_from_node(node.find_node(SyntaxNodeType.ntType))
|
|
844
|
+
bounds_node = node.find_node(SyntaxNodeType.ntBounds)
|
|
845
|
+
if bounds_node is None or not bounds_node.child_nodes:
|
|
846
|
+
return ArrayTypeRef(element_type=element, is_dynamic=True)
|
|
847
|
+
dims = tuple(self._bound_type_ref(child) for child in bounds_node.child_nodes)
|
|
848
|
+
return ArrayTypeRef(element_type=element, dimensions=dims, is_dynamic=False)
|
|
849
|
+
if type_tag == 'set':
|
|
850
|
+
elem = self._type_from_node(node.find_node(SyntaxNodeType.ntType))
|
|
851
|
+
return SetTypeRef(element_type=elem)
|
|
852
|
+
if type_tag == 'class of':
|
|
853
|
+
target = node.find_node(SyntaxNodeType.ntType)
|
|
854
|
+
return ClassOfTypeRef(self._type_from_node(target))
|
|
855
|
+
if type_tag == 'reference':
|
|
856
|
+
target = node.find_node(SyntaxNodeType.ntType)
|
|
857
|
+
return ReferenceTypeRef(self._type_from_node(target))
|
|
858
|
+
if type_tag == 'file':
|
|
859
|
+
target = node.find_node(SyntaxNodeType.ntType)
|
|
860
|
+
if target is None:
|
|
861
|
+
return FileTypeRef(None)
|
|
862
|
+
return FileTypeRef(self._type_from_node(target))
|
|
863
|
+
if type_tag in {'packed', 'bitpacked'}:
|
|
864
|
+
target = node.find_node(SyntaxNodeType.ntType)
|
|
865
|
+
return self._type_from_node(target)
|
|
866
|
+
if type_tag == 'distinct':
|
|
867
|
+
target = node.find_node(SyntaxNodeType.ntType)
|
|
868
|
+
return self._type_from_node(target)
|
|
869
|
+
if type_tag in {'procedure', 'function'}:
|
|
870
|
+
return self._proc_type_from_node(node, type_tag)
|
|
871
|
+
if name == 'subrange':
|
|
872
|
+
lower, upper = self._subrange_bounds(node)
|
|
873
|
+
return SubrangeTypeRef(lower=lower, upper=upper)
|
|
874
|
+
|
|
875
|
+
type_args_node = node.find_node(SyntaxNodeType.ntTypeArgs)
|
|
876
|
+
if name:
|
|
877
|
+
base = self._type_ref_from_name(name)
|
|
878
|
+
if type_args_node is not None:
|
|
879
|
+
args = tuple(
|
|
880
|
+
self._type_from_node(child)
|
|
881
|
+
for child in type_args_node.child_nodes
|
|
882
|
+
if child.typ == SyntaxNodeType.ntType
|
|
883
|
+
)
|
|
884
|
+
if isinstance(base, NamedTypeRef):
|
|
885
|
+
return GenericInstanceTypeRef(base=base, args=args)
|
|
886
|
+
return base
|
|
887
|
+
return UnknownTypeRef('unresolved type')
|
|
888
|
+
|
|
889
|
+
def _type_ref_from_name(self, name: str) -> TypeRef:
|
|
890
|
+
generic = self._generic_name_parts(name)
|
|
891
|
+
if generic is None:
|
|
892
|
+
return self._named_type_ref(name)
|
|
893
|
+
base_name, arg_names = generic
|
|
894
|
+
base = self._named_type_ref(base_name)
|
|
895
|
+
args = tuple(self._type_ref_from_name(arg_name) for arg_name in arg_names)
|
|
896
|
+
if isinstance(base, NamedTypeRef):
|
|
897
|
+
return GenericInstanceTypeRef(base=base, args=args)
|
|
898
|
+
return self._named_type_ref(name)
|
|
899
|
+
|
|
900
|
+
def _generic_name_parts(self, name: str) -> tuple[str, tuple[str, ...]] | None:
|
|
901
|
+
text = name.strip()
|
|
902
|
+
open_index = text.find('<')
|
|
903
|
+
if open_index <= 0 or not text.endswith('>'):
|
|
904
|
+
return None
|
|
905
|
+
depth = 0
|
|
906
|
+
for index, ch in enumerate(text[open_index:], start=open_index):
|
|
907
|
+
if ch == '<':
|
|
908
|
+
depth += 1
|
|
909
|
+
elif ch == '>':
|
|
910
|
+
depth -= 1
|
|
911
|
+
if depth == 0 and index != len(text) - 1:
|
|
912
|
+
return None
|
|
913
|
+
if depth < 0:
|
|
914
|
+
return None
|
|
915
|
+
if depth != 0:
|
|
916
|
+
return None
|
|
917
|
+
base_name = text[:open_index].strip()
|
|
918
|
+
if not base_name:
|
|
919
|
+
return None
|
|
920
|
+
args = self._split_top_level_type_args(text[open_index + 1 : -1])
|
|
921
|
+
if not args:
|
|
922
|
+
return None
|
|
923
|
+
return base_name, tuple(args)
|
|
924
|
+
|
|
925
|
+
def _split_top_level_type_args(self, text: str) -> list[str]:
|
|
926
|
+
args: list[str] = []
|
|
927
|
+
current: list[str] = []
|
|
928
|
+
angle = 0
|
|
929
|
+
square = 0
|
|
930
|
+
round_ = 0
|
|
931
|
+
in_string = False
|
|
932
|
+
index = 0
|
|
933
|
+
while index < len(text):
|
|
934
|
+
ch = text[index]
|
|
935
|
+
if ch == "'":
|
|
936
|
+
current.append(ch)
|
|
937
|
+
if in_string and index + 1 < len(text) and text[index + 1] == "'":
|
|
938
|
+
current.append(text[index + 1])
|
|
939
|
+
index += 2
|
|
940
|
+
continue
|
|
941
|
+
in_string = not in_string
|
|
942
|
+
index += 1
|
|
943
|
+
continue
|
|
944
|
+
if not in_string:
|
|
945
|
+
if ch == '<':
|
|
946
|
+
angle += 1
|
|
947
|
+
elif ch == '>':
|
|
948
|
+
angle = max(0, angle - 1)
|
|
949
|
+
elif ch == '[':
|
|
950
|
+
square += 1
|
|
951
|
+
elif ch == ']':
|
|
952
|
+
square = max(0, square - 1)
|
|
953
|
+
elif ch == '(':
|
|
954
|
+
round_ += 1
|
|
955
|
+
elif ch == ')':
|
|
956
|
+
round_ = max(0, round_ - 1)
|
|
957
|
+
elif ch == ',' and angle == 0 and square == 0 and round_ == 0:
|
|
958
|
+
arg = ''.join(current).strip()
|
|
959
|
+
if arg:
|
|
960
|
+
args.append(arg)
|
|
961
|
+
current = []
|
|
962
|
+
index += 1
|
|
963
|
+
continue
|
|
964
|
+
current.append(ch)
|
|
965
|
+
index += 1
|
|
966
|
+
arg = ''.join(current).strip()
|
|
967
|
+
if arg:
|
|
968
|
+
args.append(arg)
|
|
969
|
+
return args
|
|
970
|
+
|
|
971
|
+
def _proc_type_from_node(self, node: SyntaxNode, kind: str) -> TypeRef:
|
|
972
|
+
params = node.find_node(SyntaxNodeType.ntParameters)
|
|
973
|
+
param_types = tuple(
|
|
974
|
+
self._type_from_node(p.find_node(SyntaxNodeType.ntType))
|
|
975
|
+
for p in params.child_nodes
|
|
976
|
+
if p.typ == SyntaxNodeType.ntParameter
|
|
977
|
+
) if params else ()
|
|
978
|
+
return_type = None
|
|
979
|
+
return_type_node = node.find_node(SyntaxNodeType.ntReturnType)
|
|
980
|
+
if return_type_node is not None:
|
|
981
|
+
return_type = self._type_from_node(return_type_node.find_node(SyntaxNodeType.ntType))
|
|
982
|
+
return ProcTypeRef(name=kind, params=param_types, return_type=return_type)
|
|
983
|
+
|
|
984
|
+
def _bound_type_ref(self, node: SyntaxNode) -> TypeRef:
|
|
985
|
+
if node.typ == SyntaxNodeType.ntSubrange:
|
|
986
|
+
lower, upper = self._subrange_bounds(node)
|
|
987
|
+
return SubrangeTypeRef(lower=lower, upper=upper)
|
|
988
|
+
text = self._expr_to_text(node)
|
|
989
|
+
if text:
|
|
990
|
+
return NamedTypeRef(name=text)
|
|
991
|
+
return UnknownTypeRef('bound')
|
|
992
|
+
|
|
993
|
+
def _subrange_bounds(self, node: SyntaxNode) -> tuple[str, str]:
|
|
994
|
+
bounds = node.find_node(SyntaxNodeType.ntBounds)
|
|
995
|
+
if bounds is None:
|
|
996
|
+
children = node.child_nodes
|
|
997
|
+
else:
|
|
998
|
+
children = bounds.child_nodes
|
|
999
|
+
values = [self._expr_to_text(child) for child in children if self._expr_to_text(child)]
|
|
1000
|
+
if len(values) >= 2:
|
|
1001
|
+
return (values[0], values[1])
|
|
1002
|
+
return ('', '')
|
|
1003
|
+
|
|
1004
|
+
def _enum_members(self, node: SyntaxNode) -> Iterable[str]:
|
|
1005
|
+
for child in node.child_nodes:
|
|
1006
|
+
if child.typ == SyntaxNodeType.ntEnum:
|
|
1007
|
+
name = self._child_name(child)
|
|
1008
|
+
if name:
|
|
1009
|
+
yield name
|
|
1010
|
+
|
|
1011
|
+
def _collect_references_pass(self, node: SyntaxNode, scope: Scope, visibility: Visibility) -> None:
|
|
1012
|
+
pending = [(node, scope, visibility)]
|
|
1013
|
+
while pending:
|
|
1014
|
+
current, current_scope, current_visibility = pending.pop()
|
|
1015
|
+
if current.typ in {
|
|
1016
|
+
SyntaxNodeType.ntCall,
|
|
1017
|
+
SyntaxNodeType.ntDot,
|
|
1018
|
+
SyntaxNodeType.ntIdentifier,
|
|
1019
|
+
}:
|
|
1020
|
+
self._collect_expr_references(current, current_scope)
|
|
1021
|
+
continue
|
|
1022
|
+
if current.typ == SyntaxNodeType.ntUses:
|
|
1023
|
+
self._collect_uses_references(current, current_scope)
|
|
1024
|
+
continue
|
|
1025
|
+
if current.typ == SyntaxNodeType.ntTypeSection:
|
|
1026
|
+
for child in current.child_nodes:
|
|
1027
|
+
if child.typ == SyntaxNodeType.ntTypeDecl:
|
|
1028
|
+
self._collect_type_decl_references(
|
|
1029
|
+
child,
|
|
1030
|
+
current_scope,
|
|
1031
|
+
)
|
|
1032
|
+
continue
|
|
1033
|
+
if current.typ == SyntaxNodeType.ntVariables:
|
|
1034
|
+
for child in current.child_nodes:
|
|
1035
|
+
if child.typ == SyntaxNodeType.ntVariable:
|
|
1036
|
+
self._collect_variable_references(
|
|
1037
|
+
child,
|
|
1038
|
+
current_scope,
|
|
1039
|
+
)
|
|
1040
|
+
continue
|
|
1041
|
+
if current.typ == SyntaxNodeType.ntVariable:
|
|
1042
|
+
self._collect_variable_references(current, current_scope)
|
|
1043
|
+
continue
|
|
1044
|
+
if current.typ == SyntaxNodeType.ntConstants:
|
|
1045
|
+
for child in current.child_nodes:
|
|
1046
|
+
if child.typ == SyntaxNodeType.ntConstant:
|
|
1047
|
+
self._collect_constant_references(
|
|
1048
|
+
child,
|
|
1049
|
+
current_scope,
|
|
1050
|
+
)
|
|
1051
|
+
continue
|
|
1052
|
+
if current.typ == SyntaxNodeType.ntMethod:
|
|
1053
|
+
method_scope, body = self._collect_method_references(
|
|
1054
|
+
current,
|
|
1055
|
+
current_scope,
|
|
1056
|
+
)
|
|
1057
|
+
pending.extend(
|
|
1058
|
+
(child, method_scope, current_visibility)
|
|
1059
|
+
for child in reversed(body)
|
|
1060
|
+
)
|
|
1061
|
+
continue
|
|
1062
|
+
if current.typ == SyntaxNodeType.ntResolutionClause:
|
|
1063
|
+
self._add_method_resolution_reference(current, current_scope)
|
|
1064
|
+
continue
|
|
1065
|
+
if current.typ == SyntaxNodeType.ntProperty:
|
|
1066
|
+
self._collect_property_references(current, current_scope)
|
|
1067
|
+
continue
|
|
1068
|
+
if current.typ == SyntaxNodeType.ntField:
|
|
1069
|
+
self._collect_field_references(current, current_scope)
|
|
1070
|
+
continue
|
|
1071
|
+
if current.typ == SyntaxNodeType.ntWith:
|
|
1072
|
+
with_scope, body = self._collect_with_references(
|
|
1073
|
+
current,
|
|
1074
|
+
current_scope,
|
|
1075
|
+
)
|
|
1076
|
+
pending.extend(
|
|
1077
|
+
(child, with_scope, current_visibility)
|
|
1078
|
+
for child in reversed(body)
|
|
1079
|
+
)
|
|
1080
|
+
continue
|
|
1081
|
+
if current.typ == SyntaxNodeType.ntGoto:
|
|
1082
|
+
label_name = self._attr(current, AttributeName.anName)
|
|
1083
|
+
self._add_reference(
|
|
1084
|
+
label_name,
|
|
1085
|
+
ReferenceKind.LABEL,
|
|
1086
|
+
current,
|
|
1087
|
+
current_scope,
|
|
1088
|
+
)
|
|
1089
|
+
continue
|
|
1090
|
+
|
|
1091
|
+
pending.extend(
|
|
1092
|
+
(child, current_scope, current_visibility)
|
|
1093
|
+
for child in reversed(current.child_nodes)
|
|
1094
|
+
)
|
|
1095
|
+
|
|
1096
|
+
def _collect_uses_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1097
|
+
for child in node.child_nodes:
|
|
1098
|
+
if child.typ != SyntaxNodeType.ntUnit:
|
|
1099
|
+
continue
|
|
1100
|
+
name = self._attr(child, AttributeName.anName)
|
|
1101
|
+
if name:
|
|
1102
|
+
self._add_reference(name, ReferenceKind.UNIT, child, scope)
|
|
1103
|
+
|
|
1104
|
+
def _collect_type_decl_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1105
|
+
type_node = node.find_node(SyntaxNodeType.ntType)
|
|
1106
|
+
type_scope = self._node_scopes.get(id(node), scope)
|
|
1107
|
+
type_params = node.find_node(SyntaxNodeType.ntTypeParams)
|
|
1108
|
+
if type_params is not None:
|
|
1109
|
+
self._collect_type_param_references(type_params, scope)
|
|
1110
|
+
if type_node is not None:
|
|
1111
|
+
type_name = self._attr(type_node, AttributeName.anName).strip()
|
|
1112
|
+
type_kind = self._attr(type_node, AttributeName.anType)
|
|
1113
|
+
direct_alias = (
|
|
1114
|
+
bool(type_name)
|
|
1115
|
+
and type_name.casefold() != 'enum'
|
|
1116
|
+
and '..' not in type_name
|
|
1117
|
+
)
|
|
1118
|
+
if type_kind in {'array', 'procedure', 'function'} or direct_alias:
|
|
1119
|
+
self._record_type_reference(type_node, type_scope)
|
|
1120
|
+
else:
|
|
1121
|
+
for child in type_node.child_nodes:
|
|
1122
|
+
if child.typ == SyntaxNodeType.ntType:
|
|
1123
|
+
self._record_type_reference(child, type_scope)
|
|
1124
|
+
self._collect_type_members_references(type_node, type_scope)
|
|
1125
|
+
|
|
1126
|
+
def _collect_type_members_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1127
|
+
visibility = Visibility.PUBLIC
|
|
1128
|
+
visibility_map = self._visibility_map()
|
|
1129
|
+
for child in node.child_nodes:
|
|
1130
|
+
if child.typ == SyntaxNodeType.ntBounds:
|
|
1131
|
+
continue
|
|
1132
|
+
if child.typ in visibility_map:
|
|
1133
|
+
visibility = visibility_map[child.typ]
|
|
1134
|
+
for member in child.child_nodes:
|
|
1135
|
+
self._collect_references_pass(member, scope, visibility)
|
|
1136
|
+
continue
|
|
1137
|
+
self._collect_references_pass(child, scope, visibility)
|
|
1138
|
+
|
|
1139
|
+
def _collect_variable_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1140
|
+
type_node = node.find_node(SyntaxNodeType.ntType)
|
|
1141
|
+
if type_node is not None:
|
|
1142
|
+
self._record_type_reference(type_node, scope)
|
|
1143
|
+
self._collect_anonymous_type_member_references(node, type_node)
|
|
1144
|
+
value_node = node.find_node(SyntaxNodeType.ntValue)
|
|
1145
|
+
if value_node is not None:
|
|
1146
|
+
self._collect_expr_references(value_node, scope)
|
|
1147
|
+
|
|
1148
|
+
def _collect_constant_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1149
|
+
type_node = node.find_node(SyntaxNodeType.ntType)
|
|
1150
|
+
if type_node is not None:
|
|
1151
|
+
self._record_type_reference(type_node, scope)
|
|
1152
|
+
value_node = node.find_node(SyntaxNodeType.ntValue)
|
|
1153
|
+
if value_node is not None:
|
|
1154
|
+
self._collect_expr_references(value_node, scope)
|
|
1155
|
+
|
|
1156
|
+
def _collect_field_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1157
|
+
type_node = node.find_node(SyntaxNodeType.ntType)
|
|
1158
|
+
if type_node is not None:
|
|
1159
|
+
self._record_type_reference(type_node, scope)
|
|
1160
|
+
self._collect_anonymous_type_member_references(node, type_node)
|
|
1161
|
+
|
|
1162
|
+
def _collect_anonymous_type_member_references(
|
|
1163
|
+
self,
|
|
1164
|
+
declaration: SyntaxNode,
|
|
1165
|
+
type_node: SyntaxNode,
|
|
1166
|
+
) -> None:
|
|
1167
|
+
structured = self._anonymous_structured_type_node(type_node)
|
|
1168
|
+
member_scope = self._node_scopes.get(id(declaration))
|
|
1169
|
+
if structured is None or member_scope is None:
|
|
1170
|
+
return
|
|
1171
|
+
self._collect_type_members_references(structured, member_scope)
|
|
1172
|
+
|
|
1173
|
+
def _collect_with_references(
|
|
1174
|
+
self,
|
|
1175
|
+
node: SyntaxNode,
|
|
1176
|
+
scope: Scope,
|
|
1177
|
+
) -> tuple[Scope, list[SyntaxNode]]:
|
|
1178
|
+
expr_nodes: list[SyntaxNode] = []
|
|
1179
|
+
body_nodes: list[SyntaxNode] = []
|
|
1180
|
+
for child in node.child_nodes:
|
|
1181
|
+
if child.typ == SyntaxNodeType.ntExpressions:
|
|
1182
|
+
expr_nodes.extend(child.child_nodes)
|
|
1183
|
+
else:
|
|
1184
|
+
body_nodes.append(child)
|
|
1185
|
+
|
|
1186
|
+
for expr in expr_nodes:
|
|
1187
|
+
self._collect_expr_references(expr, scope)
|
|
1188
|
+
|
|
1189
|
+
with_scopes: list[Scope] = []
|
|
1190
|
+
for expr in expr_nodes:
|
|
1191
|
+
with_scopes.extend(self._with_scopes_for_expr(expr, scope))
|
|
1192
|
+
|
|
1193
|
+
target_scope = scope
|
|
1194
|
+
if with_scopes:
|
|
1195
|
+
target_scope = Scope(kind=ScopeKind.WITH, name='with', parent=scope)
|
|
1196
|
+
for with_scope in with_scopes:
|
|
1197
|
+
target_scope.add_with_scope(with_scope)
|
|
1198
|
+
|
|
1199
|
+
return target_scope, body_nodes
|
|
1200
|
+
|
|
1201
|
+
def _with_scopes_for_expr(self, node: SyntaxNode, scope: Scope) -> list[Scope]:
|
|
1202
|
+
name = self._expr_name(node)
|
|
1203
|
+
if not name:
|
|
1204
|
+
return []
|
|
1205
|
+
symbol = self._resolve_reference(name, ReferenceKind.VALUE, scope)
|
|
1206
|
+
if symbol is None:
|
|
1207
|
+
return []
|
|
1208
|
+
return list(self._iter_member_scopes(symbol, scope))
|
|
1209
|
+
|
|
1210
|
+
def _collect_method_references(
|
|
1211
|
+
self,
|
|
1212
|
+
node: SyntaxNode,
|
|
1213
|
+
scope: Scope,
|
|
1214
|
+
) -> tuple[Scope, list[SyntaxNode]]:
|
|
1215
|
+
method_scope = self._node_scopes.get(id(node), scope)
|
|
1216
|
+
params = node.find_node(SyntaxNodeType.ntParameters)
|
|
1217
|
+
if params is not None:
|
|
1218
|
+
for param in params.child_nodes:
|
|
1219
|
+
if param.typ != SyntaxNodeType.ntParameter:
|
|
1220
|
+
continue
|
|
1221
|
+
type_node = param.find_node(SyntaxNodeType.ntType)
|
|
1222
|
+
if type_node is not None:
|
|
1223
|
+
self._record_type_reference(type_node, method_scope)
|
|
1224
|
+
value_node = param.find_node(SyntaxNodeType.ntValue)
|
|
1225
|
+
if value_node is not None:
|
|
1226
|
+
self._collect_expr_references(value_node, method_scope)
|
|
1227
|
+
type_params = node.find_node(SyntaxNodeType.ntTypeParams)
|
|
1228
|
+
if type_params is not None:
|
|
1229
|
+
self._collect_type_param_references(type_params, method_scope)
|
|
1230
|
+
return_type_node = node.find_node(SyntaxNodeType.ntReturnType)
|
|
1231
|
+
if return_type_node is not None:
|
|
1232
|
+
type_node = return_type_node.find_node(SyntaxNodeType.ntType)
|
|
1233
|
+
if type_node is not None:
|
|
1234
|
+
self._record_type_reference(type_node, method_scope)
|
|
1235
|
+
excluded = {
|
|
1236
|
+
SyntaxNodeType.ntParameters,
|
|
1237
|
+
SyntaxNodeType.ntReturnType,
|
|
1238
|
+
SyntaxNodeType.ntTypeParams,
|
|
1239
|
+
}
|
|
1240
|
+
return (
|
|
1241
|
+
method_scope,
|
|
1242
|
+
[child for child in node.child_nodes if child.typ not in excluded],
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
def _collect_property_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1246
|
+
type_node = node.find_node(SyntaxNodeType.ntType)
|
|
1247
|
+
if type_node is not None:
|
|
1248
|
+
self._record_type_reference(type_node, scope)
|
|
1249
|
+
params = node.find_node(SyntaxNodeType.ntParameters)
|
|
1250
|
+
if params is not None:
|
|
1251
|
+
for param in params.child_nodes:
|
|
1252
|
+
if param.typ != SyntaxNodeType.ntParameter:
|
|
1253
|
+
continue
|
|
1254
|
+
type_node = param.find_node(SyntaxNodeType.ntType)
|
|
1255
|
+
if type_node is not None:
|
|
1256
|
+
self._record_type_reference(type_node, scope)
|
|
1257
|
+
for child in node.child_nodes:
|
|
1258
|
+
if child.typ in {SyntaxNodeType.ntRead, SyntaxNodeType.ntWrite, SyntaxNodeType.ntImplements}:
|
|
1259
|
+
self._collect_expr_references(child, scope)
|
|
1260
|
+
if child.typ == SyntaxNodeType.ntUnknown and self._attr(child, AttributeName.anKind) in {'add', 'remove', 'stored'}:
|
|
1261
|
+
self._collect_expr_references(child, scope)
|
|
1262
|
+
|
|
1263
|
+
def _collect_type_param_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1264
|
+
for child in node.child_nodes:
|
|
1265
|
+
if child.typ != SyntaxNodeType.ntTypeParam:
|
|
1266
|
+
continue
|
|
1267
|
+
constraints = child.find_node(SyntaxNodeType.ntConstraints)
|
|
1268
|
+
if constraints is None:
|
|
1269
|
+
continue
|
|
1270
|
+
for constraint in constraints.child_nodes:
|
|
1271
|
+
if constraint.typ == SyntaxNodeType.ntType:
|
|
1272
|
+
self._record_type_reference(constraint, scope)
|
|
1273
|
+
|
|
1274
|
+
def _record_type_reference(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1275
|
+
name = self._attr(node, AttributeName.anName)
|
|
1276
|
+
if name:
|
|
1277
|
+
self._record_type_text_reference(name, node, scope)
|
|
1278
|
+
|
|
1279
|
+
if self._attr(node, AttributeName.anType) == 'array':
|
|
1280
|
+
bounds_node = node.find_node(SyntaxNodeType.ntBounds)
|
|
1281
|
+
if bounds_node is not None:
|
|
1282
|
+
for bound in bounds_node.child_nodes:
|
|
1283
|
+
self._record_array_bound_references(bound, scope)
|
|
1284
|
+
|
|
1285
|
+
for child in node.child_nodes:
|
|
1286
|
+
if child.typ == SyntaxNodeType.ntType:
|
|
1287
|
+
self._record_type_reference(child, scope)
|
|
1288
|
+
elif child.typ == SyntaxNodeType.ntTypeArgs:
|
|
1289
|
+
for type_arg in child.child_nodes:
|
|
1290
|
+
if type_arg.typ == SyntaxNodeType.ntType:
|
|
1291
|
+
self._record_type_reference(type_arg, scope)
|
|
1292
|
+
elif child.typ in {
|
|
1293
|
+
SyntaxNodeType.ntParameters,
|
|
1294
|
+
SyntaxNodeType.ntReturnType,
|
|
1295
|
+
}:
|
|
1296
|
+
self._record_nested_type_references(child, scope)
|
|
1297
|
+
|
|
1298
|
+
def _record_nested_type_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1299
|
+
pending = list(reversed(node.child_nodes))
|
|
1300
|
+
while pending:
|
|
1301
|
+
child = pending.pop()
|
|
1302
|
+
if child.typ == SyntaxNodeType.ntType:
|
|
1303
|
+
self._record_type_reference(child, scope)
|
|
1304
|
+
else:
|
|
1305
|
+
pending.extend(reversed(child.child_nodes))
|
|
1306
|
+
|
|
1307
|
+
def _record_type_text_reference(
|
|
1308
|
+
self,
|
|
1309
|
+
text: str,
|
|
1310
|
+
node: SyntaxNode,
|
|
1311
|
+
scope: Scope,
|
|
1312
|
+
*,
|
|
1313
|
+
column_offset: int = 0,
|
|
1314
|
+
) -> None:
|
|
1315
|
+
leading = len(text) - len(text.lstrip())
|
|
1316
|
+
column_offset += leading
|
|
1317
|
+
name = text.strip()
|
|
1318
|
+
if name.casefold().startswith('specialize '):
|
|
1319
|
+
column_offset += len('specialize ')
|
|
1320
|
+
name = name[len('specialize '):].strip()
|
|
1321
|
+
while name.startswith('^'):
|
|
1322
|
+
column_offset += 1
|
|
1323
|
+
name = name[1:].lstrip()
|
|
1324
|
+
short_string = re.fullmatch(r'(?P<name>[A-Za-z_][A-Za-z0-9_.]*)\s*\[[^]]+\]', name)
|
|
1325
|
+
if short_string is not None:
|
|
1326
|
+
name = short_string.group('name')
|
|
1327
|
+
codepage_string = re.fullmatch(
|
|
1328
|
+
r'(?P<name>[A-Za-z_][A-Za-z0-9_.]*)\s*\([^()]*\)',
|
|
1329
|
+
name,
|
|
1330
|
+
)
|
|
1331
|
+
if codepage_string is not None:
|
|
1332
|
+
name = codepage_string.group('name')
|
|
1333
|
+
lowered = name.casefold()
|
|
1334
|
+
if lowered in {
|
|
1335
|
+
'const',
|
|
1336
|
+
'file',
|
|
1337
|
+
'procedure',
|
|
1338
|
+
'function',
|
|
1339
|
+
'reference to procedure',
|
|
1340
|
+
'reference to function',
|
|
1341
|
+
}:
|
|
1342
|
+
return
|
|
1343
|
+
if lowered.startswith(('procedure', 'function')):
|
|
1344
|
+
for type_match in re.finditer(
|
|
1345
|
+
r':\s*(?:\^\s*)?([A-Za-z_][A-Za-z0-9_.]*(?:\s*<[^;()]+>)?)',
|
|
1346
|
+
name,
|
|
1347
|
+
):
|
|
1348
|
+
self._record_type_text_reference(
|
|
1349
|
+
type_match.group(1),
|
|
1350
|
+
node,
|
|
1351
|
+
scope,
|
|
1352
|
+
column_offset=column_offset + type_match.start(1),
|
|
1353
|
+
)
|
|
1354
|
+
return
|
|
1355
|
+
for prefix in ('file of ', 'class of ', 'reference to ', 'set of '):
|
|
1356
|
+
if lowered.startswith(prefix):
|
|
1357
|
+
self._record_type_text_reference(
|
|
1358
|
+
name[len(prefix):].strip(),
|
|
1359
|
+
node,
|
|
1360
|
+
scope,
|
|
1361
|
+
column_offset=column_offset + len(prefix),
|
|
1362
|
+
)
|
|
1363
|
+
return
|
|
1364
|
+
if lowered.startswith('array'):
|
|
1365
|
+
array_match = re.fullmatch(
|
|
1366
|
+
r'array(?:\s*\[(?P<bounds>[^]]*)\])?\s+of\s+(?P<element>.+)',
|
|
1367
|
+
name,
|
|
1368
|
+
re.IGNORECASE,
|
|
1369
|
+
)
|
|
1370
|
+
if array_match is None:
|
|
1371
|
+
return
|
|
1372
|
+
bounds = array_match.group('bounds')
|
|
1373
|
+
if bounds:
|
|
1374
|
+
bounds_start = array_match.start('bounds')
|
|
1375
|
+
search_start = 0
|
|
1376
|
+
for bound_text in bounds.split(','):
|
|
1377
|
+
bound_start = bounds.find(bound_text, search_start)
|
|
1378
|
+
self._record_array_bound_text(
|
|
1379
|
+
bound_text,
|
|
1380
|
+
node,
|
|
1381
|
+
scope,
|
|
1382
|
+
column_offset=column_offset + bounds_start + bound_start,
|
|
1383
|
+
)
|
|
1384
|
+
search_start = bound_start + len(bound_text) + 1
|
|
1385
|
+
self._record_type_text_reference(
|
|
1386
|
+
array_match.group('element'),
|
|
1387
|
+
node,
|
|
1388
|
+
scope,
|
|
1389
|
+
column_offset=column_offset + array_match.start('element'),
|
|
1390
|
+
)
|
|
1391
|
+
return
|
|
1392
|
+
if not name:
|
|
1393
|
+
return
|
|
1394
|
+
generic = self._generic_name_parts(name)
|
|
1395
|
+
reference_name = generic[0] if generic is not None else name
|
|
1396
|
+
self._add_reference(
|
|
1397
|
+
reference_name,
|
|
1398
|
+
ReferenceKind.TYPE,
|
|
1399
|
+
node,
|
|
1400
|
+
scope,
|
|
1401
|
+
column_offset=column_offset,
|
|
1402
|
+
)
|
|
1403
|
+
if generic is not None:
|
|
1404
|
+
_, args = generic
|
|
1405
|
+
search_start = name.find('<') + 1
|
|
1406
|
+
for argument in args:
|
|
1407
|
+
argument_start = name.find(argument, search_start)
|
|
1408
|
+
self._record_type_text_reference(
|
|
1409
|
+
argument,
|
|
1410
|
+
node,
|
|
1411
|
+
scope,
|
|
1412
|
+
column_offset=column_offset + argument_start,
|
|
1413
|
+
)
|
|
1414
|
+
search_start = argument_start + len(argument)
|
|
1415
|
+
|
|
1416
|
+
def _record_array_bound_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1417
|
+
name = self._expr_name(node)
|
|
1418
|
+
if name and node.typ in {SyntaxNodeType.ntIdentifier, SyntaxNodeType.ntDot}:
|
|
1419
|
+
self._add_reference(name, ReferenceKind.TYPE, node, scope)
|
|
1420
|
+
return
|
|
1421
|
+
text = self._expr_to_text(node)
|
|
1422
|
+
if text:
|
|
1423
|
+
self._record_array_bound_text(text, node, scope)
|
|
1424
|
+
return
|
|
1425
|
+
self._record_array_bound_expression(node, scope)
|
|
1426
|
+
|
|
1427
|
+
def _record_array_bound_expression(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1428
|
+
if node.typ == SyntaxNodeType.ntCall and node.child_nodes:
|
|
1429
|
+
target = node.child_nodes[0]
|
|
1430
|
+
target_name = self._expr_name(target)
|
|
1431
|
+
if target_name.casefold() in {'low', 'high', 'pred', 'succ', 'sizeof'}:
|
|
1432
|
+
self._add_reference(target_name, ReferenceKind.CALL, target, scope)
|
|
1433
|
+
arguments = [*target.child_nodes, *node.child_nodes[1:]]
|
|
1434
|
+
for argument in arguments:
|
|
1435
|
+
self._record_array_operator_argument(argument, scope)
|
|
1436
|
+
return
|
|
1437
|
+
if node.typ == SyntaxNodeType.ntIdentifier:
|
|
1438
|
+
self._add_reference(
|
|
1439
|
+
self._attr(node, AttributeName.anName),
|
|
1440
|
+
ReferenceKind.VALUE,
|
|
1441
|
+
node,
|
|
1442
|
+
scope,
|
|
1443
|
+
)
|
|
1444
|
+
return
|
|
1445
|
+
for child in node.child_nodes:
|
|
1446
|
+
self._record_array_bound_expression(child, scope)
|
|
1447
|
+
|
|
1448
|
+
def _record_array_operator_argument(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1449
|
+
name = self._expr_name(node)
|
|
1450
|
+
if name:
|
|
1451
|
+
resolved_type = self._resolve_reference(name, ReferenceKind.TYPE, scope)
|
|
1452
|
+
kind = (
|
|
1453
|
+
ReferenceKind.TYPE
|
|
1454
|
+
if self._is_type_symbol(resolved_type)
|
|
1455
|
+
else ReferenceKind.VALUE
|
|
1456
|
+
)
|
|
1457
|
+
self._add_reference(name, kind, node, scope)
|
|
1458
|
+
return
|
|
1459
|
+
for child in node.child_nodes:
|
|
1460
|
+
self._record_array_operator_argument(child, scope)
|
|
1461
|
+
|
|
1462
|
+
def _record_array_bound_text(
|
|
1463
|
+
self,
|
|
1464
|
+
text: str,
|
|
1465
|
+
node: SyntaxNode,
|
|
1466
|
+
scope: Scope,
|
|
1467
|
+
*,
|
|
1468
|
+
column_offset: int = 0,
|
|
1469
|
+
) -> None:
|
|
1470
|
+
leading = len(text) - len(text.lstrip())
|
|
1471
|
+
column_offset += leading
|
|
1472
|
+
candidate = text.strip()
|
|
1473
|
+
if self._simple_type_name(candidate):
|
|
1474
|
+
self._add_reference(
|
|
1475
|
+
candidate,
|
|
1476
|
+
ReferenceKind.TYPE,
|
|
1477
|
+
node,
|
|
1478
|
+
scope,
|
|
1479
|
+
column_offset=column_offset,
|
|
1480
|
+
)
|
|
1481
|
+
return
|
|
1482
|
+
candidate = re.sub(
|
|
1483
|
+
r"'(?:''|[^'])*'",
|
|
1484
|
+
lambda match: ' ' * len(match.group()),
|
|
1485
|
+
candidate,
|
|
1486
|
+
)
|
|
1487
|
+
candidate = re.sub(
|
|
1488
|
+
r'\$[0-9A-Fa-f]+|%[01]+|&[0-7]+|#[0-9]+|#\$[0-9A-Fa-f]+',
|
|
1489
|
+
lambda match: ' ' * len(match.group()),
|
|
1490
|
+
candidate,
|
|
1491
|
+
)
|
|
1492
|
+
for identifier_match in re.finditer(
|
|
1493
|
+
r'&?[^\W\d]\w*(?:\.&?[^\W\d]\w*)*',
|
|
1494
|
+
candidate,
|
|
1495
|
+
):
|
|
1496
|
+
identifier = identifier_match.group()
|
|
1497
|
+
following = candidate[identifier_match.end():]
|
|
1498
|
+
if (
|
|
1499
|
+
identifier.casefold() in {'low', 'high', 'pred', 'succ', 'sizeof'}
|
|
1500
|
+
and re.match(r'\s*\(', following) is not None
|
|
1501
|
+
):
|
|
1502
|
+
self._add_reference(
|
|
1503
|
+
identifier,
|
|
1504
|
+
ReferenceKind.CALL,
|
|
1505
|
+
node,
|
|
1506
|
+
scope,
|
|
1507
|
+
column_offset=column_offset + identifier_match.start(),
|
|
1508
|
+
)
|
|
1509
|
+
continue
|
|
1510
|
+
resolved_type = self._resolve_reference(identifier, ReferenceKind.TYPE, scope)
|
|
1511
|
+
kind = (
|
|
1512
|
+
ReferenceKind.TYPE
|
|
1513
|
+
if self._is_type_symbol(resolved_type)
|
|
1514
|
+
else ReferenceKind.VALUE
|
|
1515
|
+
)
|
|
1516
|
+
self._add_reference(
|
|
1517
|
+
identifier,
|
|
1518
|
+
kind,
|
|
1519
|
+
node,
|
|
1520
|
+
scope,
|
|
1521
|
+
column_offset=column_offset + identifier_match.start(),
|
|
1522
|
+
)
|
|
1523
|
+
|
|
1524
|
+
def _simple_type_name(self, text: str) -> str:
|
|
1525
|
+
name = text.strip()
|
|
1526
|
+
parts = name.split('.')
|
|
1527
|
+
if parts and all(part.lstrip('&').isidentifier() for part in parts):
|
|
1528
|
+
return name
|
|
1529
|
+
return ''
|
|
1530
|
+
|
|
1531
|
+
def _is_type_symbol(self, symbol: Symbol | None) -> bool:
|
|
1532
|
+
return symbol is not None and symbol.kind in {
|
|
1533
|
+
SymbolKind.TYPE,
|
|
1534
|
+
SymbolKind.TYPE_PARAMETER,
|
|
1535
|
+
SymbolKind.CLASS,
|
|
1536
|
+
SymbolKind.RECORD,
|
|
1537
|
+
SymbolKind.INTERFACE,
|
|
1538
|
+
SymbolKind.ENUM,
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
def _collect_expr_references(self, node: SyntaxNode, scope: Scope) -> None:
|
|
1542
|
+
if node.typ == SyntaxNodeType.ntCall:
|
|
1543
|
+
target = node.child_nodes[0] if node.child_nodes else None
|
|
1544
|
+
if target is not None:
|
|
1545
|
+
self._record_qualified_type_reference(target, scope)
|
|
1546
|
+
self._record_value_qualifier_reference(target, scope)
|
|
1547
|
+
name = self._expr_name(target) if target is not None else ''
|
|
1548
|
+
if name:
|
|
1549
|
+
self._add_reference(name, ReferenceKind.CALL, target or node, scope)
|
|
1550
|
+
for child in node.child_nodes[1:]:
|
|
1551
|
+
self._collect_expr_references(child, scope)
|
|
1552
|
+
return
|
|
1553
|
+
if node.typ == SyntaxNodeType.ntDot:
|
|
1554
|
+
self._record_qualified_type_reference(node, scope)
|
|
1555
|
+
self._record_value_qualifier_reference(node, scope)
|
|
1556
|
+
name = self._expr_name(node)
|
|
1557
|
+
if name:
|
|
1558
|
+
self._add_reference(name, ReferenceKind.VALUE, node, scope)
|
|
1559
|
+
return
|
|
1560
|
+
if node.typ == SyntaxNodeType.ntIdentifier:
|
|
1561
|
+
name = self._attr(node, AttributeName.anName)
|
|
1562
|
+
self._add_reference(name, ReferenceKind.VALUE, node, scope)
|
|
1563
|
+
return
|
|
1564
|
+
|
|
1565
|
+
for child in node.child_nodes:
|
|
1566
|
+
self._collect_expr_references(child, scope)
|
|
1567
|
+
|
|
1568
|
+
def _record_value_qualifier_reference(
|
|
1569
|
+
self,
|
|
1570
|
+
node: SyntaxNode,
|
|
1571
|
+
scope: Scope,
|
|
1572
|
+
) -> None:
|
|
1573
|
+
if node.typ != SyntaxNodeType.ntDot or len(node.child_nodes) != 2:
|
|
1574
|
+
return
|
|
1575
|
+
qualifier = node.child_nodes[0]
|
|
1576
|
+
self._record_value_qualifier_reference(qualifier, scope)
|
|
1577
|
+
qualifier_name = self._expr_name(qualifier)
|
|
1578
|
+
resolved = self._resolve_reference(
|
|
1579
|
+
qualifier_name,
|
|
1580
|
+
ReferenceKind.VALUE,
|
|
1581
|
+
scope,
|
|
1582
|
+
)
|
|
1583
|
+
if resolved is None:
|
|
1584
|
+
resolved = self._resolve_implicit_self_member(qualifier_name, scope)
|
|
1585
|
+
if resolved is None or resolved.kind in {
|
|
1586
|
+
SymbolKind.UNIT,
|
|
1587
|
+
SymbolKind.MODULE,
|
|
1588
|
+
SymbolKind.NAMESPACE,
|
|
1589
|
+
SymbolKind.PACKAGE,
|
|
1590
|
+
SymbolKind.TYPE,
|
|
1591
|
+
SymbolKind.CLASS,
|
|
1592
|
+
SymbolKind.RECORD,
|
|
1593
|
+
SymbolKind.INTERFACE,
|
|
1594
|
+
SymbolKind.ENUM,
|
|
1595
|
+
}:
|
|
1596
|
+
return
|
|
1597
|
+
reference_node = (
|
|
1598
|
+
qualifier.child_nodes[-1]
|
|
1599
|
+
if qualifier.typ == SyntaxNodeType.ntDot and qualifier.child_nodes
|
|
1600
|
+
else qualifier
|
|
1601
|
+
)
|
|
1602
|
+
self._references.append(
|
|
1603
|
+
SymbolReference(
|
|
1604
|
+
name=self._expr_name(reference_node),
|
|
1605
|
+
kind=(
|
|
1606
|
+
ReferenceKind.CALL
|
|
1607
|
+
if resolved.kind
|
|
1608
|
+
in {
|
|
1609
|
+
SymbolKind.METHOD,
|
|
1610
|
+
SymbolKind.FUNCTION,
|
|
1611
|
+
SymbolKind.PROCEDURE,
|
|
1612
|
+
SymbolKind.CONSTRUCTOR,
|
|
1613
|
+
SymbolKind.DESTRUCTOR,
|
|
1614
|
+
}
|
|
1615
|
+
else ReferenceKind.VALUE
|
|
1616
|
+
),
|
|
1617
|
+
ref_range=self._node_range(reference_node),
|
|
1618
|
+
scope=scope,
|
|
1619
|
+
resolved=resolved,
|
|
1620
|
+
)
|
|
1621
|
+
)
|
|
1622
|
+
|
|
1623
|
+
def _resolve_implicit_self_member(
|
|
1624
|
+
self,
|
|
1625
|
+
name: str,
|
|
1626
|
+
scope: Scope,
|
|
1627
|
+
) -> Symbol | None:
|
|
1628
|
+
parts = self._normalized_reference_parts(name)
|
|
1629
|
+
if len(parts) != 2 or parts[0].casefold() != 'self':
|
|
1630
|
+
return None
|
|
1631
|
+
current: Scope | None = scope
|
|
1632
|
+
while current is not None and current.kind != ScopeKind.TYPE:
|
|
1633
|
+
current = current.parent
|
|
1634
|
+
if current is None:
|
|
1635
|
+
return None
|
|
1636
|
+
if current.owner is None:
|
|
1637
|
+
return None
|
|
1638
|
+
return self._resolve_member_symbol(current.owner, parts[1], scope)
|
|
1639
|
+
|
|
1640
|
+
def _record_qualified_type_reference(
|
|
1641
|
+
self,
|
|
1642
|
+
node: SyntaxNode,
|
|
1643
|
+
scope: Scope,
|
|
1644
|
+
) -> None:
|
|
1645
|
+
if node.typ != SyntaxNodeType.ntDot or len(node.child_nodes) != 2:
|
|
1646
|
+
return
|
|
1647
|
+
qualifier = node.child_nodes[0]
|
|
1648
|
+
qualifier_name = self._expr_name(qualifier)
|
|
1649
|
+
qualifier_symbol = self._resolve_reference(
|
|
1650
|
+
qualifier_name,
|
|
1651
|
+
ReferenceKind.VALUE,
|
|
1652
|
+
scope,
|
|
1653
|
+
)
|
|
1654
|
+
if qualifier_symbol is None or qualifier_symbol.kind not in {
|
|
1655
|
+
SymbolKind.TYPE,
|
|
1656
|
+
SymbolKind.CLASS,
|
|
1657
|
+
SymbolKind.RECORD,
|
|
1658
|
+
SymbolKind.INTERFACE,
|
|
1659
|
+
SymbolKind.ENUM,
|
|
1660
|
+
}:
|
|
1661
|
+
return
|
|
1662
|
+
|
|
1663
|
+
member_name = self._expr_name(node.child_nodes[1])
|
|
1664
|
+
qualified_name = self._expr_name(node)
|
|
1665
|
+
member_symbol = self._resolve_reference(
|
|
1666
|
+
qualified_name,
|
|
1667
|
+
ReferenceKind.VALUE,
|
|
1668
|
+
scope,
|
|
1669
|
+
)
|
|
1670
|
+
kind = ReferenceKind.TYPE_QUALIFIER
|
|
1671
|
+
if (
|
|
1672
|
+
member_symbol is not None
|
|
1673
|
+
and member_symbol.kind == SymbolKind.CONSTRUCTOR
|
|
1674
|
+
) or (
|
|
1675
|
+
qualifier_symbol.kind == SymbolKind.CLASS
|
|
1676
|
+
and member_name.casefold() == 'create'
|
|
1677
|
+
):
|
|
1678
|
+
# TObject's implicit Create is not present in every workspace index.
|
|
1679
|
+
kind = ReferenceKind.CONSTRUCTOR_CALL
|
|
1680
|
+
self._references.append(
|
|
1681
|
+
SymbolReference(
|
|
1682
|
+
name=qualifier_name,
|
|
1683
|
+
kind=kind,
|
|
1684
|
+
ref_range=self._node_range(qualifier),
|
|
1685
|
+
scope=scope,
|
|
1686
|
+
resolved=qualifier_symbol,
|
|
1687
|
+
)
|
|
1688
|
+
)
|
|
1689
|
+
|
|
1690
|
+
def _expr_name(self, node: SyntaxNode | None) -> str:
|
|
1691
|
+
if node is None:
|
|
1692
|
+
return ''
|
|
1693
|
+
if node.typ == SyntaxNodeType.ntIdentifier:
|
|
1694
|
+
name = self._attr(node, AttributeName.anName)
|
|
1695
|
+
generic_args = self._attr(node, AttributeName.anGenericArgs)
|
|
1696
|
+
if generic_args:
|
|
1697
|
+
return f'{name}{generic_args}'
|
|
1698
|
+
return name
|
|
1699
|
+
if node.typ == SyntaxNodeType.ntDot and len(node.child_nodes) == 2:
|
|
1700
|
+
left = self._expr_name(node.child_nodes[0])
|
|
1701
|
+
right = self._expr_name(node.child_nodes[1])
|
|
1702
|
+
if left and right:
|
|
1703
|
+
return f'{left}.{right}'
|
|
1704
|
+
return left or right
|
|
1705
|
+
if node.typ == SyntaxNodeType.ntCall and node.child_nodes:
|
|
1706
|
+
return self._expr_name(node.child_nodes[0])
|
|
1707
|
+
if node.typ == SyntaxNodeType.ntIndexed and node.child_nodes:
|
|
1708
|
+
return self._expr_name(node.child_nodes[0])
|
|
1709
|
+
return ''
|
|
1710
|
+
|
|
1711
|
+
def _add_reference(
|
|
1712
|
+
self,
|
|
1713
|
+
name: str,
|
|
1714
|
+
kind: ReferenceKind,
|
|
1715
|
+
node: SyntaxNode,
|
|
1716
|
+
scope: Scope,
|
|
1717
|
+
*,
|
|
1718
|
+
column_offset: int | None = None,
|
|
1719
|
+
) -> None:
|
|
1720
|
+
if not name:
|
|
1721
|
+
return
|
|
1722
|
+
resolved = self._resolve_reference(name, kind, scope)
|
|
1723
|
+
if column_offset is None:
|
|
1724
|
+
reference_node = node
|
|
1725
|
+
reference_name = name
|
|
1726
|
+
if node.typ == SyntaxNodeType.ntDot and node.child_nodes:
|
|
1727
|
+
reference_node = node.child_nodes[-1]
|
|
1728
|
+
reference_name = self._expr_name(reference_node) or name
|
|
1729
|
+
if reference_node.token_end_line:
|
|
1730
|
+
start_file, start_line, start_col = self._map_position(
|
|
1731
|
+
reference_node.file_name,
|
|
1732
|
+
reference_node.line,
|
|
1733
|
+
reference_node.col,
|
|
1734
|
+
)
|
|
1735
|
+
end_file, end_line, end_col = self._map_position(
|
|
1736
|
+
reference_node.file_name,
|
|
1737
|
+
reference_node.token_end_line,
|
|
1738
|
+
reference_node.token_end_col,
|
|
1739
|
+
)
|
|
1740
|
+
if end_file != start_file:
|
|
1741
|
+
end_line, end_col = start_line, start_col + len(reference_name)
|
|
1742
|
+
reference_range = SourceRange(
|
|
1743
|
+
file_name=start_file,
|
|
1744
|
+
start_line=start_line,
|
|
1745
|
+
start_col=start_col,
|
|
1746
|
+
end_line=end_line,
|
|
1747
|
+
end_col=end_col,
|
|
1748
|
+
)
|
|
1749
|
+
else:
|
|
1750
|
+
reference_range = self._node_range(reference_node)
|
|
1751
|
+
if (
|
|
1752
|
+
reference_range.start_line == reference_range.end_line
|
|
1753
|
+
and reference_range.start_col == reference_range.end_col
|
|
1754
|
+
):
|
|
1755
|
+
reference_range = self._offset_range(
|
|
1756
|
+
reference_node,
|
|
1757
|
+
0,
|
|
1758
|
+
len(reference_name),
|
|
1759
|
+
)
|
|
1760
|
+
else:
|
|
1761
|
+
reference_range = self._offset_range(node, column_offset, len(name))
|
|
1762
|
+
if resolved is None and kind == ReferenceKind.TYPE and not self._is_builtin_type(name):
|
|
1763
|
+
self._problems.append(
|
|
1764
|
+
SemanticProblem(
|
|
1765
|
+
message=f'Unresolved type {name}',
|
|
1766
|
+
range=reference_range,
|
|
1767
|
+
)
|
|
1768
|
+
)
|
|
1769
|
+
self._references.append(
|
|
1770
|
+
SymbolReference(
|
|
1771
|
+
name=name,
|
|
1772
|
+
kind=kind,
|
|
1773
|
+
ref_range=reference_range,
|
|
1774
|
+
scope=scope,
|
|
1775
|
+
resolved=resolved,
|
|
1776
|
+
)
|
|
1777
|
+
)
|
|
1778
|
+
|
|
1779
|
+
def _offset_range(
|
|
1780
|
+
self,
|
|
1781
|
+
node: SyntaxNode,
|
|
1782
|
+
column_offset: int,
|
|
1783
|
+
length: int,
|
|
1784
|
+
) -> SourceRange:
|
|
1785
|
+
positions = node.source_positions
|
|
1786
|
+
end_offset = column_offset + length
|
|
1787
|
+
if (
|
|
1788
|
+
positions
|
|
1789
|
+
and length > 0
|
|
1790
|
+
and column_offset < len(positions)
|
|
1791
|
+
and end_offset <= len(positions)
|
|
1792
|
+
):
|
|
1793
|
+
raw_start_line, raw_start_col = positions[column_offset]
|
|
1794
|
+
raw_end_line, raw_end_col = positions[end_offset - 1]
|
|
1795
|
+
raw_end_col += 1
|
|
1796
|
+
else:
|
|
1797
|
+
raw_start_line = node.line
|
|
1798
|
+
raw_start_col = node.col + column_offset
|
|
1799
|
+
raw_end_line = node.line
|
|
1800
|
+
raw_end_col = node.col + end_offset
|
|
1801
|
+
start_file, start_line, start_col = self._map_position(
|
|
1802
|
+
node.file_name,
|
|
1803
|
+
raw_start_line,
|
|
1804
|
+
raw_start_col,
|
|
1805
|
+
)
|
|
1806
|
+
end_file, end_line, end_col = self._map_position(
|
|
1807
|
+
node.file_name,
|
|
1808
|
+
raw_end_line,
|
|
1809
|
+
raw_end_col,
|
|
1810
|
+
)
|
|
1811
|
+
if end_file != start_file:
|
|
1812
|
+
end_line, end_col = start_line, start_col + length
|
|
1813
|
+
return SourceRange(
|
|
1814
|
+
file_name=start_file,
|
|
1815
|
+
start_line=start_line,
|
|
1816
|
+
start_col=start_col,
|
|
1817
|
+
end_line=end_line,
|
|
1818
|
+
end_col=end_col,
|
|
1819
|
+
)
|
|
1820
|
+
|
|
1821
|
+
def _add_method_resolution_reference(
|
|
1822
|
+
self,
|
|
1823
|
+
node: SyntaxNode,
|
|
1824
|
+
scope: Scope,
|
|
1825
|
+
) -> None:
|
|
1826
|
+
name = self._attr(node, AttributeName.anName)
|
|
1827
|
+
if not name:
|
|
1828
|
+
return
|
|
1829
|
+
type_scope: Scope | None = scope
|
|
1830
|
+
while type_scope is not None and type_scope.kind != ScopeKind.TYPE:
|
|
1831
|
+
type_scope = type_scope.parent
|
|
1832
|
+
routine_kinds = {
|
|
1833
|
+
SymbolKind.METHOD,
|
|
1834
|
+
SymbolKind.FUNCTION,
|
|
1835
|
+
SymbolKind.PROCEDURE,
|
|
1836
|
+
SymbolKind.CONSTRUCTOR,
|
|
1837
|
+
SymbolKind.DESTRUCTOR,
|
|
1838
|
+
}
|
|
1839
|
+
candidates = (
|
|
1840
|
+
[
|
|
1841
|
+
symbol
|
|
1842
|
+
for symbol in type_scope.lookup_local(name)
|
|
1843
|
+
if symbol.kind in routine_kinds
|
|
1844
|
+
]
|
|
1845
|
+
if type_scope is not None
|
|
1846
|
+
else []
|
|
1847
|
+
)
|
|
1848
|
+
self._references.append(
|
|
1849
|
+
SymbolReference(
|
|
1850
|
+
name=name,
|
|
1851
|
+
kind=ReferenceKind.VALUE,
|
|
1852
|
+
ref_range=self._node_range(node),
|
|
1853
|
+
scope=scope,
|
|
1854
|
+
resolved=candidates[0] if candidates else None,
|
|
1855
|
+
)
|
|
1856
|
+
)
|
|
1857
|
+
|
|
1858
|
+
def _resolve_reference(self, name: str, kind: ReferenceKind, scope: Scope) -> Symbol | None:
|
|
1859
|
+
parts = self._normalized_reference_parts(name)
|
|
1860
|
+
if not parts:
|
|
1861
|
+
return None
|
|
1862
|
+
if len(parts) > 1:
|
|
1863
|
+
resolved = self._resolve_qualified_reference(parts, scope)
|
|
1864
|
+
if resolved is not None:
|
|
1865
|
+
return resolved
|
|
1866
|
+
symbols = self._index.resolve_qualified('.'.join(parts))
|
|
1867
|
+
return symbols[0] if symbols else None
|
|
1868
|
+
symbols = scope.resolve(parts[0])
|
|
1869
|
+
if symbols:
|
|
1870
|
+
return symbols[0]
|
|
1871
|
+
return None
|
|
1872
|
+
|
|
1873
|
+
def _add_problem(self, message: str, node: SyntaxNode) -> None:
|
|
1874
|
+
self._problems.append(
|
|
1875
|
+
SemanticProblem(
|
|
1876
|
+
message=message,
|
|
1877
|
+
range=self._node_range(node),
|
|
1878
|
+
)
|
|
1879
|
+
)
|
|
1880
|
+
|
|
1881
|
+
def _is_builtin_type(self, name: str) -> bool:
|
|
1882
|
+
base = name.split('.')[-1]
|
|
1883
|
+
base = self._strip_generic_args(base)
|
|
1884
|
+
return base.casefold() in self._builtin_types
|
|
1885
|
+
|
|
1886
|
+
def _normalized_reference_parts(self, name: str) -> list[str]:
|
|
1887
|
+
if not name:
|
|
1888
|
+
return []
|
|
1889
|
+
parts = self._split_reference_parts(name)
|
|
1890
|
+
normalized = [self._strip_generic_args(part) for part in parts]
|
|
1891
|
+
return [part for part in normalized if part]
|
|
1892
|
+
|
|
1893
|
+
def _split_reference_parts(self, name: str) -> list[str]:
|
|
1894
|
+
parts: list[str] = []
|
|
1895
|
+
buf: list[str] = []
|
|
1896
|
+
depth = 0
|
|
1897
|
+
for ch in name:
|
|
1898
|
+
if ch == '<':
|
|
1899
|
+
depth += 1
|
|
1900
|
+
elif ch == '>':
|
|
1901
|
+
if depth:
|
|
1902
|
+
depth -= 1
|
|
1903
|
+
elif ch == '.' and depth == 0:
|
|
1904
|
+
part = ''.join(buf).strip()
|
|
1905
|
+
if part:
|
|
1906
|
+
parts.append(part)
|
|
1907
|
+
buf = []
|
|
1908
|
+
continue
|
|
1909
|
+
buf.append(ch)
|
|
1910
|
+
part = ''.join(buf).strip()
|
|
1911
|
+
if part:
|
|
1912
|
+
parts.append(part)
|
|
1913
|
+
return parts
|
|
1914
|
+
|
|
1915
|
+
def _strip_generic_args(self, name: str) -> str:
|
|
1916
|
+
if '<' in name:
|
|
1917
|
+
return name.split('<', 1)[0]
|
|
1918
|
+
return name
|
|
1919
|
+
|
|
1920
|
+
def _resolve_qualified_reference(self, parts: list[str], scope: Scope) -> Symbol | None:
|
|
1921
|
+
if parts[0].casefold() == 'self':
|
|
1922
|
+
type_scope: Scope | None = scope
|
|
1923
|
+
while type_scope is not None and type_scope.kind != ScopeKind.TYPE:
|
|
1924
|
+
type_scope = type_scope.parent
|
|
1925
|
+
if type_scope is not None and type_scope.owner is not None:
|
|
1926
|
+
symbol = type_scope.owner
|
|
1927
|
+
for part in parts[1:]:
|
|
1928
|
+
member = self._resolve_member_symbol(symbol, part, scope)
|
|
1929
|
+
if member is None:
|
|
1930
|
+
break
|
|
1931
|
+
symbol = member
|
|
1932
|
+
else:
|
|
1933
|
+
return symbol
|
|
1934
|
+
unit_scope, unit_len = self._match_unit_prefix(parts)
|
|
1935
|
+
if unit_scope is not None and unit_len > 0:
|
|
1936
|
+
unit_symbol = self._resolve_scope_chain(
|
|
1937
|
+
unit_scope,
|
|
1938
|
+
parts[unit_len:],
|
|
1939
|
+
allow_imports=False,
|
|
1940
|
+
origin_scope=scope,
|
|
1941
|
+
)
|
|
1942
|
+
if unit_symbol is not None:
|
|
1943
|
+
return unit_symbol
|
|
1944
|
+
return self._resolve_scope_chain(scope, parts, allow_imports=True, origin_scope=scope)
|
|
1945
|
+
|
|
1946
|
+
def _match_unit_prefix(self, parts: list[str]) -> tuple[Scope | None, int]:
|
|
1947
|
+
if len(parts) < 2:
|
|
1948
|
+
return None, 0
|
|
1949
|
+
for idx in range(len(parts) - 1, 0, -1):
|
|
1950
|
+
unit_name = '.'.join(parts[:idx])
|
|
1951
|
+
unit_scope = self._index.lookup_unit(unit_name)
|
|
1952
|
+
if unit_scope is not None:
|
|
1953
|
+
return unit_scope, idx
|
|
1954
|
+
return None, 0
|
|
1955
|
+
|
|
1956
|
+
def _resolve_scope_chain(
|
|
1957
|
+
self,
|
|
1958
|
+
scope: Scope,
|
|
1959
|
+
parts: list[str],
|
|
1960
|
+
*,
|
|
1961
|
+
allow_imports: bool,
|
|
1962
|
+
origin_scope: Scope,
|
|
1963
|
+
) -> Symbol | None:
|
|
1964
|
+
if not parts:
|
|
1965
|
+
return None
|
|
1966
|
+
symbol = self._resolve_in_scope(scope, parts[0], allow_imports=allow_imports)
|
|
1967
|
+
if symbol is None:
|
|
1968
|
+
return None
|
|
1969
|
+
for part in parts[1:]:
|
|
1970
|
+
member_symbol = self._resolve_member_symbol(symbol, part, origin_scope)
|
|
1971
|
+
if member_symbol is None:
|
|
1972
|
+
return None
|
|
1973
|
+
symbol = member_symbol
|
|
1974
|
+
return symbol
|
|
1975
|
+
|
|
1976
|
+
def _resolve_in_scope(self, scope: Scope, name: str, *, allow_imports: bool) -> Symbol | None:
|
|
1977
|
+
symbols = scope.resolve(name) if allow_imports else scope.lookup_local(name)
|
|
1978
|
+
return symbols[0] if symbols else None
|
|
1979
|
+
|
|
1980
|
+
def _member_scope_for_symbol(self, symbol: Symbol, scope: Scope) -> Scope | None:
|
|
1981
|
+
scopes = list(self._iter_member_scopes(symbol, scope))
|
|
1982
|
+
return scopes[0] if scopes else None
|
|
1983
|
+
|
|
1984
|
+
def _resolve_member_symbol(self, symbol: Symbol, name: str, scope: Scope) -> Symbol | None:
|
|
1985
|
+
for member_scope in self._iter_member_scopes(symbol, scope):
|
|
1986
|
+
members = member_scope.lookup_local(name)
|
|
1987
|
+
if members:
|
|
1988
|
+
return members[0]
|
|
1989
|
+
return None
|
|
1990
|
+
|
|
1991
|
+
def _iter_member_scopes(self, symbol: Symbol, scope: Scope) -> Iterable[Scope]:
|
|
1992
|
+
type_symbol = self._type_owner_for_symbol(symbol, scope)
|
|
1993
|
+
if type_symbol is None:
|
|
1994
|
+
return
|
|
1995
|
+
yield from self._iter_type_member_scopes(type_symbol, scope)
|
|
1996
|
+
|
|
1997
|
+
def _type_owner_for_symbol(self, symbol: Symbol, scope: Scope) -> Symbol | None:
|
|
1998
|
+
if symbol.member_scope is not None and symbol.kind in {
|
|
1999
|
+
SymbolKind.TYPE,
|
|
2000
|
+
SymbolKind.CLASS,
|
|
2001
|
+
SymbolKind.RECORD,
|
|
2002
|
+
SymbolKind.INTERFACE,
|
|
2003
|
+
SymbolKind.ENUM,
|
|
2004
|
+
}:
|
|
2005
|
+
return symbol
|
|
2006
|
+
type_ref = symbol.type_ref
|
|
2007
|
+
if isinstance(type_ref, ProcTypeRef) and type_ref.return_type is not None:
|
|
2008
|
+
type_ref = type_ref.return_type
|
|
2009
|
+
return self._type_symbol_from_ref(type_ref, scope)
|
|
2010
|
+
|
|
2011
|
+
def _iter_type_member_scopes(self, type_symbol: Symbol, scope: Scope) -> Iterable[Scope]:
|
|
2012
|
+
seen: set[int] = set()
|
|
2013
|
+
queue: list[Symbol] = [type_symbol]
|
|
2014
|
+
while queue:
|
|
2015
|
+
current = queue.pop(0)
|
|
2016
|
+
current_id = id(current)
|
|
2017
|
+
if current_id in seen:
|
|
2018
|
+
continue
|
|
2019
|
+
seen.add(current_id)
|
|
2020
|
+
if current.member_scope is not None:
|
|
2021
|
+
yield current.member_scope
|
|
2022
|
+
for base_ref in current.base_types:
|
|
2023
|
+
base_symbol = self._type_symbol_from_ref(base_ref, scope)
|
|
2024
|
+
if base_symbol is not None:
|
|
2025
|
+
queue.append(base_symbol)
|
|
2026
|
+
|
|
2027
|
+
def _type_symbol_from_ref(self, type_ref: TypeRef, scope: Scope) -> Symbol | None:
|
|
2028
|
+
if isinstance(type_ref, GenericInstanceTypeRef):
|
|
2029
|
+
return self._type_symbol_from_ref(type_ref.base, scope)
|
|
2030
|
+
if isinstance(type_ref, NamedTypeRef):
|
|
2031
|
+
if type_ref.unit_name:
|
|
2032
|
+
unit_scope = self._index.lookup_unit(type_ref.unit_name)
|
|
2033
|
+
if unit_scope is not None:
|
|
2034
|
+
symbols = unit_scope.lookup_local(type_ref.name)
|
|
2035
|
+
return symbols[0] if symbols else None
|
|
2036
|
+
if '.' in type_ref.name:
|
|
2037
|
+
symbols = self._index.resolve_qualified(type_ref.name)
|
|
2038
|
+
if symbols:
|
|
2039
|
+
return symbols[0]
|
|
2040
|
+
symbols = scope.resolve(type_ref.name)
|
|
2041
|
+
return symbols[0] if symbols else None
|
|
2042
|
+
return None
|
|
2043
|
+
|
|
2044
|
+
def _define_symbol(self, scope: Scope, symbol: Symbol) -> None:
|
|
2045
|
+
existing = scope.lookup_local(symbol.name)
|
|
2046
|
+
scope.define(symbol)
|
|
2047
|
+
for prev in existing:
|
|
2048
|
+
if self._is_overloadable(prev, symbol):
|
|
2049
|
+
prev.add_overload(symbol)
|
|
2050
|
+
symbol.add_overload(prev)
|
|
2051
|
+
|
|
2052
|
+
def _is_overloadable(self, left: Symbol, right: Symbol) -> bool:
|
|
2053
|
+
overload_kinds = {
|
|
2054
|
+
SymbolKind.METHOD,
|
|
2055
|
+
SymbolKind.FUNCTION,
|
|
2056
|
+
SymbolKind.PROCEDURE,
|
|
2057
|
+
SymbolKind.CONSTRUCTOR,
|
|
2058
|
+
SymbolKind.DESTRUCTOR,
|
|
2059
|
+
}
|
|
2060
|
+
return left.kind in overload_kinds and right.kind in overload_kinds
|
|
2061
|
+
|
|
2062
|
+
def _symbol_attributes(self, node: SyntaxNode) -> dict[str, str]:
|
|
2063
|
+
attrs: dict[str, str] = {}
|
|
2064
|
+
skip = {
|
|
2065
|
+
AttributeName.anName,
|
|
2066
|
+
AttributeName.anKind,
|
|
2067
|
+
AttributeName.anType,
|
|
2068
|
+
AttributeName.anClass,
|
|
2069
|
+
AttributeName.anVisibility,
|
|
2070
|
+
AttributeName.anPath,
|
|
2071
|
+
}
|
|
2072
|
+
for key, value in node.attributes:
|
|
2073
|
+
if key in skip:
|
|
2074
|
+
continue
|
|
2075
|
+
attrs[ATTRIBUTE_NAME_STRINGS[key]] = value
|
|
2076
|
+
return attrs
|
|
2077
|
+
|
|
2078
|
+
def _constraint_names(self, node: SyntaxNode) -> tuple[str, ...]:
|
|
2079
|
+
constraints_node = node.find_node(SyntaxNodeType.ntConstraints)
|
|
2080
|
+
if constraints_node is None:
|
|
2081
|
+
return ()
|
|
2082
|
+
names: list[str] = []
|
|
2083
|
+
for child in constraints_node.child_nodes:
|
|
2084
|
+
if child.typ == SyntaxNodeType.ntClassConstraint:
|
|
2085
|
+
names.append('class')
|
|
2086
|
+
elif child.typ == SyntaxNodeType.ntRecordConstraint:
|
|
2087
|
+
names.append('record')
|
|
2088
|
+
elif child.typ == SyntaxNodeType.ntConstructorConstraint:
|
|
2089
|
+
names.append('constructor')
|
|
2090
|
+
elif child.typ == SyntaxNodeType.ntInterfaceConstraint:
|
|
2091
|
+
names.append('interface')
|
|
2092
|
+
elif child.typ == SyntaxNodeType.ntUnmanagedConstraint:
|
|
2093
|
+
names.append('unmanaged')
|
|
2094
|
+
elif child.typ == SyntaxNodeType.ntType:
|
|
2095
|
+
names.append(self._type_from_node(child).display_name())
|
|
2096
|
+
return tuple(names)
|
|
2097
|
+
|
|
2098
|
+
def _named_type_ref(self, name: str) -> NamedTypeRef:
|
|
2099
|
+
if '.' in name:
|
|
2100
|
+
unit_name, type_name = self._split_qualified_name(name)
|
|
2101
|
+
return NamedTypeRef(name=type_name, unit_name=unit_name)
|
|
2102
|
+
return NamedTypeRef(name=name)
|
|
2103
|
+
|
|
2104
|
+
def _split_qualified_name(self, name: str) -> tuple[str, str]:
|
|
2105
|
+
parts = name.split('.')
|
|
2106
|
+
return ('.'.join(parts[:-1]), parts[-1])
|
|
2107
|
+
|
|
2108
|
+
def _unit_scope_for_symbol(self, symbol: Symbol) -> Scope | None:
|
|
2109
|
+
scope: Scope | None = symbol.scope
|
|
2110
|
+
while scope is not None:
|
|
2111
|
+
if scope.kind == ScopeKind.UNIT:
|
|
2112
|
+
return scope
|
|
2113
|
+
scope = scope.parent
|
|
2114
|
+
return None
|
|
2115
|
+
|
|
2116
|
+
def _attr(self, node: SyntaxNode, name: AttributeName) -> str:
|
|
2117
|
+
return node.get_attribute(name)
|
|
2118
|
+
|
|
2119
|
+
def _child_name(self, node: SyntaxNode | None) -> str:
|
|
2120
|
+
if node is None:
|
|
2121
|
+
return ''
|
|
2122
|
+
if isinstance(node, ValuedSyntaxNode):
|
|
2123
|
+
return node.value
|
|
2124
|
+
name = self._attr(node, AttributeName.anName)
|
|
2125
|
+
if name:
|
|
2126
|
+
return name
|
|
2127
|
+
for child in node.child_nodes:
|
|
2128
|
+
if child.typ == SyntaxNodeType.ntName:
|
|
2129
|
+
if isinstance(child, ValuedSyntaxNode):
|
|
2130
|
+
return child.value
|
|
2131
|
+
return self._attr(child, AttributeName.anName)
|
|
2132
|
+
return ''
|
|
2133
|
+
|
|
2134
|
+
def _expr_to_text(self, node: SyntaxNode) -> str:
|
|
2135
|
+
if isinstance(node, ValuedSyntaxNode):
|
|
2136
|
+
return node.value
|
|
2137
|
+
name = node.get_attribute(AttributeName.anName)
|
|
2138
|
+
if name:
|
|
2139
|
+
return name
|
|
2140
|
+
binary_ops = {
|
|
2141
|
+
SyntaxNodeType.ntAdd: '+',
|
|
2142
|
+
SyntaxNodeType.ntSub: '-',
|
|
2143
|
+
SyntaxNodeType.ntMul: '*',
|
|
2144
|
+
SyntaxNodeType.ntFDiv: '/',
|
|
2145
|
+
SyntaxNodeType.ntDiv: 'div',
|
|
2146
|
+
SyntaxNodeType.ntMod: 'mod',
|
|
2147
|
+
SyntaxNodeType.ntShl: 'shl',
|
|
2148
|
+
SyntaxNodeType.ntShr: 'shr',
|
|
2149
|
+
SyntaxNodeType.ntAnd: 'and',
|
|
2150
|
+
SyntaxNodeType.ntOr: 'or',
|
|
2151
|
+
SyntaxNodeType.ntXor: 'xor',
|
|
2152
|
+
SyntaxNodeType.ntEqual: '=',
|
|
2153
|
+
SyntaxNodeType.ntNotEqual: '<>',
|
|
2154
|
+
SyntaxNodeType.ntLower: '<',
|
|
2155
|
+
SyntaxNodeType.ntLowerEqual: '<=',
|
|
2156
|
+
SyntaxNodeType.ntGreater: '>',
|
|
2157
|
+
SyntaxNodeType.ntGreaterEqual: '>=',
|
|
2158
|
+
SyntaxNodeType.ntIn: 'in',
|
|
2159
|
+
SyntaxNodeType.ntIs: 'is',
|
|
2160
|
+
SyntaxNodeType.ntAs: 'as',
|
|
2161
|
+
SyntaxNodeType.ntNotIn: 'not in',
|
|
2162
|
+
SyntaxNodeType.ntIsNot: 'is not',
|
|
2163
|
+
}
|
|
2164
|
+
if node.typ in binary_ops and len(node.child_nodes) >= 2:
|
|
2165
|
+
left = self._expr_to_text(node.child_nodes[0])
|
|
2166
|
+
right = self._expr_to_text(node.child_nodes[1])
|
|
2167
|
+
if left and right:
|
|
2168
|
+
return f'{left} {binary_ops[node.typ]} {right}'
|
|
2169
|
+
if node.typ == SyntaxNodeType.ntUnaryMinus and node.child_nodes:
|
|
2170
|
+
inner = self._expr_to_text(node.child_nodes[0])
|
|
2171
|
+
return f'-{inner}' if inner else ''
|
|
2172
|
+
if node.typ == SyntaxNodeType.ntNot and node.child_nodes:
|
|
2173
|
+
inner = self._expr_to_text(node.child_nodes[0])
|
|
2174
|
+
return f'not {inner}' if inner else ''
|
|
2175
|
+
if node.typ in {SyntaxNodeType.ntAddr, SyntaxNodeType.ntDeref} and node.child_nodes:
|
|
2176
|
+
inner = self._expr_to_text(node.child_nodes[0])
|
|
2177
|
+
if not inner:
|
|
2178
|
+
return ''
|
|
2179
|
+
return f'@{inner}' if node.typ == SyntaxNodeType.ntAddr else f'^{inner}'
|
|
2180
|
+
if node.typ == SyntaxNodeType.ntCall and node.child_nodes:
|
|
2181
|
+
callee = self._expr_to_text(node.child_nodes[0])
|
|
2182
|
+
args_node = node.find_node(SyntaxNodeType.ntArguments)
|
|
2183
|
+
args = []
|
|
2184
|
+
if args_node is not None:
|
|
2185
|
+
for child in args_node.child_nodes:
|
|
2186
|
+
text = self._expr_to_text(child)
|
|
2187
|
+
if text:
|
|
2188
|
+
args.append(text)
|
|
2189
|
+
return f'{callee}({", ".join(args)})' if callee else ''
|
|
2190
|
+
if node.typ == SyntaxNodeType.ntNamedArgument and node.child_nodes:
|
|
2191
|
+
arg_name = self._attr(node, AttributeName.anName)
|
|
2192
|
+
value = self._expr_to_text(node.child_nodes[0])
|
|
2193
|
+
if arg_name and value:
|
|
2194
|
+
return f'{arg_name}: {value}'
|
|
2195
|
+
if node.typ == SyntaxNodeType.ntPositionalArgument and node.child_nodes:
|
|
2196
|
+
return self._expr_to_text(node.child_nodes[0])
|
|
2197
|
+
if node.typ == SyntaxNodeType.ntDot and len(node.child_nodes) >= 2:
|
|
2198
|
+
left = self._expr_to_text(node.child_nodes[0])
|
|
2199
|
+
right = self._expr_to_text(node.child_nodes[1])
|
|
2200
|
+
if left and right:
|
|
2201
|
+
return f'{left}.{right}'
|
|
2202
|
+
if node.typ == SyntaxNodeType.ntIndexed and node.child_nodes:
|
|
2203
|
+
base = self._expr_to_text(node.child_nodes[0])
|
|
2204
|
+
exprs_node = node.find_node(SyntaxNodeType.ntExpressions)
|
|
2205
|
+
parts = []
|
|
2206
|
+
if exprs_node is not None:
|
|
2207
|
+
for child in exprs_node.child_nodes:
|
|
2208
|
+
text = self._expr_to_text(child)
|
|
2209
|
+
if text:
|
|
2210
|
+
parts.append(text)
|
|
2211
|
+
return f'{base}[{", ".join(parts)}]' if base else ''
|
|
2212
|
+
if node.typ == SyntaxNodeType.ntExpression and self._attr(node, AttributeName.anKind) == 'format':
|
|
2213
|
+
parts = []
|
|
2214
|
+
for child in node.child_nodes:
|
|
2215
|
+
text = self._expr_to_text(child)
|
|
2216
|
+
if text:
|
|
2217
|
+
parts.append(text)
|
|
2218
|
+
return ':'.join(parts)
|
|
2219
|
+
return ''
|
|
2220
|
+
|
|
2221
|
+
def _node_range(self, node: SyntaxNode) -> SourceRange:
|
|
2222
|
+
if isinstance(node, CompoundSyntaxNode):
|
|
2223
|
+
end_line, end_col = node.end_line, node.end_col
|
|
2224
|
+
else:
|
|
2225
|
+
end_line, end_col = node.line, node.col
|
|
2226
|
+
start_file, start_line, start_col = self._map_position(
|
|
2227
|
+
node.file_name,
|
|
2228
|
+
node.line,
|
|
2229
|
+
node.col,
|
|
2230
|
+
)
|
|
2231
|
+
end_file, mapped_end_line, mapped_end_col = self._map_position(
|
|
2232
|
+
node.file_name,
|
|
2233
|
+
end_line,
|
|
2234
|
+
end_col,
|
|
2235
|
+
)
|
|
2236
|
+
if end_file != start_file or (
|
|
2237
|
+
mapped_end_line,
|
|
2238
|
+
mapped_end_col,
|
|
2239
|
+
) < (start_line, start_col):
|
|
2240
|
+
mapped_end_line, mapped_end_col = start_line, start_col
|
|
2241
|
+
return SourceRange(
|
|
2242
|
+
file_name=start_file,
|
|
2243
|
+
start_line=start_line,
|
|
2244
|
+
start_col=start_col,
|
|
2245
|
+
end_line=mapped_end_line,
|
|
2246
|
+
end_col=mapped_end_col,
|
|
2247
|
+
)
|
|
2248
|
+
|
|
2249
|
+
def _node_name_range(self, node: SyntaxNode, name: str) -> SourceRange:
|
|
2250
|
+
node_range = self._node_range(node)
|
|
2251
|
+
return SourceRange(
|
|
2252
|
+
file_name=node_range.file_name,
|
|
2253
|
+
start_line=node_range.start_line,
|
|
2254
|
+
start_col=node_range.start_col,
|
|
2255
|
+
end_line=node_range.start_line,
|
|
2256
|
+
end_col=node_range.start_col + len(name),
|
|
2257
|
+
)
|
|
2258
|
+
|
|
2259
|
+
def _map_position(
|
|
2260
|
+
self,
|
|
2261
|
+
file_name: str,
|
|
2262
|
+
line: int,
|
|
2263
|
+
col: int,
|
|
2264
|
+
) -> tuple[str, int, int]:
|
|
2265
|
+
preprocessed = self._source_maps.get(file_name)
|
|
2266
|
+
if preprocessed is None:
|
|
2267
|
+
return file_name, line, col
|
|
2268
|
+
mapped_file, mapped_line, mapped_col = preprocessed.map_position(line, col)
|
|
2269
|
+
if not mapped_file or mapped_line < 1 or mapped_col < 1:
|
|
2270
|
+
return file_name, line, col
|
|
2271
|
+
return mapped_file, mapped_line, mapped_col
|
|
2272
|
+
|
|
2273
|
+
def _node_tree_range(self, node: SyntaxNode) -> SourceRange:
|
|
2274
|
+
result = self._node_range(node)
|
|
2275
|
+
pending = list(reversed(node.child_nodes))
|
|
2276
|
+
while pending:
|
|
2277
|
+
child = pending.pop()
|
|
2278
|
+
child_range = self._node_range(child)
|
|
2279
|
+
if (
|
|
2280
|
+
child_range.file_name == result.file_name
|
|
2281
|
+
and (child_range.end_line, child_range.end_col)
|
|
2282
|
+
> (result.end_line, result.end_col)
|
|
2283
|
+
):
|
|
2284
|
+
result = SourceRange(
|
|
2285
|
+
file_name=result.file_name,
|
|
2286
|
+
start_line=result.start_line,
|
|
2287
|
+
start_col=result.start_col,
|
|
2288
|
+
end_line=child_range.end_line,
|
|
2289
|
+
end_col=child_range.end_col,
|
|
2290
|
+
)
|
|
2291
|
+
pending.extend(reversed(child.child_nodes))
|
|
2292
|
+
return result
|
|
2293
|
+
|
|
2294
|
+
def _visibility_map(self) -> dict[SyntaxNodeType, Visibility]:
|
|
2295
|
+
return {
|
|
2296
|
+
SyntaxNodeType.ntPrivate: Visibility.PRIVATE,
|
|
2297
|
+
SyntaxNodeType.ntProtected: Visibility.PROTECTED,
|
|
2298
|
+
SyntaxNodeType.ntPublic: Visibility.PUBLIC,
|
|
2299
|
+
SyntaxNodeType.ntPublished: Visibility.PUBLISHED,
|
|
2300
|
+
SyntaxNodeType.ntStrictPrivate: Visibility.STRICT_PRIVATE,
|
|
2301
|
+
SyntaxNodeType.ntStrictProtected: Visibility.STRICT_PROTECTED,
|
|
2302
|
+
}
|