1use crate::core::semantic::analyzer::{AnalysisResult, FunctionCall, Import, TypeReference};
7use crate::utils::error::ContextCreatorError;
8use std::collections::HashMap;
9use tree_sitter::{Language, Parser, Query, QueryCursor, Tree};
10
11pub struct QueryEngine {
13 #[allow(dead_code)]
14 language: Language,
15 #[allow(dead_code)]
16 language_name: String,
17 import_query: Query,
18 function_call_query: Query,
19 type_reference_query: Query,
20}
21
22impl QueryEngine {
23 pub fn new(language: Language, language_name: &str) -> Result<Self, ContextCreatorError> {
25 let import_query = Self::create_import_query(language, language_name)?;
26 let function_call_query = Self::create_function_call_query(language, language_name)?;
27 let type_reference_query = Self::create_type_reference_query(language, language_name)?;
28
29 Ok(Self {
30 language,
31 language_name: language_name.to_string(),
32 import_query,
33 function_call_query,
34 type_reference_query,
35 })
36 }
37
38 pub fn analyze_with_parser(
40 &self,
41 parser: &mut Parser,
42 content: &str,
43 ) -> Result<AnalysisResult, ContextCreatorError> {
44 let tree = parser.parse(content, None).ok_or_else(|| {
46 ContextCreatorError::ParseError("Failed to parse content".to_string())
47 })?;
48
49 self.analyze_tree(&tree, content)
50 }
51
52 pub fn analyze_tree(
54 &self,
55 tree: &Tree,
56 content: &str,
57 ) -> Result<AnalysisResult, ContextCreatorError> {
58 let mut result = AnalysisResult::default();
59 let mut query_cursor = QueryCursor::new();
60 let root_node = tree.root_node();
61
62 let import_matches =
64 query_cursor.matches(&self.import_query, root_node, content.as_bytes());
65 result.imports = self.extract_imports(import_matches, content)?;
66
67 let call_matches =
69 query_cursor.matches(&self.function_call_query, root_node, content.as_bytes());
70 result.function_calls = self.extract_function_calls(call_matches, content)?;
71
72 let type_matches =
74 query_cursor.matches(&self.type_reference_query, root_node, content.as_bytes());
75 result.type_references = self.extract_type_references(type_matches, content)?;
76
77 Ok(result)
78 }
79
80 fn create_import_query(
82 language: Language,
83 language_name: &str,
84 ) -> Result<Query, ContextCreatorError> {
85 let query_text = match language_name {
86 "rust" => {
87 r#"
88 ; Use declarations
89 (use_declaration) @rust_import
90
91 ; Module declarations
92 (mod_item
93 name: (identifier) @mod_name
94 ) @rust_module
95
96 ; Extern crate declarations
97 (extern_crate_declaration
98 name: (identifier) @crate_name
99 ) @extern_crate
100 "#
101 }
102 "python" => {
103 r#"
104 ; Simple import statements (import os, import sys)
105 (import_statement
106 (dotted_name) @module_name
107 ) @simple_import
108
109 ; From import statements with absolute modules (from pathlib import Path)
110 (import_from_statement
111 module_name: (dotted_name) @from_module
112 (dotted_name) @import_item
113 ) @from_import
114
115 ; From import with aliased imports
116 (import_from_statement
117 module_name: (dotted_name) @from_module
118 (aliased_import
119 name: (dotted_name) @import_item
120 )
121 ) @from_import_aliased
122
123 ; Relative from imports (from . import utils, from ..lib import helper)
124 (import_from_statement
125 module_name: (relative_import) @relative_module
126 (dotted_name) @import_item
127 ) @relative_from_import
128
129 ; Relative from imports with aliased imports
130 (import_from_statement
131 module_name: (relative_import) @relative_module
132 (aliased_import
133 name: (dotted_name) @import_item
134 )
135 ) @relative_from_import_aliased
136 "#
137 }
138 "javascript" => {
139 r#"
140 ; Import declarations
141 (import_statement
142 (import_clause
143 [
144 (identifier) @import_name
145 (namespace_import (identifier) @import_name)
146 (named_imports
147 (import_specifier
148 [
149 (identifier) @import_name
150 name: (identifier) @import_name
151 ]
152 )
153 )
154 ]
155 )?
156 source: (string) @module_path
157 ) @js_import
158
159 ; Require calls (CommonJS)
160 (call_expression
161 function: (identifier) @require_fn (#eq? @require_fn "require")
162 arguments: (arguments (string) @module_path)
163 ) @require
164 "#
165 }
166 "typescript" => {
167 r#"
168 ; Import declarations
169 (import_statement
170 (import_clause
171 [
172 (identifier) @import_name
173 (namespace_import (identifier) @import_name)
174 (named_imports
175 (import_specifier
176 [
177 (identifier) @import_name
178 name: (identifier) @import_name
179 ]
180 )
181 )
182 ]
183 )?
184 source: (string) @module_path
185 ) @ts_import
186
187 ; Require calls (CommonJS)
188 (call_expression
189 function: (identifier) @require_fn (#eq? @require_fn "require")
190 arguments: (arguments (string) @module_path)
191 ) @require
192 "#
193 }
194 _ => {
195 return Err(ContextCreatorError::ParseError(format!(
196 "Unsupported language for import queries: {language_name}"
197 )))
198 }
199 };
200
201 Query::new(language, query_text).map_err(|e| {
202 ContextCreatorError::ParseError(format!("Failed to create import query: {e}"))
203 })
204 }
205
206 fn create_function_call_query(
208 language: Language,
209 language_name: &str,
210 ) -> Result<Query, ContextCreatorError> {
211 let query_text = match language_name {
212 "rust" => {
213 r#"
214 ; Simple function calls (helper)
215 (call_expression
216 function: (identifier) @fn_name
217 ) @call
218
219 ; Scoped function calls (lib::greet)
220 (call_expression
221 function: (scoped_identifier
222 path: (identifier) @module_name
223 name: (identifier) @fn_name
224 )
225 ) @scoped_call
226
227 ; Nested scoped function calls (lib::User::new)
228 (call_expression
229 function: (scoped_identifier
230 path: (scoped_identifier
231 path: (identifier) @module_name
232 name: (identifier) @type_name
233 )
234 name: (identifier) @fn_name
235 )
236 ) @nested_scoped_call
237
238 ; Method calls (obj.method())
239 (call_expression
240 function: (field_expression
241 field: (field_identifier) @method_name
242 )
243 ) @method_call
244
245 ; Macro calls (println!)
246 (macro_invocation
247 macro: (identifier) @macro_name
248 ) @macro_call
249 "#
250 }
251 "python" => {
252 r#"
253 ; Simple function calls (print, len)
254 (call
255 function: (identifier) @fn_name
256 ) @call
257
258 ; Module attribute calls (os.path, module.func)
259 (call
260 function: (attribute
261 object: (identifier) @module_name
262 attribute: (identifier) @fn_name
263 )
264 ) @module_call
265
266 ; Nested attribute calls (os.path.join)
267 (call
268 function: (attribute
269 attribute: (identifier) @fn_name
270 )
271 ) @nested_call
272 "#
273 }
274 "javascript" => {
275 r#"
276 ; Function calls
277 (call_expression
278 function: [
279 (identifier) @fn_name
280 (member_expression
281 object: (identifier) @module_name
282 property: (property_identifier) @fn_name
283 )
284 ]
285 ) @call
286 "#
287 }
288 "typescript" => {
289 r#"
290 ; Function calls
291 (call_expression
292 function: [
293 (identifier) @fn_name
294 (member_expression
295 object: (identifier) @module_name
296 property: (property_identifier) @fn_name
297 )
298 ]
299 ) @call
300 "#
301 }
302 _ => {
303 return Err(ContextCreatorError::ParseError(format!(
304 "Unsupported language for function call queries: {language_name}"
305 )))
306 }
307 };
308
309 Query::new(language, query_text).map_err(|e| {
310 ContextCreatorError::ParseError(format!("Failed to create function call query: {e}"))
311 })
312 }
313
314 fn create_type_reference_query(
316 language: Language,
317 language_name: &str,
318 ) -> Result<Query, ContextCreatorError> {
319 let query_text = match language_name {
320 "rust" => {
321 r#"
322 ; Type identifiers (excluding definitions)
323 (type_identifier) @type_name
324 (#not-match? @type_name "^(i8|i16|i32|i64|i128|u8|u16|u32|u64|u128|f32|f64|bool|char|str|String|Vec|Option|Result)$")
325
326 ; Generic types
327 (generic_type
328 type: (type_identifier) @type_name
329 )
330
331 ; Scoped type identifiers
332 (scoped_type_identifier
333 path: (identifier) @module_name
334 name: (type_identifier) @type_name
335 )
336
337 ; Types in function parameters
338 (parameter
339 type: [
340 (type_identifier) @param_type
341 (generic_type type: (type_identifier) @param_type)
342 (reference_type type: (type_identifier) @param_type)
343 ]
344 )
345
346 ; Return types
347 (function_item
348 return_type: [
349 (type_identifier) @return_type
350 (generic_type type: (type_identifier) @return_type)
351 (reference_type type: (type_identifier) @return_type)
352 ]
353 )
354
355 ; Field types in structs
356 (field_declaration
357 type: [
358 (type_identifier) @field_type
359 (generic_type type: (type_identifier) @field_type)
360 (reference_type type: (type_identifier) @field_type)
361 ]
362 )
363
364 ; Trait bounds
365 (trait_bounds
366 (type_identifier) @trait_name
367 )
368
369 ; Types in use statements (traits and types)
370 (use_declaration
371 (scoped_identifier
372 name: (identifier) @imported_type
373 )
374 )
375 (#match? @imported_type "^[A-Z]")
376 "#
377 }
378 "python" => {
379 r#"
380 ; Type identifiers in type positions
381 (type (identifier) @type_name)
382
383 ; Function parameter type annotations
384 (typed_parameter (identifier) @param_type)
385
386 ; Class inheritance
387 (class_definition
388 superclasses: (argument_list (identifier) @parent_class)
389 )
390
391 ; Generic/subscript type references
392 (subscript (identifier) @subscript_type)
393 "#
394 }
395 "javascript" => {
396 r#"
397 ; JSX element types (React components)
398 (jsx_element
399 open_tag: (jsx_opening_element
400 name: (identifier) @jsx_type
401 )
402 )
403 (#match? @jsx_type "^[A-Z]")
404
405 ; JSX self-closing elements
406 (jsx_self_closing_element
407 name: (identifier) @jsx_type
408 )
409 (#match? @jsx_type "^[A-Z]")
410 "#
411 }
412 "typescript" => {
413 r#"
414 ; Type annotations
415 (type_annotation
416 (type_identifier) @type_name
417 )
418
419 ; Predefined type annotations (void, any, etc.)
420 (type_annotation
421 (predefined_type) @type_name
422 )
423
424 ; Generic type arguments
425 (type_arguments
426 (type_identifier) @type_arg
427 )
428
429 ; Interface declarations
430 (interface_declaration
431 name: (type_identifier) @interface_name
432 )
433
434 ; Type aliases
435 (type_alias_declaration
436 name: (type_identifier) @type_alias
437 )
438 "#
439 }
440 _ => {
441 return Err(ContextCreatorError::ParseError(format!(
442 "Unsupported language for type queries: {language_name}"
443 )))
444 }
445 };
446
447 Query::new(language, query_text).map_err(|e| {
448 ContextCreatorError::ParseError(format!("Failed to create type reference query: {e}"))
449 })
450 }
451
452 fn extract_imports<'a>(
454 &self,
455 matches: tree_sitter::QueryMatches<'a, 'a, &'a [u8]>,
456 content: &str,
457 ) -> Result<Vec<Import>, ContextCreatorError> {
458 let mut imports = Vec::new();
459 let import_query_captures = self.import_query.capture_names();
460
461 for match_ in matches {
462 let mut module = String::new();
463 let mut items = Vec::new();
464 let mut is_relative = false;
465 let mut line = 0;
466
467 for capture in match_.captures {
468 let capture_name = &import_query_captures[capture.index as usize];
469 let node = capture.node;
470 line = node.start_position().row + 1;
471
472 match capture_name.as_str() {
473 "rust_import" => {
474 let (parsed_module, parsed_items, is_rel) =
476 self.parse_rust_use_declaration(node, content);
477 module = parsed_module;
478 items = parsed_items;
479 is_relative = is_rel;
480 }
481 "js_import" | "ts_import" => {
482 }
485 "simple_import" => {
486 }
488 "from_import" | "from_import_aliased" => {
489 }
491 "relative_from_import" | "relative_from_import_aliased" => {
492 is_relative = true;
494 }
495 "rust_module" => {
496 let (parsed_module, parsed_items, is_rel) =
498 self.parse_rust_module_declaration(node, content);
499 module = parsed_module;
500 items = parsed_items;
501 is_relative = is_rel;
502 }
503 "mod_name" | "crate_name" => {
504 if let Ok(name) = node.utf8_text(content.as_bytes()) {
505 if module.is_empty() {
507 module = name.to_string();
508 is_relative = capture_name == "mod_name";
509 }
510 }
511 }
512 "module_name" => {
513 if let Ok(name) = node.utf8_text(content.as_bytes()) {
515 module = name.trim_matches('"').to_string();
516 }
517 }
518 "from_module" => {
519 if let Ok(name) = node.utf8_text(content.as_bytes()) {
521 module = name.to_string();
522 }
523 }
524 "relative_module" => {
525 if let Ok(name) = node.utf8_text(content.as_bytes()) {
527 module = name.to_string();
528 is_relative = true;
529 }
530 }
531 "import_name" | "import_item" => {
532 if let Ok(name) = node.utf8_text(content.as_bytes()) {
533 items.push(name.to_string());
534 }
535 }
536 "module_path" => {
537 if let Ok(name) = node.utf8_text(content.as_bytes()) {
538 module = name.trim_matches('"').trim_matches('\'').to_string();
539 if module.starts_with('.') {
541 is_relative = true;
542 }
543 }
544 }
545 _ => {}
546 }
547 }
548
549 if !module.is_empty() || !items.is_empty() {
550 if self.is_secure_import(&module) {
552 imports.push(Import {
553 module,
554 items,
555 is_relative,
556 line,
557 });
558 } else {
559 eprintln!("Warning: Blocked potentially dangerous import: {module}");
561 }
562 }
563 }
564
565 Ok(imports)
566 }
567
568 fn extract_function_calls<'a>(
570 &self,
571 matches: tree_sitter::QueryMatches<'a, 'a, &'a [u8]>,
572 content: &str,
573 ) -> Result<Vec<FunctionCall>, ContextCreatorError> {
574 let mut calls = Vec::new();
575 let call_query_captures = self.function_call_query.capture_names();
576
577 for match_ in matches {
578 let mut name = String::new();
579 let mut module = None;
580 let mut line = 0;
581 let mut module_name = String::new();
582 let mut type_name = String::new();
583
584 for capture in match_.captures {
585 let capture_name = &call_query_captures[capture.index as usize];
586 let node = capture.node;
587 line = node.start_position().row + 1;
588
589 match capture_name.as_str() {
590 "fn_name" | "method_name" => {
591 if let Ok(fn_name) = node.utf8_text(content.as_bytes()) {
592 name = fn_name.to_string();
593 }
594 }
595 "module_name" => {
596 if let Ok(mod_name) = node.utf8_text(content.as_bytes()) {
597 module_name = mod_name.to_string();
598 module = Some(mod_name.to_string());
599 }
600 }
601 "type_name" => {
602 if let Ok(type_name_str) = node.utf8_text(content.as_bytes()) {
603 type_name = type_name_str.to_string();
604 }
605 }
606 "macro_name" => {
607 if let Ok(macro_name) = node.utf8_text(content.as_bytes()) {
608 name = macro_name.to_string();
609 }
610 }
611 _ => {}
612 }
613 }
614
615 if !module_name.is_empty() && !type_name.is_empty() {
617 module = Some(format!("{module_name}::{type_name}"));
618 }
619
620 if !name.is_empty() {
621 calls.push(FunctionCall { name, module, line });
622 }
623 }
624
625 Ok(calls)
626 }
627
628 fn extract_type_references<'a>(
630 &self,
631 matches: tree_sitter::QueryMatches<'a, 'a, &'a [u8]>,
632 content: &str,
633 ) -> Result<Vec<TypeReference>, ContextCreatorError> {
634 let mut type_refs = Vec::new();
635 let type_query_captures = self.type_reference_query.capture_names();
636
637 for match_ in matches {
638 let mut names = HashMap::new();
639 let mut module = None;
640 let mut line = 0;
641
642 for capture in match_.captures {
643 let capture_name = &type_query_captures[capture.index as usize];
644 let node = capture.node;
645 line = node.start_position().row + 1;
646
647 if let Ok(text) = node.utf8_text(content.as_bytes()) {
648 match capture_name.as_str() {
649 "type_name" | "param_type" | "return_type" | "field_type"
650 | "trait_name" | "imported_type" | "interface_name" | "type_alias"
651 | "jsx_type" | "parent_class" | "type_arg" | "base_type"
652 | "subscript_type" => {
653 names.insert(capture_name.to_string(), text.to_string());
654 }
655 "module_name" => {
656 module = Some(text.to_string());
657 }
658 _ => {}
659 }
660 }
661 }
662
663 for (_, type_name) in names {
665 if self.is_builtin_type(&type_name) {
667 continue;
668 }
669
670 type_refs.push(TypeReference {
671 name: type_name.clone(),
672 module: module.clone(),
673 line,
674 definition_path: None,
675 is_external: false,
676 external_package: None,
677 });
678 }
679 }
680
681 Ok(type_refs)
682 }
683
684 pub fn resolve_type_definitions(
687 &self,
688 type_refs: &mut [TypeReference],
689 current_file: &std::path::Path,
690 project_root: &std::path::Path,
691 ) -> Result<(), ContextCreatorError> {
692 use crate::core::semantic::path_validator::validate_import_path;
693
694 for type_ref in type_refs.iter_mut() {
695 if type_ref.definition_path.is_some() || type_ref.is_external {
697 continue;
698 }
699
700 if let Some(def_path) = self.find_type_definition(
702 &type_ref.name,
703 type_ref.module.as_deref(),
704 current_file,
705 project_root,
706 )? {
707 match validate_import_path(project_root, &def_path) {
709 Ok(validated_path) => {
710 type_ref.definition_path = Some(validated_path);
711 }
712 Err(_) => {
713 type_ref.is_external = true;
715 }
716 }
717 }
718 }
719
720 Ok(())
721 }
722
723 fn find_type_definition(
725 &self,
726 type_name: &str,
727 module_name: Option<&str>,
728 current_file: &std::path::Path,
729 project_root: &std::path::Path,
730 ) -> Result<Option<std::path::PathBuf>, ContextCreatorError> {
731 use std::fs;
732
733 let current_dir = current_file.parent().unwrap_or(project_root);
735
736 let type_name_lower = type_name.to_lowercase();
738
739 let extensions = self.get_search_extensions(current_file);
741
742 let mut patterns = vec![
744 format!("{type_name_lower}.{}", extensions[0]),
746 format!("types.{}", extensions[0]),
748 format!("mod.{}", extensions[0]),
750 format!("index.{}", extensions[0]),
751 format!("{type_name_lower}_types.{}", extensions[0]),
753 format!("{type_name_lower}_type.{}", extensions[0]),
754 format!("{type_name_lower}s.{}", extensions[0]), ];
756
757 for ext in &extensions[1..] {
759 patterns.push(format!("{type_name_lower}.{ext}"));
760 patterns.push(format!("types.{ext}"));
761 patterns.push(format!("index.{ext}"));
762 }
763
764 if let Some(module) = module_name {
766 if module.starts_with("crate::") {
768 let relative_path = module.strip_prefix("crate::").unwrap();
769 let path_parts: Vec<&str> = relative_path.split("::").collect();
770
771 if path_parts.len() > 1 {
772 let module_path = path_parts[..path_parts.len() - 1].join("/");
775 let type_name_lower = path_parts.last().unwrap().to_lowercase();
776
777 for ext in &extensions {
778 patterns.insert(0, format!("{module_path}/{type_name_lower}.{ext}"));
779 patterns.insert(1, format!("{module_path}/mod.{ext}"));
780 }
781 }
782 } else if module.contains("::") {
783 let path_parts: Vec<&str> = module.split("::").collect();
785
786 if path_parts.len() > 1 {
787 let module_path = path_parts[..path_parts.len() - 1].join("/");
790 let type_name_lower = path_parts.last().unwrap().to_lowercase();
791
792 for ext in &extensions {
793 patterns.insert(0, format!("{module_path}/{type_name_lower}.{ext}"));
794 patterns.insert(1, format!("{module_path}/mod.{ext}"));
795 }
796 }
797 } else {
798 let module_lower = module.to_lowercase();
800 for ext in &extensions {
801 patterns.insert(0, format!("{module_lower}.{ext}"));
802 patterns.insert(1, format!("{module}.{ext}")); }
804 }
805 }
806
807 let mut search_dirs = vec![
809 project_root.join("src"), project_root.to_path_buf(),
811 current_dir.to_path_buf(),
812 ];
813
814 if let Some(parent_dir) = current_dir.parent() {
816 search_dirs.push(parent_dir.to_path_buf());
817 }
818
819 search_dirs.extend(vec![
821 project_root.join("src/models"),
822 project_root.join("src/types"),
823 project_root.join("shared"),
824 project_root.join("shared/types"),
825 project_root.join("lib"),
826 project_root.join("domain"),
827 current_dir.join("models"),
828 current_dir.join("types"),
829 ]);
830
831 for search_dir in search_dirs {
832 if !search_dir.exists() {
833 continue;
834 }
835
836 for pattern in &patterns {
837 let candidate = search_dir.join(pattern);
838 if candidate.exists() {
839 if let Ok(content) = fs::read_to_string(&candidate) {
841 if self.file_contains_definition(&candidate, &content, type_name)? {
842 return Ok(Some(candidate));
843 }
844 }
845 }
846 }
847 }
848
849 Ok(None)
850 }
851
852 fn file_contains_definition(
854 &self,
855 path: &std::path::Path,
856 content: &str,
857 type_name: &str,
858 ) -> Result<bool, ContextCreatorError> {
859 let language = match path.extension().and_then(|s| s.to_str()) {
861 Some("rs") => Some(tree_sitter_rust::language()),
862 Some("py") => Some(tree_sitter_python::language()),
863 Some("ts") | Some("tsx") => Some(tree_sitter_typescript::language_typescript()),
864 Some("js") | Some("jsx") => Some(tree_sitter_javascript::language()),
865 _ => None,
866 };
867
868 if let Some(language) = language {
869 let mut parser = tree_sitter::Parser::new();
870 if parser.set_language(language).is_err() {
871 return Ok(false);
872 }
873
874 if let Some(tree) = parser.parse(content, None) {
875 let query_text = match path.extension().and_then(|s| s.to_str()) {
877 Some("rs") => {
878 r#"
879 [
880 (struct_item name: (type_identifier) @name)
881 (enum_item name: (type_identifier) @name)
882 (trait_item name: (type_identifier) @name)
883 (type_item name: (type_identifier) @name)
884 (union_item name: (type_identifier) @name)
885 ]
886 "#
887 }
888 Some("py") => {
889 r#"
890 [
891 (class_definition name: (identifier) @name)
892 (function_definition name: (identifier) @name)
893 ]
894 "#
895 }
896 Some("ts") | Some("tsx") => {
897 r#"
898 [
899 (interface_declaration name: (type_identifier) @name)
900 (type_alias_declaration name: (type_identifier) @name)
901 (class_declaration name: (type_identifier) @name)
902 (enum_declaration name: (identifier) @name)
903 ]
904 "#
905 }
906 Some("js") | Some("jsx") => {
907 r#"
908 [
909 (class_declaration name: (identifier) @name)
910 (function_declaration name: (identifier) @name)
911 ]
912 "#
913 }
914 _ => return Ok(false),
915 };
916
917 if let Ok(query) = tree_sitter::Query::new(language, query_text) {
918 let mut cursor = tree_sitter::QueryCursor::new();
919 let matches = cursor.matches(&query, tree.root_node(), content.as_bytes());
920
921 for m in matches {
923 for capture in m.captures {
924 if let Ok(captured_text) = capture.node.utf8_text(content.as_bytes()) {
925 if captured_text == type_name {
926 return Ok(true);
927 }
928 }
929 }
930 }
931 }
932 }
933 }
934
935 Ok(false)
936 }
937
938 fn get_search_extensions(&self, current_file: &std::path::Path) -> Vec<&'static str> {
940 match current_file.extension().and_then(|s| s.to_str()) {
941 Some("rs") => vec!["rs"],
942 Some("py") => vec!["py"],
943 Some("ts") | Some("tsx") => vec!["ts", "tsx", "js", "jsx"],
944 Some("js") | Some("jsx") => vec!["js", "jsx", "ts", "tsx"],
945 _ => vec!["rs", "py", "ts", "js"], }
947 }
948
949 #[allow(dead_code)]
951 fn parse_rust_use_tree(
952 &self,
953 node: tree_sitter::Node,
954 content: &str,
955 ) -> (String, Vec<String>, bool) {
956 if let Ok(text) = node.utf8_text(content.as_bytes()) {
959 let is_relative =
960 text.contains("self::") || text.contains("super::") || text.contains("crate::");
961 (text.to_string(), Vec::new(), is_relative)
962 } else {
963 (String::new(), Vec::new(), false)
964 }
965 }
966
967 fn parse_rust_module_declaration(
969 &self,
970 node: tree_sitter::Node,
971 content: &str,
972 ) -> (String, Vec<String>, bool) {
973 if let Ok(text) = node.utf8_text(content.as_bytes()) {
975 if let Some(mod_start) = text.find("mod ") {
977 let after_mod = &text[mod_start + 4..];
978 if let Some(end_pos) = after_mod.find(';') {
979 let module_name = after_mod[..end_pos].trim();
980 return (module_name.to_string(), Vec::new(), true);
981 } else if let Some(end_pos) = after_mod.find(' ') {
982 let module_name = after_mod[..end_pos].trim();
983 return (module_name.to_string(), Vec::new(), true);
984 }
985 }
986 }
987 (String::new(), Vec::new(), false)
988 }
989
990 fn parse_rust_use_declaration(
992 &self,
993 node: tree_sitter::Node,
994 content: &str,
995 ) -> (String, Vec<String>, bool) {
996 if let Ok(text) = node.utf8_text(content.as_bytes()) {
998 let clean_text = text
1001 .trim()
1002 .trim_start_matches("use ")
1003 .trim_end_matches(';')
1004 .trim();
1005
1006 let is_relative = clean_text.contains("self::")
1007 || clean_text.contains("super::")
1008 || clean_text.contains("crate::");
1009
1010 if clean_text.contains('{') && clean_text.contains('}') {
1011 if let Some(colon_pos) = clean_text.find("::") {
1013 let module = clean_text[..colon_pos].to_string();
1014
1015 if let Some(start) = clean_text.find('{') {
1017 if let Some(end) = clean_text.find('}') {
1018 let items_str = &clean_text[start + 1..end];
1019 let items: Vec<String> = items_str
1020 .split(',')
1021 .map(|s| s.trim().to_string())
1022 .filter(|s| !s.is_empty())
1023 .collect();
1024 return (module, items, is_relative);
1025 }
1026 }
1027 }
1028 } else {
1029 return (clean_text.to_string(), Vec::new(), is_relative);
1031 }
1032
1033 (clean_text.to_string(), Vec::new(), is_relative)
1034 } else {
1035 (String::new(), Vec::new(), false)
1036 }
1037 }
1038
1039 fn is_secure_import(&self, module: &str) -> bool {
1041 if module.is_empty() {
1043 return false;
1044 }
1045
1046 if module.starts_with('/') {
1048 if module.contains("/etc/") || module.contains("/sys/") || module.contains("/proc/") {
1050 return false;
1051 }
1052 }
1053
1054 if module.len() >= 2 && module.chars().nth(1) == Some(':') {
1056 if module.to_lowercase().contains("windows")
1058 || module.to_lowercase().contains("system32")
1059 {
1060 return false;
1061 }
1062 }
1063
1064 let dot_dot_count = module.matches("..").count();
1066 if dot_dot_count > 3 {
1067 return false;
1069 }
1070
1071 let dangerous_patterns = [
1073 "/etc/passwd",
1074 "/etc/shadow",
1075 "/root/",
1076 "C:\\Windows\\",
1077 "C:\\System32\\",
1078 "../../../../etc/",
1079 "..\\..\\..\\..\\windows\\",
1080 "file:///",
1081 "~/../../../",
1082 "%USERPROFILE%",
1083 "$HOME/../../../",
1084 ];
1085
1086 for pattern in &dangerous_patterns {
1087 if module.contains(pattern) {
1088 return false;
1089 }
1090 }
1091
1092 if module.contains('\0') || module.contains('\x00') {
1094 return false;
1095 }
1096
1097 true
1099 }
1100
1101 fn is_builtin_type(&self, type_name: &str) -> bool {
1103 matches!(
1104 type_name,
1105 "i8" | "i16"
1106 | "i32"
1107 | "i64"
1108 | "i128"
1109 | "u8"
1110 | "u16"
1111 | "u32"
1112 | "u64"
1113 | "u128"
1114 | "f32"
1115 | "f64"
1116 | "bool"
1117 | "char"
1118 | "str"
1119 | "String"
1120 | "Vec"
1121 | "Option"
1122 | "Result"
1123 | "Box"
1124 | "Rc"
1125 | "Arc"
1126 | "HashMap"
1127 | "HashSet"
1128 | "number"
1129 | "string"
1130 | "boolean"
1131 | "object"
1132 | "int"
1133 | "float"
1134 | "list"
1135 | "dict"
1136 | "tuple"
1137 | "set"
1138 )
1139 }
1140}
1141
1142#[cfg(test)]
1143mod tests {
1144 use super::*;
1145
1146 #[test]
1147 fn test_rust_query_creation() {
1148 let engine = QueryEngine::new(tree_sitter_rust::language(), "rust");
1149 assert!(engine.is_ok());
1150 }
1151
1152 #[test]
1153 fn test_python_query_creation() {
1154 let engine = QueryEngine::new(tree_sitter_python::language(), "python");
1155 if let Err(e) = &engine {
1156 println!("Python QueryEngine error: {e}");
1157 }
1158 assert!(engine.is_ok());
1159 }
1160
1161 #[test]
1162 fn test_javascript_query_creation() {
1163 let engine = QueryEngine::new(tree_sitter_javascript::language(), "javascript");
1164 if let Err(e) = &engine {
1165 println!("JavaScript QueryEngine error: {e}");
1166 }
1167 assert!(engine.is_ok());
1168 }
1169
1170 #[test]
1171 fn test_typescript_query_creation() {
1172 let engine = QueryEngine::new(tree_sitter_typescript::language_typescript(), "typescript");
1173 if let Err(e) = &engine {
1174 println!("TypeScript QueryEngine error: {e}");
1175 }
1176 assert!(engine.is_ok());
1177 }
1178
1179 #[test]
1180 fn test_builtin_type_detection() {
1181 let engine = QueryEngine::new(tree_sitter_rust::language(), "rust").unwrap();
1182
1183 assert!(engine.is_builtin_type("String"));
1184 assert!(engine.is_builtin_type("Vec"));
1185 assert!(engine.is_builtin_type("i32"));
1186 assert!(!engine.is_builtin_type("MyCustomType"));
1187 }
1188}