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.
Files changed (56) hide show
  1. delphi_lsp/__init__.py +184 -0
  2. delphi_lsp/_version.py +1 -0
  3. delphi_lsp/agent_cache.py +1574 -0
  4. delphi_lsp/agent_cli.py +902 -0
  5. delphi_lsp/agent_context.py +3907 -0
  6. delphi_lsp/agent_cpg.py +349 -0
  7. delphi_lsp/agent_cpg_builder.py +666 -0
  8. delphi_lsp/agent_layers.py +1410 -0
  9. delphi_lsp/agent_metrics.py +526 -0
  10. delphi_lsp/agent_protocol.py +445 -0
  11. delphi_lsp/agent_relations.py +1881 -0
  12. delphi_lsp/agent_templates.py +614 -0
  13. delphi_lsp/agent_wiki.py +2853 -0
  14. delphi_lsp/agent_workspace.py +860 -0
  15. delphi_lsp/ast_serialize.py +126 -0
  16. delphi_lsp/binary.py +250 -0
  17. delphi_lsp/comment_builder.py +28 -0
  18. delphi_lsp/consts.py +345 -0
  19. delphi_lsp/delphi_lsp_native.pyd +0 -0
  20. delphi_lsp/delphiast_lexer.py +399 -0
  21. delphi_lsp/delphiast_parser.py +2189 -0
  22. delphi_lsp/delphiast_tokens.py +41 -0
  23. delphi_lsp/grammar.py +459 -0
  24. delphi_lsp/incremental.py +1139 -0
  25. delphi_lsp/lark_builder.py +2684 -0
  26. delphi_lsp/lark_tokens.py +236 -0
  27. delphi_lsp/lexical_scanner.py +30 -0
  28. delphi_lsp/lsp_server.py +6698 -0
  29. delphi_lsp/metrics.py +1452 -0
  30. delphi_lsp/native_bridge.py +300 -0
  31. delphi_lsp/navigation_cache.py +189 -0
  32. delphi_lsp/nodes.py +462 -0
  33. delphi_lsp/parallel_outline.py +662 -0
  34. delphi_lsp/parse_artifacts.py +969 -0
  35. delphi_lsp/parser.py +342 -0
  36. delphi_lsp/parser_backend.py +139 -0
  37. delphi_lsp/preprocessor.py +1122 -0
  38. delphi_lsp/progress.py +26 -0
  39. delphi_lsp/project_config.py +285 -0
  40. delphi_lsp/project_discovery.py +932 -0
  41. delphi_lsp/project_indexer.py +470 -0
  42. delphi_lsp/py.typed +1 -0
  43. delphi_lsp/response_cache.py +465 -0
  44. delphi_lsp/semantic.py +394 -0
  45. delphi_lsp/semantic_builder.py +2302 -0
  46. delphi_lsp/source_reader.py +17 -0
  47. delphi_lsp/wiki_external_sort.py +392 -0
  48. delphi_lsp/wiki_memory.py +548 -0
  49. delphi_lsp/workspace.py +83 -0
  50. delphi_lsp/writer.py +73 -0
  51. python_delphi_lsp-3.6.0.dist-info/METADATA +766 -0
  52. python_delphi_lsp-3.6.0.dist-info/RECORD +56 -0
  53. python_delphi_lsp-3.6.0.dist-info/WHEEL +4 -0
  54. python_delphi_lsp-3.6.0.dist-info/entry_points.txt +3 -0
  55. python_delphi_lsp-3.6.0.dist-info/licenses/LICENSE +373 -0
  56. 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
+ }