codegraphcontext-rust 0.5.5__cp310-cp310-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.
- codegraphcontext/__init__.py +2 -0
- codegraphcontext/__main__.py +5 -0
- codegraphcontext/_cgc_rust.cp310-win_amd64.pyd +0 -0
- codegraphcontext/cli/__init__.py +1 -0
- codegraphcontext/cli/cli_helpers.py +774 -0
- codegraphcontext/cli/config_manager.py +1051 -0
- codegraphcontext/cli/main.py +2386 -0
- codegraphcontext/cli/registry_commands.py +404 -0
- codegraphcontext/cli/setup_macos.py +93 -0
- codegraphcontext/cli/setup_wizard.py +991 -0
- codegraphcontext/cli/visualizer.py +50 -0
- codegraphcontext/core/__init__.py +165 -0
- codegraphcontext/core/bundle_registry.py +181 -0
- codegraphcontext/core/cgc_bundle.py +858 -0
- codegraphcontext/core/cgcignore.py +118 -0
- codegraphcontext/core/database.py +273 -0
- codegraphcontext/core/database_falkordb.py +480 -0
- codegraphcontext/core/database_falkordb_remote.py +199 -0
- codegraphcontext/core/database_kuzu.py +636 -0
- codegraphcontext/core/falkor_worker.py +132 -0
- codegraphcontext/core/jobs.py +132 -0
- codegraphcontext/core/watcher.py +260 -0
- codegraphcontext/prompts.py +125 -0
- codegraphcontext/server.py +471 -0
- codegraphcontext/tool_definitions.py +220 -0
- codegraphcontext/tools/__init__.py +0 -0
- codegraphcontext/tools/advanced_language_query_tool.py +103 -0
- codegraphcontext/tools/code_finder.py +1160 -0
- codegraphcontext/tools/graph_builder.py +337 -0
- codegraphcontext/tools/handlers/analysis_handlers.py +114 -0
- codegraphcontext/tools/handlers/indexing_handlers.py +116 -0
- codegraphcontext/tools/handlers/management_handlers.py +339 -0
- codegraphcontext/tools/handlers/query_handlers.py +83 -0
- codegraphcontext/tools/handlers/watcher_handlers.py +83 -0
- codegraphcontext/tools/indexing/__init__.py +1 -0
- codegraphcontext/tools/indexing/constants.py +25 -0
- codegraphcontext/tools/indexing/discovery.py +64 -0
- codegraphcontext/tools/indexing/engine.py +109 -0
- codegraphcontext/tools/indexing/persistence/__init__.py +3 -0
- codegraphcontext/tools/indexing/persistence/parquet_loader.py +643 -0
- codegraphcontext/tools/indexing/persistence/writer.py +1114 -0
- codegraphcontext/tools/indexing/pipeline.py +171 -0
- codegraphcontext/tools/indexing/pre_scan.py +105 -0
- codegraphcontext/tools/indexing/resolution/__init__.py +8 -0
- codegraphcontext/tools/indexing/resolution/calls.py +253 -0
- codegraphcontext/tools/indexing/resolution/inheritance.py +91 -0
- codegraphcontext/tools/indexing/sanitize.py +41 -0
- codegraphcontext/tools/indexing/schema.py +79 -0
- codegraphcontext/tools/indexing/schema_contract.py +44 -0
- codegraphcontext/tools/indexing/scip_pipeline.py +140 -0
- codegraphcontext/tools/languages/c.py +563 -0
- codegraphcontext/tools/languages/cpp.py +616 -0
- codegraphcontext/tools/languages/csharp.py +551 -0
- codegraphcontext/tools/languages/dart.py +378 -0
- codegraphcontext/tools/languages/elixir.py +460 -0
- codegraphcontext/tools/languages/go.py +499 -0
- codegraphcontext/tools/languages/haskell.py +426 -0
- codegraphcontext/tools/languages/java.py +500 -0
- codegraphcontext/tools/languages/javascript.py +589 -0
- codegraphcontext/tools/languages/kotlin.py +640 -0
- codegraphcontext/tools/languages/perl.py +260 -0
- codegraphcontext/tools/languages/php.py +519 -0
- codegraphcontext/tools/languages/python.py +576 -0
- codegraphcontext/tools/languages/ruby.py +537 -0
- codegraphcontext/tools/languages/rust.py +295 -0
- codegraphcontext/tools/languages/scala.py +519 -0
- codegraphcontext/tools/languages/swift.py +491 -0
- codegraphcontext/tools/languages/typescript.py +575 -0
- codegraphcontext/tools/languages/typescriptjsx.py +151 -0
- codegraphcontext/tools/package_resolver.py +472 -0
- codegraphcontext/tools/query_tool_languages/c_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/cpp_toolkit.py +87 -0
- codegraphcontext/tools/query_tool_languages/csharp_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/dart_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/go_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/haskell_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/java_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/javascript_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/perl_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/python_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/ruby_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/rust_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/scala_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/swift_toolkit.py +5 -0
- codegraphcontext/tools/query_tool_languages/typescript_toolkit.py +5 -0
- codegraphcontext/tools/scip_indexer.py +467 -0
- codegraphcontext/tools/scip_pb2.py +2455 -0
- codegraphcontext/tools/system.py +133 -0
- codegraphcontext/tools/tree_sitter_parser.py +103 -0
- codegraphcontext/utils/debug_log.py +90 -0
- codegraphcontext/utils/path_ignore.py +55 -0
- codegraphcontext/utils/repo_path.py +27 -0
- codegraphcontext/utils/tree_sitter_manager.py +285 -0
- codegraphcontext/utils/visualize_graph.py +0 -0
- codegraphcontext/viz/server.py +282 -0
- codegraphcontext_rust-0.5.5.dist-info/METADATA +537 -0
- codegraphcontext_rust-0.5.5.dist-info/RECORD +101 -0
- codegraphcontext_rust-0.5.5.dist-info/WHEEL +4 -0
- codegraphcontext_rust-0.5.5.dist-info/entry_points.txt +3 -0
- codegraphcontext_rust-0.5.5.dist-info/licenses/LICENSE +21 -0
- codegraphcontext_rust-0.5.5.dist-info/sboms/cgc-python.cyclonedx.json +2333 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Any, Dict, Optional, Tuple
|
|
4
|
+
from codegraphcontext.utils.debug_log import debug_log, info_logger, error_logger, warning_logger
|
|
5
|
+
from codegraphcontext.utils.tree_sitter_manager import execute_query
|
|
6
|
+
|
|
7
|
+
CPP_QUERIES = {
|
|
8
|
+
"functions": """
|
|
9
|
+
(function_definition
|
|
10
|
+
declarator: (function_declarator
|
|
11
|
+
declarator: [
|
|
12
|
+
(identifier) @name
|
|
13
|
+
(field_identifier) @name
|
|
14
|
+
(qualified_identifier) @qualified_name
|
|
15
|
+
]
|
|
16
|
+
)
|
|
17
|
+
) @function_node
|
|
18
|
+
""",
|
|
19
|
+
"classes": """
|
|
20
|
+
(class_specifier
|
|
21
|
+
name: (type_identifier) @name
|
|
22
|
+
) @class
|
|
23
|
+
""",
|
|
24
|
+
"imports": """
|
|
25
|
+
(preproc_include
|
|
26
|
+
path: [
|
|
27
|
+
(string_literal) @path
|
|
28
|
+
(system_lib_string) @path
|
|
29
|
+
]
|
|
30
|
+
) @import
|
|
31
|
+
""",
|
|
32
|
+
"calls": """
|
|
33
|
+
(call_expression
|
|
34
|
+
function: [
|
|
35
|
+
(identifier) @function_name
|
|
36
|
+
(field_expression
|
|
37
|
+
field: (field_identifier) @method_name
|
|
38
|
+
)
|
|
39
|
+
(qualified_identifier) @scoped_name
|
|
40
|
+
]
|
|
41
|
+
arguments: (argument_list) @args
|
|
42
|
+
)
|
|
43
|
+
""",
|
|
44
|
+
"enums":"""
|
|
45
|
+
(enum_specifier
|
|
46
|
+
name: (type_identifier) @name
|
|
47
|
+
body: (enumerator_list
|
|
48
|
+
(enumerator
|
|
49
|
+
name: (identifier) @value
|
|
50
|
+
)*
|
|
51
|
+
)? @body
|
|
52
|
+
) @enum
|
|
53
|
+
""",
|
|
54
|
+
"structs":"""
|
|
55
|
+
(struct_specifier
|
|
56
|
+
name: (type_identifier) @name
|
|
57
|
+
body: (field_declaration_list)? @body
|
|
58
|
+
) @struct
|
|
59
|
+
""",
|
|
60
|
+
"unions": """
|
|
61
|
+
(union_specifier
|
|
62
|
+
name: (type_identifier)? @name
|
|
63
|
+
body: (field_declaration_list
|
|
64
|
+
(field_declaration
|
|
65
|
+
declarator: [
|
|
66
|
+
(field_identifier) @value
|
|
67
|
+
(pointer_declarator (field_identifier) @value)
|
|
68
|
+
(array_declarator (field_identifier) @value)
|
|
69
|
+
]
|
|
70
|
+
)*
|
|
71
|
+
)? @body
|
|
72
|
+
) @union
|
|
73
|
+
|
|
74
|
+
""",
|
|
75
|
+
"macros": """
|
|
76
|
+
(preproc_def
|
|
77
|
+
name: (identifier) @name
|
|
78
|
+
) @macro
|
|
79
|
+
""",
|
|
80
|
+
"variables": """
|
|
81
|
+
(declaration
|
|
82
|
+
declarator: (init_declarator
|
|
83
|
+
declarator: (identifier) @name))
|
|
84
|
+
|
|
85
|
+
(declaration
|
|
86
|
+
declarator: (init_declarator
|
|
87
|
+
declarator: (pointer_declarator
|
|
88
|
+
declarator: (identifier) @name)))
|
|
89
|
+
|
|
90
|
+
(field_declaration
|
|
91
|
+
declarator: [
|
|
92
|
+
(field_identifier) @name
|
|
93
|
+
(pointer_declarator declarator: (field_identifier) @name)
|
|
94
|
+
(array_declarator declarator: (field_identifier) @name)
|
|
95
|
+
(reference_declarator (field_identifier) @name)
|
|
96
|
+
]
|
|
97
|
+
)
|
|
98
|
+
""",
|
|
99
|
+
"lambda_assignments": """
|
|
100
|
+
; Match a lambda assigned to a variable
|
|
101
|
+
(declaration
|
|
102
|
+
declarator: (init_declarator
|
|
103
|
+
declarator: (identifier) @name
|
|
104
|
+
value: (lambda_expression) @lambda_node))
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class CppTreeSitterParser:
|
|
110
|
+
"""A C++-specific parser using tree-sitter."""
|
|
111
|
+
|
|
112
|
+
def __init__(self, generic_parser_wrapper):
|
|
113
|
+
self.generic_parser_wrapper = generic_parser_wrapper
|
|
114
|
+
self.language_name = "cpp"
|
|
115
|
+
self.language = generic_parser_wrapper.language
|
|
116
|
+
self.parser = generic_parser_wrapper.parser
|
|
117
|
+
|
|
118
|
+
def _get_node_text(self, node) -> str:
|
|
119
|
+
return node.text.decode('utf-8')
|
|
120
|
+
|
|
121
|
+
def parse(self, path: Path, is_dependency: bool = False, index_source: bool = False, **kwargs) -> Dict:
|
|
122
|
+
"""Parses a C++ file and returns its structure."""
|
|
123
|
+
self.index_source = index_source
|
|
124
|
+
with open(path, "r", encoding="utf-8", errors="ignore") as f:
|
|
125
|
+
source_code = f.read()
|
|
126
|
+
|
|
127
|
+
tree = self.parser.parse(bytes(source_code, "utf8"))
|
|
128
|
+
root_node = tree.root_node
|
|
129
|
+
|
|
130
|
+
functions = self._find_functions(root_node)
|
|
131
|
+
functions.extend(self._find_lambda_assignments(root_node))
|
|
132
|
+
function_calls = self._find_calls(root_node)
|
|
133
|
+
classes = self._find_classes(root_node)
|
|
134
|
+
imports = self._find_imports(root_node)
|
|
135
|
+
structs = self._find_structs(root_node)
|
|
136
|
+
enums = self._find_enums(root_node)
|
|
137
|
+
unions = self._find_unions(root_node)
|
|
138
|
+
macros = self._find_macros(root_node)
|
|
139
|
+
variables = self._find_variables(root_node)
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
"path": str(path),
|
|
143
|
+
"functions": functions,
|
|
144
|
+
"classes": classes,
|
|
145
|
+
"structs": structs,
|
|
146
|
+
"enums": enums,
|
|
147
|
+
"unions": unions,
|
|
148
|
+
"macros": macros,
|
|
149
|
+
"variables": variables,
|
|
150
|
+
"declarations": [], # Placeholder
|
|
151
|
+
"imports": imports,
|
|
152
|
+
"function_calls": function_calls,
|
|
153
|
+
"is_dependency": is_dependency,
|
|
154
|
+
"lang": self.language_name,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
def _find_functions(self, root_node):
|
|
158
|
+
functions = []
|
|
159
|
+
query_str = CPP_QUERIES['functions']
|
|
160
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
161
|
+
capture_name = match[1]
|
|
162
|
+
node = match[0]
|
|
163
|
+
if capture_name in ('name', 'qualified_name'):
|
|
164
|
+
# Find the enclosing function_definition node
|
|
165
|
+
func_node = node.parent
|
|
166
|
+
while func_node and func_node.type != 'function_definition':
|
|
167
|
+
func_node = func_node.parent
|
|
168
|
+
|
|
169
|
+
if not func_node: continue
|
|
170
|
+
|
|
171
|
+
# For qualified names like QueueElement::execute, extract
|
|
172
|
+
# both the class context and the method name
|
|
173
|
+
raw_text = self._get_node_text(node)
|
|
174
|
+
class_context = None
|
|
175
|
+
if capture_name == 'qualified_name' and '::' in raw_text:
|
|
176
|
+
parts = raw_text.rsplit('::', 1)
|
|
177
|
+
class_context = parts[0]
|
|
178
|
+
name = parts[1]
|
|
179
|
+
else:
|
|
180
|
+
name = raw_text
|
|
181
|
+
|
|
182
|
+
params = self._extract_function_params(func_node)
|
|
183
|
+
|
|
184
|
+
func_data = {
|
|
185
|
+
"name": name,
|
|
186
|
+
"line_number": node.start_point[0] + 1,
|
|
187
|
+
"end_line": func_node.end_point[0] + 1,
|
|
188
|
+
"args": params,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if class_context:
|
|
192
|
+
func_data["class_context"] = class_context
|
|
193
|
+
|
|
194
|
+
if self.index_source:
|
|
195
|
+
func_data["source"] = self._get_node_text(func_node)
|
|
196
|
+
|
|
197
|
+
functions.append(func_data)
|
|
198
|
+
return functions
|
|
199
|
+
|
|
200
|
+
def _extract_function_params(self, func_node) -> list[str]:
|
|
201
|
+
params = []
|
|
202
|
+
declarator_node = func_node.child_by_field_name('declarator')
|
|
203
|
+
if not declarator_node:
|
|
204
|
+
return []
|
|
205
|
+
|
|
206
|
+
parameters_node = declarator_node.child_by_field_name('parameters')
|
|
207
|
+
if not parameters_node or parameters_node.type != 'parameter_list':
|
|
208
|
+
return []
|
|
209
|
+
|
|
210
|
+
for param in parameters_node.children:
|
|
211
|
+
if param.type == 'parameter_declaration':
|
|
212
|
+
# Extract name
|
|
213
|
+
param_decl = param.child_by_field_name('declarator')
|
|
214
|
+
# Unwrap pointers/refs to find identifier
|
|
215
|
+
while param_decl and param_decl.type not in ('identifier', 'field_identifier', 'type_identifier'):
|
|
216
|
+
child = param_decl.child_by_field_name('declarator')
|
|
217
|
+
if child:
|
|
218
|
+
param_decl = child
|
|
219
|
+
else:
|
|
220
|
+
break
|
|
221
|
+
|
|
222
|
+
name = self._get_node_text(param_decl) if param_decl else ""
|
|
223
|
+
|
|
224
|
+
# Extract type
|
|
225
|
+
param_type_node = param.child_by_field_name('type')
|
|
226
|
+
type_str = self._get_node_text(param_type_node) if param_type_node else ""
|
|
227
|
+
|
|
228
|
+
if name:
|
|
229
|
+
# Storing "type name" string, or just name?
|
|
230
|
+
# Standard in this project seems to be list of strings.
|
|
231
|
+
# Given C++ complexity, providing "type name" provides more info.
|
|
232
|
+
if type_str:
|
|
233
|
+
params.append(f"{type_str} {name}")
|
|
234
|
+
else:
|
|
235
|
+
params.append(name)
|
|
236
|
+
return params
|
|
237
|
+
|
|
238
|
+
def _find_classes(self, root_node):
|
|
239
|
+
classes = []
|
|
240
|
+
query_str = CPP_QUERIES['classes']
|
|
241
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
242
|
+
capture_name = match[1]
|
|
243
|
+
node = match[0]
|
|
244
|
+
if capture_name == 'name':
|
|
245
|
+
class_node = node.parent
|
|
246
|
+
name = self._get_node_text(node)
|
|
247
|
+
bases = self._extract_base_classes(class_node)
|
|
248
|
+
class_data = {
|
|
249
|
+
"name": name,
|
|
250
|
+
"line_number": node.start_point[0] + 1,
|
|
251
|
+
"end_line": class_node.end_point[0] + 1,
|
|
252
|
+
"bases": bases,
|
|
253
|
+
}
|
|
254
|
+
if self.index_source:
|
|
255
|
+
class_data["source"] = self._get_node_text(class_node)
|
|
256
|
+
classes.append(class_data)
|
|
257
|
+
return classes
|
|
258
|
+
|
|
259
|
+
def _extract_base_classes(self, class_node) -> list[str]:
|
|
260
|
+
"""Extract base class names from a class_specifier node.
|
|
261
|
+
|
|
262
|
+
Handles: class Foo : public Bar, private Baz, virtual Base
|
|
263
|
+
Tree-sitter AST: class_specifier -> base_class_clause -> (base_class_specifier)+
|
|
264
|
+
Each base_class_specifier has an optional access_specifier and a type node.
|
|
265
|
+
"""
|
|
266
|
+
bases = []
|
|
267
|
+
for child in class_node.children:
|
|
268
|
+
if child.type == 'base_class_clause':
|
|
269
|
+
for base_spec in child.children:
|
|
270
|
+
if base_spec.type in ('base_class_specifier', 'type_identifier',
|
|
271
|
+
'qualified_identifier', 'template_type'):
|
|
272
|
+
# base_class_specifier contains access specifier + type
|
|
273
|
+
if base_spec.type == 'base_class_specifier':
|
|
274
|
+
# Find the type identifier within the base specifier
|
|
275
|
+
for sub in base_spec.children:
|
|
276
|
+
if sub.type in ('type_identifier', 'qualified_identifier',
|
|
277
|
+
'template_type'):
|
|
278
|
+
base_name = self._get_node_text(sub)
|
|
279
|
+
# Strip template args for graph matching
|
|
280
|
+
if '<' in base_name:
|
|
281
|
+
base_name = base_name[:base_name.index('<')].strip()
|
|
282
|
+
bases.append(base_name)
|
|
283
|
+
break
|
|
284
|
+
else:
|
|
285
|
+
# Direct type node (no access specifier)
|
|
286
|
+
base_name = self._get_node_text(base_spec)
|
|
287
|
+
if '<' in base_name:
|
|
288
|
+
base_name = base_name[:base_name.index('<')].strip()
|
|
289
|
+
bases.append(base_name)
|
|
290
|
+
break # Only one base_class_clause per class
|
|
291
|
+
return bases
|
|
292
|
+
|
|
293
|
+
def _find_imports(self, root_node):
|
|
294
|
+
imports = []
|
|
295
|
+
query_str = CPP_QUERIES['imports']
|
|
296
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
297
|
+
capture_name = match[1]
|
|
298
|
+
node = match[0]
|
|
299
|
+
if capture_name == 'path':
|
|
300
|
+
path = self._get_node_text(node).strip('<>')
|
|
301
|
+
imports.append({
|
|
302
|
+
"name": path,
|
|
303
|
+
"full_import_name": path,
|
|
304
|
+
"line_number": node.start_point[0] + 1,
|
|
305
|
+
"alias": None,
|
|
306
|
+
})
|
|
307
|
+
return imports
|
|
308
|
+
|
|
309
|
+
def _find_enums(self, root_node):
|
|
310
|
+
enums = []
|
|
311
|
+
query_str = CPP_QUERIES['enums']
|
|
312
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
313
|
+
if capture_name == 'name':
|
|
314
|
+
name = self._get_node_text(node)
|
|
315
|
+
enum_node = node.parent
|
|
316
|
+
enum_data = {
|
|
317
|
+
"name": name,
|
|
318
|
+
"line_number": node.start_point[0] + 1,
|
|
319
|
+
"end_line": enum_node.end_point[0] + 1,
|
|
320
|
+
}
|
|
321
|
+
if self.index_source:
|
|
322
|
+
enum_data["source"] = self._get_node_text(enum_node)
|
|
323
|
+
enums.append(enum_data)
|
|
324
|
+
return enums
|
|
325
|
+
|
|
326
|
+
def _find_structs(self, root_node):
|
|
327
|
+
structs = []
|
|
328
|
+
query_str = CPP_QUERIES['structs']
|
|
329
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
330
|
+
if capture_name == 'name':
|
|
331
|
+
name = self._get_node_text(node)
|
|
332
|
+
struct_node = node.parent
|
|
333
|
+
struct_data = {
|
|
334
|
+
"name": name,
|
|
335
|
+
"line_number": node.start_point[0] + 1,
|
|
336
|
+
"end_line": struct_node.end_point[0] + 1,
|
|
337
|
+
}
|
|
338
|
+
if self.index_source:
|
|
339
|
+
struct_data["source"] = self._get_node_text(struct_node)
|
|
340
|
+
structs.append(struct_data)
|
|
341
|
+
return structs
|
|
342
|
+
|
|
343
|
+
def _find_unions(self, root_node):
|
|
344
|
+
unions = []
|
|
345
|
+
query_str = CPP_QUERIES['unions']
|
|
346
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
347
|
+
if capture_name == 'name':
|
|
348
|
+
name = self._get_node_text(node)
|
|
349
|
+
union_node = node.parent
|
|
350
|
+
union_data = {
|
|
351
|
+
"name": name,
|
|
352
|
+
"line_number": node.start_point[0] + 1,
|
|
353
|
+
"end_line": union_node.end_point[0] + 1,
|
|
354
|
+
}
|
|
355
|
+
if self.index_source:
|
|
356
|
+
union_data["source"] = self._get_node_text(union_node)
|
|
357
|
+
unions.append(union_data)
|
|
358
|
+
return unions
|
|
359
|
+
|
|
360
|
+
def _find_macros(self, root_node):
|
|
361
|
+
macros = []
|
|
362
|
+
query_str = CPP_QUERIES['macros']
|
|
363
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
364
|
+
capture_name = match[1]
|
|
365
|
+
node = match[0]
|
|
366
|
+
if capture_name == 'name':
|
|
367
|
+
macro_node = node.parent
|
|
368
|
+
name = self._get_node_text(node)
|
|
369
|
+
macro_data = {
|
|
370
|
+
"name": name,
|
|
371
|
+
"line_number": node.start_point[0] + 1,
|
|
372
|
+
"end_line": macro_node.end_point[0] + 1,
|
|
373
|
+
}
|
|
374
|
+
if self.index_source:
|
|
375
|
+
macro_data["source"] = self._get_node_text(macro_node)
|
|
376
|
+
macros.append(macro_data)
|
|
377
|
+
return macros
|
|
378
|
+
|
|
379
|
+
def _find_lambda_assignments(self, root_node):
|
|
380
|
+
functions = []
|
|
381
|
+
query_str = CPP_QUERIES.get('lambda_assignments')
|
|
382
|
+
if not query_str: return []
|
|
383
|
+
|
|
384
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
385
|
+
capture_name = match[1]
|
|
386
|
+
node = match[0]
|
|
387
|
+
|
|
388
|
+
if capture_name == 'name':
|
|
389
|
+
assignment_node = node.parent
|
|
390
|
+
lambda_node = assignment_node.child_by_field_name('value')
|
|
391
|
+
if lambda_node is None or lambda_node.type != 'lambda_expression':
|
|
392
|
+
continue
|
|
393
|
+
|
|
394
|
+
params_node = lambda_node.child_by_field_name('declarator')
|
|
395
|
+
if params_node:
|
|
396
|
+
params_node = params_node.child_by_field_name('parameters')
|
|
397
|
+
name = self._get_node_text(node)
|
|
398
|
+
params_node = lambda_node.child_by_field_name('parameters')
|
|
399
|
+
|
|
400
|
+
context, context_type, _ = self._get_parent_context(assignment_node)
|
|
401
|
+
class_context, _, _ = self._get_parent_context(assignment_node, types=('class_specifier',))
|
|
402
|
+
|
|
403
|
+
func_data = {
|
|
404
|
+
"name": name,
|
|
405
|
+
"line_number": node.start_point[0] + 1,
|
|
406
|
+
"end_line": assignment_node.end_point[0] + 1,
|
|
407
|
+
"args": [p for p in [self._get_node_text(p) for p in params_node.children if p.type == 'identifier'] if p] if params_node else [],
|
|
408
|
+
|
|
409
|
+
"docstring": None,
|
|
410
|
+
"cyclomatic_complexity": 1,
|
|
411
|
+
"context": context,
|
|
412
|
+
"context_type": context_type,
|
|
413
|
+
"class_context": class_context,
|
|
414
|
+
"decorators": [],
|
|
415
|
+
"lang": self.language_name,
|
|
416
|
+
"is_dependency": False,
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if self.index_source:
|
|
420
|
+
func_data["source"] = self._get_node_text(assignment_node)
|
|
421
|
+
|
|
422
|
+
functions.append(func_data)
|
|
423
|
+
return functions
|
|
424
|
+
|
|
425
|
+
def _find_variables(self, root_node):
|
|
426
|
+
variables = []
|
|
427
|
+
query_str = CPP_QUERIES['variables']
|
|
428
|
+
for match in execute_query(self.language, query_str, root_node):
|
|
429
|
+
capture_name = match[1]
|
|
430
|
+
node = match[0]
|
|
431
|
+
|
|
432
|
+
if capture_name == 'name':
|
|
433
|
+
assignment_node = node.parent
|
|
434
|
+
|
|
435
|
+
# Skip lambda assignments, they are handled by _find_lambda_assignments
|
|
436
|
+
right_node = assignment_node.child_by_field_name('value')
|
|
437
|
+
if right_node and right_node.type == 'lambda_expression':
|
|
438
|
+
continue
|
|
439
|
+
|
|
440
|
+
name = self._get_node_text(node)
|
|
441
|
+
value = self._get_node_text(right_node) if right_node else None
|
|
442
|
+
|
|
443
|
+
type_node = assignment_node.child_by_field_name('type')
|
|
444
|
+
type_text = self._get_node_text(type_node) if type_node else None
|
|
445
|
+
|
|
446
|
+
context, _, _ = self._get_parent_context(node)
|
|
447
|
+
class_context, _, _ = self._get_parent_context(node, types=('class_specifier',))
|
|
448
|
+
|
|
449
|
+
variable_data = {
|
|
450
|
+
"name": name,
|
|
451
|
+
"line_number": node.start_point[0] + 1,
|
|
452
|
+
"value": value,
|
|
453
|
+
"type": type_text,
|
|
454
|
+
"context": context,
|
|
455
|
+
"class_context": class_context,
|
|
456
|
+
"lang": self.language_name,
|
|
457
|
+
"is_dependency": False,
|
|
458
|
+
}
|
|
459
|
+
variables.append(variable_data)
|
|
460
|
+
return variables
|
|
461
|
+
|
|
462
|
+
def _get_parent_context(self, node, types=('function_definition', 'class_specifier')):
|
|
463
|
+
curr = node.parent
|
|
464
|
+
while curr:
|
|
465
|
+
if curr.type in types:
|
|
466
|
+
if curr.type == 'function_definition':
|
|
467
|
+
# Traverse declarator to find name, handling qualified names
|
|
468
|
+
decl = curr.child_by_field_name('declarator')
|
|
469
|
+
while decl:
|
|
470
|
+
if decl.type == 'identifier':
|
|
471
|
+
return self._get_node_text(decl), curr.type, decl.start_point[0] + 1
|
|
472
|
+
if decl.type == 'qualified_identifier':
|
|
473
|
+
# e.g. QueueElement::execute — return just the method name
|
|
474
|
+
text = self._get_node_text(decl)
|
|
475
|
+
name = text.rsplit('::', 1)[-1] if '::' in text else text
|
|
476
|
+
return name, curr.type, decl.start_point[0] + 1
|
|
477
|
+
if decl.type == 'field_identifier':
|
|
478
|
+
return self._get_node_text(decl), curr.type, decl.start_point[0] + 1
|
|
479
|
+
|
|
480
|
+
child = decl.child_by_field_name('declarator')
|
|
481
|
+
if child:
|
|
482
|
+
decl = child
|
|
483
|
+
else:
|
|
484
|
+
break
|
|
485
|
+
# Fallback or if not found
|
|
486
|
+
return None, curr.type, curr.start_point[0] + 1
|
|
487
|
+
elif curr.type == 'class_specifier':
|
|
488
|
+
name_node = curr.child_by_field_name('name')
|
|
489
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
490
|
+
else:
|
|
491
|
+
name_node = curr.child_by_field_name('name')
|
|
492
|
+
return self._get_node_text(name_node) if name_node else None, curr.type, curr.start_point[0] + 1
|
|
493
|
+
curr = curr.parent
|
|
494
|
+
return None, None, None
|
|
495
|
+
|
|
496
|
+
def _find_calls(self, root_node):
|
|
497
|
+
calls = []
|
|
498
|
+
query_str = CPP_QUERIES['calls']
|
|
499
|
+
for node, capture_name in execute_query(self.language, query_str, root_node):
|
|
500
|
+
if capture_name in ("function_name", "method_name", "scoped_name"):
|
|
501
|
+
raw_text = self._get_node_text(node)
|
|
502
|
+
|
|
503
|
+
# Determine the called function name and any object/class qualifier
|
|
504
|
+
inferred_obj_type = None
|
|
505
|
+
if capture_name == "scoped_name" and '::' in raw_text:
|
|
506
|
+
# e.g. QueueElement::setStatus or std::move
|
|
507
|
+
parts = raw_text.rsplit('::', 1)
|
|
508
|
+
func_name = parts[1]
|
|
509
|
+
inferred_obj_type = parts[0]
|
|
510
|
+
elif capture_name == "method_name":
|
|
511
|
+
# e.g. obj.method() or ptr->method() — field_expression
|
|
512
|
+
func_name = raw_text
|
|
513
|
+
# Try to get the object expression for type inference
|
|
514
|
+
field_expr = node.parent # field_expression node
|
|
515
|
+
if field_expr and field_expr.type == 'field_expression':
|
|
516
|
+
obj_node = field_expr.child_by_field_name('argument')
|
|
517
|
+
if obj_node:
|
|
518
|
+
obj_text = self._get_node_text(obj_node)
|
|
519
|
+
# Treat this->method like self.method for resolution
|
|
520
|
+
if obj_text == 'this':
|
|
521
|
+
inferred_obj_type = 'this'
|
|
522
|
+
else:
|
|
523
|
+
inferred_obj_type = obj_text
|
|
524
|
+
else:
|
|
525
|
+
func_name = raw_text
|
|
526
|
+
|
|
527
|
+
# Get context info (which function/class contains this call)
|
|
528
|
+
context_name, context_type, context_line = self._get_parent_context(node)
|
|
529
|
+
class_context, _, _ = self._get_parent_context(node, types=("class_specifier",))
|
|
530
|
+
|
|
531
|
+
call_data = {
|
|
532
|
+
"name": func_name,
|
|
533
|
+
"full_name": raw_text,
|
|
534
|
+
"line_number": node.start_point[0] + 1,
|
|
535
|
+
"args": [],
|
|
536
|
+
"inferred_obj_type": inferred_obj_type,
|
|
537
|
+
"context": (context_name, context_type, context_line),
|
|
538
|
+
"class_context": class_context,
|
|
539
|
+
"lang": self.language_name,
|
|
540
|
+
"is_dependency": False,
|
|
541
|
+
}
|
|
542
|
+
calls.append(call_data)
|
|
543
|
+
return calls
|
|
544
|
+
|
|
545
|
+
def _get_full_name(self, node):
|
|
546
|
+
"Builds a fully qualified name for a function or call node."
|
|
547
|
+
|
|
548
|
+
name_parts = []
|
|
549
|
+
|
|
550
|
+
# Move upward and collect parent scopes
|
|
551
|
+
curr = node
|
|
552
|
+
while curr:
|
|
553
|
+
if curr.type in ("function_definition", "function_declarator"):
|
|
554
|
+
id_node = curr.child_by_field_name("declarator")
|
|
555
|
+
if id_node:
|
|
556
|
+
if id_node.type == "identifier":
|
|
557
|
+
name_parts.insert(0, id_node.text.decode("utf8"))
|
|
558
|
+
elif id_node.type == "qualified_identifier":
|
|
559
|
+
# Already contains Class::method — use it directly
|
|
560
|
+
return id_node.text.decode("utf8")
|
|
561
|
+
elif curr.type == "class_specifier":
|
|
562
|
+
name_node = curr.child_by_field_name("name")
|
|
563
|
+
if name_node:
|
|
564
|
+
name_parts.insert(0, name_node.text.decode("utf8"))
|
|
565
|
+
elif curr.type == "namespace_definition":
|
|
566
|
+
name_node = curr.child_by_field_name("name")
|
|
567
|
+
if name_node:
|
|
568
|
+
name_parts.insert(0, name_node.text.decode("utf8"))
|
|
569
|
+
curr = curr.parent
|
|
570
|
+
|
|
571
|
+
return "::".join(name_parts) if name_parts else None
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def pre_scan_cpp(files: list[Path], parser_wrapper) -> dict:
|
|
575
|
+
"""
|
|
576
|
+
Quickly scans C++ files to build a map of top-level class, struct, and function names
|
|
577
|
+
to their file paths.
|
|
578
|
+
"""
|
|
579
|
+
imports_map = {}
|
|
580
|
+
|
|
581
|
+
query_str = """
|
|
582
|
+
(class_specifier name: (type_identifier) @name)
|
|
583
|
+
(struct_specifier name: (type_identifier) @name)
|
|
584
|
+
(function_definition declarator: (function_declarator declarator: (identifier) @name))
|
|
585
|
+
(function_definition declarator: (function_declarator declarator: (qualified_identifier) @qualified_name))
|
|
586
|
+
"""
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
for path in files:
|
|
590
|
+
try:
|
|
591
|
+
with open(path, "r", encoding="utf-8", errors="ignore") as f:
|
|
592
|
+
source_bytes = f.read().encode("utf-8")
|
|
593
|
+
tree = parser_wrapper.parser.parse(source_bytes)
|
|
594
|
+
|
|
595
|
+
for node, capture_name in execute_query(parser_wrapper.language, query_str, tree.root_node):
|
|
596
|
+
resolved_path = str(path.resolve())
|
|
597
|
+
if capture_name == "name":
|
|
598
|
+
name = node.text.decode("utf-8")
|
|
599
|
+
paths = imports_map.setdefault(name, [])
|
|
600
|
+
if resolved_path not in paths:
|
|
601
|
+
paths.append(resolved_path)
|
|
602
|
+
elif capture_name == "qualified_name":
|
|
603
|
+
# e.g. QueueElement::execute — index both the full name and the method name
|
|
604
|
+
full_name = node.text.decode("utf-8")
|
|
605
|
+
paths = imports_map.setdefault(full_name, [])
|
|
606
|
+
if resolved_path not in paths:
|
|
607
|
+
paths.append(resolved_path)
|
|
608
|
+
if '::' in full_name:
|
|
609
|
+
method_name = full_name.rsplit('::', 1)[1]
|
|
610
|
+
paths = imports_map.setdefault(method_name, [])
|
|
611
|
+
if resolved_path not in paths:
|
|
612
|
+
paths.append(resolved_path)
|
|
613
|
+
except Exception as e:
|
|
614
|
+
warning_logger(f"Tree-sitter pre-scan failed for {path}: {e}")
|
|
615
|
+
|
|
616
|
+
return imports_map
|