code-oracle 0.1.0__py3-none-any.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.
- code_oracle/__init__.py +30 -0
- code_oracle/cli.py +795 -0
- code_oracle/config.py +145 -0
- code_oracle/dataset.py +5325 -0
- code_oracle/dead_code/__init__.py +32 -0
- code_oracle/dead_code/detector.py +379 -0
- code_oracle/dead_code/entrypoints.py +333 -0
- code_oracle/dead_code/models.py +255 -0
- code_oracle/dead_code/semantics.py +416 -0
- code_oracle/decision.py +906 -0
- code_oracle/engine.py +430 -0
- code_oracle/export_onnx.py +436 -0
- code_oracle/hook.py +531 -0
- code_oracle/indexer.py +894 -0
- code_oracle/languages/__init__.py +114 -0
- code_oracle/languages/common.py +127 -0
- code_oracle/languages/go.py +395 -0
- code_oracle/languages/python.py +336 -0
- code_oracle/languages/rust.py +474 -0
- code_oracle/languages/typescript.py +775 -0
- code_oracle/linearizer.py +166 -0
- code_oracle/locator.py +301 -0
- code_oracle/models.py +237 -0
- code_oracle/perf_lint/__init__.py +38 -0
- code_oracle/perf_lint/engine.py +234 -0
- code_oracle/perf_lint/models.py +229 -0
- code_oracle/perf_lint/rules/__init__.py +31 -0
- code_oracle/perf_lint/rules/async_blocking.py +143 -0
- code_oracle/perf_lint/rules/n_plus_one.py +232 -0
- code_oracle/perf_lint/rules/nested_loops.py +137 -0
- code_oracle/perf_lint/rules/unclosed_res.py +494 -0
- code_oracle/perf_lint/visitor.py +299 -0
- code_oracle/server.py +184 -0
- code_oracle/slicer.py +225 -0
- code_oracle/symbolic.py +459 -0
- code_oracle-0.1.0.dist-info/METADATA +225 -0
- code_oracle-0.1.0.dist-info/RECORD +40 -0
- code_oracle-0.1.0.dist-info/WHEEL +4 -0
- code_oracle-0.1.0.dist-info/entry_points.txt +2 -0
- code_oracle-0.1.0.dist-info/licenses/LICENSE +190 -0
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Rust AST extractor using Tree-sitter.
|
|
3
|
+
Supports Rust structs, enums, traits, impl blocks, functions, methods, and use/mod declarations.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from typing import List, Optional, Tuple
|
|
7
|
+
from tree_sitter import Language, Node, Parser
|
|
8
|
+
import tree_sitter_rust
|
|
9
|
+
|
|
10
|
+
from code_oracle.languages.common import extract_preceding_docstring, format_syntax_error, get_node_text
|
|
11
|
+
from code_oracle.models import CallReference, ImportReference, Parameter, Symbol
|
|
12
|
+
|
|
13
|
+
_RUST_LANG = Language(tree_sitter_rust.language())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_rust_parser() -> Parser:
|
|
17
|
+
"""Return a Tree-sitter parser configured for Rust."""
|
|
18
|
+
return Parser(_RUST_LANG)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def validate_rust_syntax(source: str, file_path: str = "") -> Optional[str]:
|
|
22
|
+
"""Validate Rust source syntax, returning an error message if invalid."""
|
|
23
|
+
if not source.strip():
|
|
24
|
+
return None
|
|
25
|
+
parser = get_rust_parser()
|
|
26
|
+
tree = parser.parse(source.encode("utf-8"))
|
|
27
|
+
return format_syntax_error(tree.root_node, "Rust")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _extract_rust_parameters(params_node: Node, source_bytes: bytes) -> Tuple[List[Parameter], bool]:
|
|
31
|
+
"""
|
|
32
|
+
Extract parameters from a Rust parameters node.
|
|
33
|
+
Returns: (list_of_parameters, has_self_receiver)
|
|
34
|
+
"""
|
|
35
|
+
params: List[Parameter] = []
|
|
36
|
+
has_self = False
|
|
37
|
+
if not params_node:
|
|
38
|
+
return params, has_self
|
|
39
|
+
|
|
40
|
+
for child in params_node.children:
|
|
41
|
+
if child.type in ("(", ")", ","):
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
if child.type == "self_parameter":
|
|
45
|
+
has_self = True
|
|
46
|
+
params.append(Parameter(name="self", annotation=get_node_text(child, source_bytes)))
|
|
47
|
+
|
|
48
|
+
elif child.type == "parameter":
|
|
49
|
+
name_node = child.child_by_field_name("pattern") or (
|
|
50
|
+
child.children[0] if child.children else None
|
|
51
|
+
)
|
|
52
|
+
param_name = get_node_text(name_node, source_bytes) if name_node else f"arg{len(params)}"
|
|
53
|
+
|
|
54
|
+
type_node = child.child_by_field_name("type") or (
|
|
55
|
+
child.children[-1] if len(child.children) > 1 else None
|
|
56
|
+
)
|
|
57
|
+
type_str = get_node_text(type_node, source_bytes).strip() if type_node else None
|
|
58
|
+
|
|
59
|
+
params.append(
|
|
60
|
+
Parameter(
|
|
61
|
+
name=param_name,
|
|
62
|
+
annotation=type_str,
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
return params, has_self
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _extract_rust_calls(node: Node, source_bytes: bytes, caller_id: Optional[str] = None) -> List[CallReference]:
|
|
70
|
+
"""Recursively extract function and method calls inside a Rust AST node."""
|
|
71
|
+
calls: List[CallReference] = []
|
|
72
|
+
|
|
73
|
+
def walk(n: Node):
|
|
74
|
+
if n.type == "call_expression":
|
|
75
|
+
fn_node = n.child_by_field_name("function")
|
|
76
|
+
callee_name = get_node_text(fn_node, source_bytes).strip() if fn_node else ""
|
|
77
|
+
|
|
78
|
+
args_count = 0
|
|
79
|
+
args_node = n.child_by_field_name("arguments")
|
|
80
|
+
if args_node:
|
|
81
|
+
for arg in args_node.children:
|
|
82
|
+
if arg.type in ("(", ")", ",", "comment", "line_comment", "block_comment") or "comment" in arg.type:
|
|
83
|
+
continue
|
|
84
|
+
args_count += 1
|
|
85
|
+
|
|
86
|
+
if callee_name:
|
|
87
|
+
calls.append(
|
|
88
|
+
CallReference(
|
|
89
|
+
callee=callee_name,
|
|
90
|
+
args_count=args_count,
|
|
91
|
+
kwargs=[],
|
|
92
|
+
lineno=n.start_point.row + 1,
|
|
93
|
+
caller=caller_id,
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
for child in n.children:
|
|
98
|
+
walk(child)
|
|
99
|
+
|
|
100
|
+
walk(node)
|
|
101
|
+
return calls
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def extract_rust_imports(source: str, file_path: str = "") -> List[ImportReference]:
|
|
105
|
+
"""Extract all use declarations and mod declarations from Rust source."""
|
|
106
|
+
if not source.strip():
|
|
107
|
+
return []
|
|
108
|
+
|
|
109
|
+
parser = get_rust_parser()
|
|
110
|
+
source_bytes = source.encode("utf-8")
|
|
111
|
+
tree = parser.parse(source_bytes)
|
|
112
|
+
|
|
113
|
+
imports: List[ImportReference] = []
|
|
114
|
+
|
|
115
|
+
def walk(node: Node):
|
|
116
|
+
if node.type == "use_declaration":
|
|
117
|
+
lineno = node.start_point.row + 1
|
|
118
|
+
for ch in node.children:
|
|
119
|
+
if ch.type in ("scoped_identifier", "identifier"):
|
|
120
|
+
text = get_node_text(ch, source_bytes)
|
|
121
|
+
if "::" in text:
|
|
122
|
+
mod_part, name_part = text.rsplit("::", 1)
|
|
123
|
+
imports.append(
|
|
124
|
+
ImportReference(
|
|
125
|
+
module=mod_part,
|
|
126
|
+
name=name_part,
|
|
127
|
+
lineno=lineno,
|
|
128
|
+
file_path=file_path,
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
else:
|
|
132
|
+
imports.append(
|
|
133
|
+
ImportReference(
|
|
134
|
+
module=None,
|
|
135
|
+
name=text,
|
|
136
|
+
lineno=lineno,
|
|
137
|
+
file_path=file_path,
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
elif ch.type == "scoped_use_list":
|
|
141
|
+
prefix_node = ch.child_by_field_name("path") or (
|
|
142
|
+
ch.children[0] if ch.children else None
|
|
143
|
+
)
|
|
144
|
+
prefix = get_node_text(prefix_node, source_bytes) if prefix_node else ""
|
|
145
|
+
list_node = ch.child_by_field_name("list") or next(
|
|
146
|
+
(c for c in ch.children if c.type == "use_list"), None
|
|
147
|
+
)
|
|
148
|
+
if list_node:
|
|
149
|
+
for item in list_node.children:
|
|
150
|
+
if item.type == "identifier":
|
|
151
|
+
imports.append(
|
|
152
|
+
ImportReference(
|
|
153
|
+
module=prefix,
|
|
154
|
+
name=get_node_text(item, source_bytes),
|
|
155
|
+
lineno=lineno,
|
|
156
|
+
file_path=file_path,
|
|
157
|
+
)
|
|
158
|
+
)
|
|
159
|
+
elif item.type == "use_as_clause":
|
|
160
|
+
path_child = item.child_by_field_name("path") or item.children[0]
|
|
161
|
+
alias_child = item.child_by_field_name("alias") or item.children[-1]
|
|
162
|
+
imports.append(
|
|
163
|
+
ImportReference(
|
|
164
|
+
module=prefix,
|
|
165
|
+
name=get_node_text(path_child, source_bytes),
|
|
166
|
+
asname=get_node_text(alias_child, source_bytes),
|
|
167
|
+
lineno=lineno,
|
|
168
|
+
file_path=file_path,
|
|
169
|
+
)
|
|
170
|
+
)
|
|
171
|
+
elif ch.type == "use_wildcard":
|
|
172
|
+
text = get_node_text(ch, source_bytes)
|
|
173
|
+
mod_part = text.replace("::*", "").strip()
|
|
174
|
+
imports.append(
|
|
175
|
+
ImportReference(
|
|
176
|
+
module=mod_part,
|
|
177
|
+
name="*",
|
|
178
|
+
lineno=lineno,
|
|
179
|
+
file_path=file_path,
|
|
180
|
+
)
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
elif node.type == "mod_item":
|
|
184
|
+
name_node = node.child_by_field_name("name")
|
|
185
|
+
if name_node:
|
|
186
|
+
mod_name = get_node_text(name_node, source_bytes)
|
|
187
|
+
imports.append(
|
|
188
|
+
ImportReference(
|
|
189
|
+
module=None,
|
|
190
|
+
name=mod_name,
|
|
191
|
+
lineno=node.start_point.row + 1,
|
|
192
|
+
file_path=file_path,
|
|
193
|
+
)
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
for child in node.children:
|
|
197
|
+
walk(child)
|
|
198
|
+
|
|
199
|
+
walk(tree.root_node)
|
|
200
|
+
return imports
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _get_rust_visibility(node: Node, source_bytes: bytes) -> Tuple[bool, str]:
|
|
204
|
+
"""Determine if a Rust node has pub visibility."""
|
|
205
|
+
vis_node = node.child_by_field_name("visibility") or next(
|
|
206
|
+
(c for c in node.children if c.type == "visibility_modifier"), None
|
|
207
|
+
)
|
|
208
|
+
if not vis_node:
|
|
209
|
+
return False, "internal"
|
|
210
|
+
vis_text = get_node_text(vis_node, source_bytes)
|
|
211
|
+
if vis_text == "pub":
|
|
212
|
+
return True, "public"
|
|
213
|
+
elif any(k in vis_text for k in ("pub(crate)", "pub(super)", "pub(self)", "pub(in ")):
|
|
214
|
+
return False, "internal"
|
|
215
|
+
return True, "public"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def extract_rust_symbols(source: str, file_path: str = "") -> List[Symbol]:
|
|
219
|
+
"""Parse Rust source into AST and extract symbol entities with detailed metadata."""
|
|
220
|
+
if not source.strip():
|
|
221
|
+
return []
|
|
222
|
+
|
|
223
|
+
parser = get_rust_parser()
|
|
224
|
+
source_bytes = source.encode("utf-8")
|
|
225
|
+
tree = parser.parse(source_bytes)
|
|
226
|
+
|
|
227
|
+
symbols: List[Symbol] = []
|
|
228
|
+
|
|
229
|
+
for child in tree.root_node.children:
|
|
230
|
+
docstring = extract_preceding_docstring(child, source_bytes)
|
|
231
|
+
is_exp, vis = _get_rust_visibility(child, source_bytes)
|
|
232
|
+
|
|
233
|
+
if child.type == "function_item":
|
|
234
|
+
name_node = child.child_by_field_name("name")
|
|
235
|
+
if not name_node:
|
|
236
|
+
continue
|
|
237
|
+
fn_name = get_node_text(name_node, source_bytes)
|
|
238
|
+
sym_id = f"{file_path}::{fn_name}"
|
|
239
|
+
|
|
240
|
+
params_node = child.child_by_field_name("parameters")
|
|
241
|
+
params, _ = _extract_rust_parameters(params_node, source_bytes) if params_node else ([], False)
|
|
242
|
+
|
|
243
|
+
ret_node = child.child_by_field_name("return_type")
|
|
244
|
+
ret_type = get_node_text(ret_node, source_bytes).lstrip("->").strip() if ret_node else None
|
|
245
|
+
|
|
246
|
+
min_args = len(params)
|
|
247
|
+
max_args = len(params)
|
|
248
|
+
|
|
249
|
+
body_node = child.child_by_field_name("body")
|
|
250
|
+
calls = _extract_rust_calls(body_node, source_bytes, caller_id=sym_id) if body_node else []
|
|
251
|
+
|
|
252
|
+
param_strs = [p.name + (f": {p.annotation}" if p.annotation else "") for p in params]
|
|
253
|
+
ret_suffix = f" -> {ret_type}" if ret_type else ""
|
|
254
|
+
signature = f"fn {fn_name}({', '.join(param_strs)}){ret_suffix}"
|
|
255
|
+
|
|
256
|
+
symbols.append(
|
|
257
|
+
Symbol(
|
|
258
|
+
name=fn_name,
|
|
259
|
+
qualname=fn_name,
|
|
260
|
+
file_path=file_path,
|
|
261
|
+
kind="function",
|
|
262
|
+
lineno=child.start_point.row + 1,
|
|
263
|
+
end_lineno=child.end_point.row + 1,
|
|
264
|
+
signature=signature,
|
|
265
|
+
params=params,
|
|
266
|
+
min_args=min_args,
|
|
267
|
+
max_args=max_args,
|
|
268
|
+
return_type=ret_type,
|
|
269
|
+
calls=calls,
|
|
270
|
+
is_method=False,
|
|
271
|
+
is_static=False,
|
|
272
|
+
docstring=docstring,
|
|
273
|
+
is_exported=is_exp,
|
|
274
|
+
visibility=vis,
|
|
275
|
+
)
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
elif child.type == "struct_item":
|
|
279
|
+
name_node = child.child_by_field_name("name")
|
|
280
|
+
if name_node:
|
|
281
|
+
s_name = get_node_text(name_node, source_bytes)
|
|
282
|
+
symbols.append(
|
|
283
|
+
Symbol(
|
|
284
|
+
name=s_name,
|
|
285
|
+
qualname=s_name,
|
|
286
|
+
file_path=file_path,
|
|
287
|
+
kind="struct",
|
|
288
|
+
lineno=child.start_point.row + 1,
|
|
289
|
+
end_lineno=child.end_point.row + 1,
|
|
290
|
+
signature=f"struct {s_name}",
|
|
291
|
+
docstring=docstring,
|
|
292
|
+
is_exported=is_exp,
|
|
293
|
+
visibility=vis,
|
|
294
|
+
)
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
elif child.type == "enum_item":
|
|
298
|
+
name_node = child.child_by_field_name("name")
|
|
299
|
+
if name_node:
|
|
300
|
+
e_name = get_node_text(name_node, source_bytes)
|
|
301
|
+
symbols.append(
|
|
302
|
+
Symbol(
|
|
303
|
+
name=e_name,
|
|
304
|
+
qualname=e_name,
|
|
305
|
+
file_path=file_path,
|
|
306
|
+
kind="enum",
|
|
307
|
+
lineno=child.start_point.row + 1,
|
|
308
|
+
end_lineno=child.end_point.row + 1,
|
|
309
|
+
signature=f"enum {e_name}",
|
|
310
|
+
docstring=docstring,
|
|
311
|
+
is_exported=is_exp,
|
|
312
|
+
visibility=vis,
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
elif child.type == "trait_item":
|
|
317
|
+
name_node = child.child_by_field_name("name")
|
|
318
|
+
if name_node:
|
|
319
|
+
t_name = get_node_text(name_node, source_bytes)
|
|
320
|
+
symbols.append(
|
|
321
|
+
Symbol(
|
|
322
|
+
name=t_name,
|
|
323
|
+
qualname=t_name,
|
|
324
|
+
file_path=file_path,
|
|
325
|
+
kind="interface",
|
|
326
|
+
lineno=child.start_point.row + 1,
|
|
327
|
+
end_lineno=child.end_point.row + 1,
|
|
328
|
+
signature=f"trait {t_name}",
|
|
329
|
+
docstring=docstring,
|
|
330
|
+
is_exported=is_exp,
|
|
331
|
+
visibility=vis,
|
|
332
|
+
)
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
elif child.type == "impl_item":
|
|
336
|
+
type_node = child.child_by_field_name("type")
|
|
337
|
+
trait_node = child.child_by_field_name("trait")
|
|
338
|
+
raw_struct = get_node_text(type_node, source_bytes) if type_node else "Unknown"
|
|
339
|
+
raw_trait = get_node_text(trait_node, source_bytes) if trait_node else None
|
|
340
|
+
# Strip generic parameters and lifetime bounds for clean qualname and inheritance resolution
|
|
341
|
+
struct_name = raw_struct.split("<")[0].strip()
|
|
342
|
+
trait_name = raw_trait.split("<")[0].strip() if raw_trait else None
|
|
343
|
+
|
|
344
|
+
body_node = child.child_by_field_name("body")
|
|
345
|
+
if body_node:
|
|
346
|
+
for item in body_node.children:
|
|
347
|
+
if item.type == "function_item":
|
|
348
|
+
fn_name_node = item.child_by_field_name("name")
|
|
349
|
+
if not fn_name_node:
|
|
350
|
+
continue
|
|
351
|
+
fn_name = get_node_text(fn_name_node, source_bytes)
|
|
352
|
+
qualname = f"{struct_name}.{fn_name}"
|
|
353
|
+
sym_id = f"{file_path}::{qualname}"
|
|
354
|
+
|
|
355
|
+
params_node = item.child_by_field_name("parameters")
|
|
356
|
+
params, has_self = (
|
|
357
|
+
_extract_rust_parameters(params_node, source_bytes)
|
|
358
|
+
if params_node
|
|
359
|
+
else ([], False)
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
ret_node = item.child_by_field_name("return_type")
|
|
363
|
+
ret_type = get_node_text(ret_node, source_bytes).lstrip("->").strip() if ret_node else None
|
|
364
|
+
|
|
365
|
+
is_method = has_self
|
|
366
|
+
is_static = not has_self
|
|
367
|
+
|
|
368
|
+
# For method calls, self is receiver (first param)
|
|
369
|
+
formal_count = len(params) - 1 if is_method else len(params)
|
|
370
|
+
min_args = formal_count
|
|
371
|
+
max_args = formal_count
|
|
372
|
+
|
|
373
|
+
item_body = item.child_by_field_name("body")
|
|
374
|
+
calls = _extract_rust_calls(item_body, source_bytes, caller_id=sym_id) if item_body else []
|
|
375
|
+
|
|
376
|
+
param_strs = [p.name + (f": {p.annotation}" if p.annotation else "") for p in params]
|
|
377
|
+
ret_suffix = f" -> {ret_type}" if ret_type else ""
|
|
378
|
+
signature = f"fn {fn_name}({', '.join(param_strs)}){ret_suffix}"
|
|
379
|
+
|
|
380
|
+
m_doc = extract_preceding_docstring(item, source_bytes)
|
|
381
|
+
m_exp, m_vis = _get_rust_visibility(item, source_bytes)
|
|
382
|
+
|
|
383
|
+
symbols.append(
|
|
384
|
+
Symbol(
|
|
385
|
+
name=fn_name,
|
|
386
|
+
qualname=qualname,
|
|
387
|
+
file_path=file_path,
|
|
388
|
+
kind="method" if is_method else "function",
|
|
389
|
+
lineno=item.start_point.row + 1,
|
|
390
|
+
end_lineno=item.end_point.row + 1,
|
|
391
|
+
signature=signature,
|
|
392
|
+
params=params,
|
|
393
|
+
min_args=min_args,
|
|
394
|
+
max_args=max_args,
|
|
395
|
+
return_type=ret_type,
|
|
396
|
+
calls=calls,
|
|
397
|
+
is_method=is_method,
|
|
398
|
+
is_static=is_static,
|
|
399
|
+
bases=[trait_name] if trait_name else [],
|
|
400
|
+
docstring=m_doc,
|
|
401
|
+
is_exported=m_exp,
|
|
402
|
+
visibility=m_vis,
|
|
403
|
+
)
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
elif child.type in ("const_item", "static_item"):
|
|
407
|
+
is_const = child.type == "const_item"
|
|
408
|
+
kind = "constant" if is_const else "variable"
|
|
409
|
+
name_node = child.child_by_field_name("name") or next(
|
|
410
|
+
(c for c in child.children if c.type == "identifier"), None
|
|
411
|
+
)
|
|
412
|
+
if name_node:
|
|
413
|
+
v_name = get_node_text(name_node, source_bytes)
|
|
414
|
+
symbols.append(
|
|
415
|
+
Symbol(
|
|
416
|
+
name=v_name,
|
|
417
|
+
qualname=v_name,
|
|
418
|
+
file_path=file_path,
|
|
419
|
+
kind=kind,
|
|
420
|
+
lineno=child.start_point.row + 1,
|
|
421
|
+
end_lineno=child.end_point.row + 1,
|
|
422
|
+
signature=f"{'const' if is_const else 'static'} {v_name}",
|
|
423
|
+
min_args=0,
|
|
424
|
+
max_args=0,
|
|
425
|
+
docstring=docstring,
|
|
426
|
+
is_exported=is_exp,
|
|
427
|
+
visibility=vis,
|
|
428
|
+
)
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
elif child.type == "type_item":
|
|
432
|
+
name_node = child.child_by_field_name("name") or next(
|
|
433
|
+
(c for c in child.children if c.type == "type_identifier"), None
|
|
434
|
+
)
|
|
435
|
+
if name_node:
|
|
436
|
+
t_name = get_node_text(name_node, source_bytes)
|
|
437
|
+
symbols.append(
|
|
438
|
+
Symbol(
|
|
439
|
+
name=t_name,
|
|
440
|
+
qualname=t_name,
|
|
441
|
+
file_path=file_path,
|
|
442
|
+
kind="type_alias",
|
|
443
|
+
lineno=child.start_point.row + 1,
|
|
444
|
+
end_lineno=child.end_point.row + 1,
|
|
445
|
+
signature=f"type {t_name}",
|
|
446
|
+
docstring=docstring,
|
|
447
|
+
is_exported=is_exp,
|
|
448
|
+
visibility=vis,
|
|
449
|
+
)
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
# Extract module calls
|
|
453
|
+
all_calls = _extract_rust_calls(tree.root_node, source_bytes, caller_id=f"{file_path}::<module>")
|
|
454
|
+
module_calls = [
|
|
455
|
+
c for c in all_calls
|
|
456
|
+
if not any(s.lineno <= c.lineno <= s.end_lineno for s in symbols if s.kind != "module")
|
|
457
|
+
]
|
|
458
|
+
if module_calls:
|
|
459
|
+
line_count = len(source.splitlines()) or 1
|
|
460
|
+
module_sym = Symbol(
|
|
461
|
+
name="<module>",
|
|
462
|
+
qualname="<module>",
|
|
463
|
+
file_path=file_path,
|
|
464
|
+
kind="module",
|
|
465
|
+
lineno=1,
|
|
466
|
+
end_lineno=line_count,
|
|
467
|
+
signature=f"// module {file_path}",
|
|
468
|
+
calls=module_calls,
|
|
469
|
+
is_exported=True,
|
|
470
|
+
visibility="public",
|
|
471
|
+
)
|
|
472
|
+
symbols.append(module_sym)
|
|
473
|
+
|
|
474
|
+
return symbols
|