1use crate::tools::tree_sitter::analysis::{
4 CodeAnalysis, CodeMetrics, DependencyInfo, DependencyKind,
5};
6use crate::tools::tree_sitter::languages::*;
7use anyhow::Result;
8use serde::{Deserialize, Serialize};
9use std::collections::HashMap;
10use std::path::Path;
13use tree_sitter::{Language, Parser, Tree};
14
15#[derive(Debug, thiserror::Error)]
17pub enum TreeSitterError {
18 #[error("Unsupported language: {0}")]
19 UnsupportedLanguage(String),
20
21 #[error("Parse error: {0}")]
22 ParseError(String),
23
24 #[error("File read error: {0}")]
25 FileReadError(String),
26
27 #[error("Language detection failed: {0}")]
28 LanguageDetectionError(String),
29
30 #[error("Query execution error: {0}")]
31 QueryError(String),
32}
33
34#[derive(Debug, Clone, Copy, Serialize, Deserialize, Eq, Hash, PartialEq)]
36pub enum LanguageSupport {
37 Rust,
38 Python,
39 JavaScript,
40 TypeScript,
41 Go,
42 Java,
43 Swift,
44}
45
46#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct SyntaxTree {
49 pub root: SyntaxNode,
50 pub source_code: String,
51 pub language: LanguageSupport,
52 pub diagnostics: Vec<Diagnostic>,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
57pub struct SyntaxNode {
58 pub kind: String,
59 pub start_position: Position,
60 pub end_position: Position,
61 pub text: String,
62 pub children: Vec<SyntaxNode>,
64 pub named_children: HashMap<String, Vec<SyntaxNode>>,
65 pub leading_comments: Vec<String>,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize, Eq, Hash, PartialEq)]
72pub struct Position {
73 pub row: usize,
74 pub column: usize,
75 pub byte_offset: usize,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
80pub struct Diagnostic {
81 pub level: DiagnosticLevel,
82 pub message: String,
83 pub position: Position,
84 pub node_kind: String,
85}
86
87#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
88pub enum DiagnosticLevel {
89 Error,
90 Warning,
91 Info,
92}
93
94pub struct TreeSitterAnalyzer {
96 parsers: HashMap<LanguageSupport, Parser>,
97 supported_languages: Vec<LanguageSupport>,
98 current_file: String,
99}
100
101impl TreeSitterAnalyzer {
102 pub fn new() -> Result<Self> {
104 let mut parsers = HashMap::new();
105
106 let languages = vec![
108 LanguageSupport::Rust,
109 LanguageSupport::Python,
110 LanguageSupport::JavaScript,
111 LanguageSupport::TypeScript,
112 LanguageSupport::Go,
113 LanguageSupport::Java,
114 ];
115
116 for language in &languages {
117 let mut parser = Parser::new();
118 let ts_language = get_language(language.clone())?;
119 parser.set_language(&ts_language)?;
120 parsers.insert(language.clone(), parser);
121 }
122
123 Ok(Self {
124 parsers,
125 supported_languages: languages,
126 current_file: String::new(),
127 })
128 }
129
130 pub fn supported_languages(&self) -> &[LanguageSupport] {
132 &self.supported_languages
133 }
134
135 pub fn detect_language_from_path<P: AsRef<Path>>(&self, path: P) -> Result<LanguageSupport> {
137 let path = path.as_ref();
138 let extension = path
139 .extension()
140 .and_then(|ext| ext.to_str())
141 .ok_or_else(|| {
142 TreeSitterError::LanguageDetectionError("No file extension found".to_string())
143 })?;
144
145 match extension {
146 "rs" => Ok(LanguageSupport::Rust),
147 "py" => Ok(LanguageSupport::Python),
148 "js" => Ok(LanguageSupport::JavaScript),
149 "ts" => Ok(LanguageSupport::TypeScript),
150 "tsx" => Ok(LanguageSupport::TypeScript),
151 "jsx" => Ok(LanguageSupport::JavaScript),
152 "go" => Ok(LanguageSupport::Go),
153 "java" => Ok(LanguageSupport::Java),
154 "swift" => Ok(LanguageSupport::Swift),
155 _ => Err(TreeSitterError::UnsupportedLanguage(extension.to_string()).into()),
156 }
157 }
158
159 pub fn parse(&mut self, source_code: &str, language: LanguageSupport) -> Result<Tree> {
161 let parser = self
162 .parsers
163 .get_mut(&language)
164 .ok_or_else(|| TreeSitterError::UnsupportedLanguage(format!("{:?}", language)))?;
165
166 let tree = parser.parse(source_code, None).ok_or_else(|| {
167 TreeSitterError::ParseError("Failed to parse source code".to_string())
168 })?;
169
170 Ok(tree)
171 }
172
173 pub fn extract_symbols(
175 &mut self,
176 syntax_tree: &Tree,
177 source_code: &str,
178 language: LanguageSupport,
179 ) -> Result<Vec<SymbolInfo>> {
180 let mut symbols = Vec::new();
181 let root_node = syntax_tree.root_node();
182
183 self.extract_symbols_recursive(root_node, source_code, language, &mut symbols, None)?;
185
186 Ok(symbols)
187 }
188
189 fn extract_symbols_recursive(
191 &self,
192 node: tree_sitter::Node,
193 source_code: &str,
194 language: LanguageSupport,
195 symbols: &mut Vec<SymbolInfo>,
196 parent_scope: Option<String>,
197 ) -> Result<()> {
198 let _node_text = &source_code[node.start_byte()..node.end_byte()];
199 let kind = node.kind();
200
201 match language {
203 LanguageSupport::Rust => {
204 if kind == "function_item" || kind == "method_definition" {
205 if let Some(name_node) = self.find_child_by_type(node, "identifier") {
207 let name = &source_code[name_node.start_byte()..name_node.end_byte()];
208 symbols.push(SymbolInfo {
209 name: name.to_string(),
210 kind: SymbolKind::Function,
211 position: Position {
212 row: node.start_position().row,
213 column: node.start_position().column,
214 byte_offset: node.start_byte(),
215 },
216 scope: parent_scope.clone(),
217 signature: None,
218 documentation: None,
219 });
220 }
221 } else if kind == "struct_item" || kind == "enum_item" {
222 if let Some(name_node) = self.find_child_by_type(node, "type_identifier") {
224 let name = &source_code[name_node.start_byte()..name_node.end_byte()];
225 symbols.push(SymbolInfo {
226 name: name.to_string(),
227 kind: SymbolKind::Type,
228 position: Position {
229 row: node.start_position().row,
230 column: node.start_position().column,
231 byte_offset: node.start_byte(),
232 },
233 scope: parent_scope.clone(),
234 signature: None,
235 documentation: None,
236 });
237 }
238 }
239 }
240 LanguageSupport::Python => {
241 if kind == "function_definition" {
242 if let Some(name_node) = self.find_child_by_type(node, "identifier") {
244 let name = &source_code[name_node.start_byte()..name_node.end_byte()];
245 symbols.push(SymbolInfo {
246 name: name.to_string(),
247 kind: SymbolKind::Function,
248 position: Position {
249 row: node.start_position().row,
250 column: node.start_position().column,
251 byte_offset: node.start_byte(),
252 },
253 scope: parent_scope.clone(),
254 signature: None,
255 documentation: None,
256 });
257 }
258 } else if kind == "class_definition" {
259 if let Some(name_node) = self.find_child_by_type(node, "identifier") {
261 let name = &source_code[name_node.start_byte()..name_node.end_byte()];
262 symbols.push(SymbolInfo {
263 name: name.to_string(),
264 kind: SymbolKind::Type,
265 position: Position {
266 row: node.start_position().row,
267 column: node.start_position().column,
268 byte_offset: node.start_byte(),
269 },
270 scope: parent_scope.clone(),
271 signature: None,
272 documentation: None,
273 });
274 }
275 }
276 }
277 _ => {
278 if kind.contains("function") || kind.contains("method") {
280 if let Some(name_node) = self.find_child_by_type(node, "identifier") {
282 let name = &source_code[name_node.start_byte()..name_node.end_byte()];
283 symbols.push(SymbolInfo {
284 name: name.to_string(),
285 kind: SymbolKind::Function,
286 position: Position {
287 row: node.start_position().row,
288 column: node.start_position().column,
289 byte_offset: node.start_byte(),
290 },
291 scope: parent_scope.clone(),
292 signature: None,
293 documentation: None,
294 });
295 }
296 }
297 }
298 }
299
300 let mut cursor = node.walk();
302 for child in node.children(&mut cursor) {
303 self.extract_symbols_recursive(
304 child,
305 source_code,
306 language.clone(),
307 symbols,
308 parent_scope.clone(),
309 )?;
310 }
311
312 Ok(())
313 }
314
315 fn find_child_by_type<'a>(
317 &self,
318 node: tree_sitter::Node<'a>,
319 type_name: &str,
320 ) -> Option<tree_sitter::Node<'a>> {
321 let mut cursor = node.walk();
322 for child in node.children(&mut cursor) {
323 if child.kind() == type_name {
324 return Some(child);
325 }
326 }
327 None
328 }
329
330 pub fn extract_dependencies(
332 &self,
333 syntax_tree: &Tree,
334 language: LanguageSupport,
335 ) -> Result<Vec<DependencyInfo>> {
336 let mut dependencies = Vec::new();
337 let root_node = syntax_tree.root_node();
338
339 match language {
341 LanguageSupport::Rust => {
342 self.extract_rust_dependencies(root_node, &mut dependencies)?;
343 }
344 LanguageSupport::Python => {
345 self.extract_python_dependencies(root_node, &mut dependencies)?;
346 }
347 LanguageSupport::JavaScript | LanguageSupport::TypeScript => {
348 self.extract_js_dependencies(root_node, &mut dependencies)?;
349 }
350 _ => {
351 self.extract_basic_dependencies(root_node, &mut dependencies)?;
353 }
354 }
355
356 Ok(dependencies)
357 }
358
359 fn extract_rust_dependencies(
361 &self,
362 node: tree_sitter::Node,
363 dependencies: &mut Vec<DependencyInfo>,
364 ) -> Result<()> {
365 let mut cursor = node.walk();
366
367 if node.kind() == "use_declaration" {
369 if let Some(_path_node) = self
371 .find_child_by_type(node, "use_list")
372 .or_else(|| self.find_child_by_type(node, "scoped_identifier"))
373 .or_else(|| self.find_child_by_type(node, "identifier"))
374 {
375 dependencies.push(DependencyInfo {
377 name: "unknown_rust_dep".to_string(), kind: DependencyKind::Import,
379 source: "use_declaration".to_string(),
380 position: Position {
381 row: node.start_position().row,
382 column: node.start_position().column,
383 byte_offset: node.start_byte(),
384 },
385 });
386 }
387 } else if node.kind() == "extern_crate_declaration" {
388 if let Some(_name_node) = self.find_child_by_type(node, "identifier") {
390 dependencies.push(DependencyInfo {
391 name: "unknown_crate".to_string(), kind: DependencyKind::External,
393 source: "extern_crate".to_string(),
394 position: Position {
395 row: node.start_position().row,
396 column: node.start_position().column,
397 byte_offset: node.start_byte(),
398 },
399 });
400 }
401 }
402
403 for child in node.children(&mut cursor) {
405 self.extract_rust_dependencies(child, dependencies)?;
406 }
407
408 Ok(())
409 }
410
411 fn extract_python_dependencies(
413 &self,
414 node: tree_sitter::Node,
415 dependencies: &mut Vec<DependencyInfo>,
416 ) -> Result<()> {
417 let mut cursor = node.walk();
418
419 if node.kind() == "import_statement" || node.kind() == "import_from_statement" {
421 dependencies.push(DependencyInfo {
423 name: "unknown_python_module".to_string(), kind: DependencyKind::Import,
425 source: node.kind().to_string(),
426 position: Position {
427 row: node.start_position().row,
428 column: node.start_position().column,
429 byte_offset: node.start_byte(),
430 },
431 });
432 }
433
434 for child in node.children(&mut cursor) {
436 self.extract_python_dependencies(child, dependencies)?;
437 }
438
439 Ok(())
440 }
441
442 fn extract_js_dependencies(
444 &self,
445 node: tree_sitter::Node,
446 dependencies: &mut Vec<DependencyInfo>,
447 ) -> Result<()> {
448 let mut cursor = node.walk();
449
450 if node.kind() == "import_statement" {
452 dependencies.push(DependencyInfo {
454 name: "unknown_js_module".to_string(), kind: DependencyKind::Import,
456 source: node.kind().to_string(),
457 position: Position {
458 row: node.start_position().row,
459 column: node.start_position().column,
460 byte_offset: node.start_byte(),
461 },
462 });
463 }
464
465 for child in node.children(&mut cursor) {
467 self.extract_js_dependencies(child, dependencies)?;
468 }
469
470 Ok(())
471 }
472
473 fn extract_basic_dependencies(
475 &self,
476 node: tree_sitter::Node,
477 dependencies: &mut Vec<DependencyInfo>,
478 ) -> Result<()> {
479 let mut cursor = node.walk();
480
481 if node.kind().contains("import") || node.kind().contains("include") {
483 dependencies.push(DependencyInfo {
485 name: "unknown_dependency".to_string(),
486 kind: DependencyKind::Import,
487 source: node.kind().to_string(),
488 position: Position {
489 row: node.start_position().row,
490 column: node.start_position().column,
491 byte_offset: node.start_byte(),
492 },
493 });
494 }
495
496 for child in node.children(&mut cursor) {
498 self.extract_basic_dependencies(child, dependencies)?;
499 }
500
501 Ok(())
502 }
503
504 pub fn calculate_metrics(&self, syntax_tree: &Tree, source_code: &str) -> Result<CodeMetrics> {
506 let root_node = syntax_tree.root_node();
507 let lines = source_code.lines().collect::<Vec<_>>();
508
509 let mut functions_count = 0;
511 let mut classes_count = 0;
512 let mut variables_count = 0;
513 let mut imports_count = 0;
514
515 self.count_nodes_recursive(
516 root_node,
517 &mut functions_count,
518 &mut classes_count,
519 &mut variables_count,
520 &mut imports_count,
521 );
522
523 let lines_of_comments = lines
525 .iter()
526 .filter(|l| {
527 l.trim().starts_with("//")
528 || l.trim().starts_with("/*")
529 || l.trim().starts_with("#")
530 })
531 .count();
532
533 let blank_lines = lines.iter().filter(|l| l.trim().is_empty()).count();
534 let lines_of_code = lines.len();
535
536 let comment_ratio = if lines_of_code > 0 {
537 lines_of_comments as f64 / lines_of_code as f64
538 } else {
539 0.0
540 };
541
542 Ok(CodeMetrics {
543 lines_of_code,
544 lines_of_comments,
545 blank_lines,
546 functions_count,
547 classes_count,
548 variables_count,
549 imports_count,
550 comment_ratio,
551 })
552 }
553
554 fn count_nodes_recursive(
556 &self,
557 node: tree_sitter::Node,
558 functions_count: &mut usize,
559 classes_count: &mut usize,
560 variables_count: &mut usize,
561 imports_count: &mut usize,
562 ) {
563 let kind = node.kind();
564
565 if kind.contains("function") || kind.contains("method") {
567 *functions_count += 1;
568 } else if kind.contains("class") || kind.contains("struct") || kind.contains("enum") {
569 *classes_count += 1;
570 } else if kind.contains("variable") || kind.contains("let") || kind.contains("const") {
571 *variables_count += 1;
572 } else if kind.contains("import") || kind.contains("include") || kind.contains("use") {
573 *imports_count += 1;
574 }
575
576 let mut cursor = node.walk();
578 for child in node.children(&mut cursor) {
579 self.count_nodes_recursive(
580 child,
581 functions_count,
582 classes_count,
583 variables_count,
584 imports_count,
585 );
586 }
587 }
588
589 pub fn parse_file<P: AsRef<Path>>(&mut self, file_path: P) -> Result<SyntaxTree> {
591 let file_path = file_path.as_ref();
592 let language = self.detect_language_from_path(file_path)?;
593
594 let source_code = std::fs::read_to_string(file_path)
595 .map_err(|e| TreeSitterError::FileReadError(e.to_string()))?;
596
597 let tree = self.parse(&source_code, language.clone())?;
598
599 let root = self.convert_tree_to_syntax_node(tree.root_node(), &source_code);
601 let diagnostics = self.collect_diagnostics(&tree, &source_code);
602
603 Ok(SyntaxTree {
604 root,
605 source_code,
606 language,
607 diagnostics,
608 })
609 }
610
611 pub fn convert_tree_to_syntax_node(
613 &self,
614 node: tree_sitter::Node,
615 source_code: &str,
616 ) -> SyntaxNode {
617 let start = node.start_position();
618 let end = node.end_position();
619
620 let mut converted_children: Vec<SyntaxNode> = Vec::new();
622 let mut cursor = node.walk();
623 for child in node.children(&mut cursor) {
624 let mut leading_comments: Vec<String> = Vec::new();
626 for prev in converted_children.iter().rev() {
627 let k = prev.kind.to_lowercase();
628 if k.contains("comment") {
629 leading_comments.push(prev.text.trim().to_string());
630 } else {
631 break;
632 }
633 }
634 leading_comments.reverse();
635
636 let mut converted = self.convert_tree_to_syntax_node(child, source_code);
638 converted.leading_comments = leading_comments;
639 converted_children.push(converted);
640 }
641
642 SyntaxNode {
643 kind: node.kind().to_string(),
644 start_position: Position {
645 row: start.row,
646 column: start.column,
647 byte_offset: node.start_byte(),
648 },
649 end_position: Position {
650 row: end.row,
651 column: end.column,
652 byte_offset: node.end_byte(),
653 },
654 text: source_code[node.start_byte()..node.end_byte()].to_string(),
655 children: converted_children,
656 named_children: self.collect_named_children(node, source_code),
657 leading_comments: Vec::new(),
658 }
659 }
660
661 fn collect_named_children(
663 &self,
664 node: tree_sitter::Node,
665 source_code: &str,
666 ) -> HashMap<String, Vec<SyntaxNode>> {
667 let mut named_children = HashMap::new();
668
669 for child in node.named_children(&mut node.walk()) {
670 let kind = child.kind().to_string();
671 let syntax_node = self.convert_tree_to_syntax_node(child, source_code);
672
673 named_children
674 .entry(kind)
675 .or_insert_with(Vec::new)
676 .push(syntax_node);
677 }
678
679 named_children
680 }
681
682 pub fn collect_diagnostics(&self, tree: &Tree, _source_code: &str) -> Vec<Diagnostic> {
684 let mut diagnostics = Vec::new();
685
686 if tree.root_node().has_error() {
688 diagnostics.push(Diagnostic {
689 level: DiagnosticLevel::Error,
690 message: "Syntax error detected in code".to_string(),
691 position: Position {
692 row: 0,
693 column: 0,
694 byte_offset: 0,
695 },
696 node_kind: "root".to_string(),
697 });
698 }
699
700 diagnostics
701 }
702
703 pub fn get_parser_stats(&self) -> HashMap<String, usize> {
705 let mut stats = HashMap::new();
706 stats.insert(
707 "supported_languages".to_string(),
708 self.supported_languages.len(),
709 );
710 stats
711 }
712
713 pub fn analyze_file_with_tree_sitter(
714 &mut self,
715 file_path: &std::path::Path,
716 source_code: &str,
717 ) -> Result<CodeAnalysis> {
718 let language = self
719 .detect_language_from_path(file_path)
720 .unwrap_or_else(|_| {
721 self.detect_language_from_content(source_code)
722 .unwrap_or(LanguageSupport::Rust)
723 });
724
725 self.current_file = file_path.to_string_lossy().to_string();
726
727 let tree = self.parse(source_code, language.clone())?;
728
729 let symbols = self.extract_symbols(&tree, source_code, language.clone())?;
731 let dependencies = self.extract_dependencies(&tree, language.clone())?;
732 let metrics = self.calculate_metrics(&tree, source_code)?;
733
734 Ok(CodeAnalysis {
735 file_path: self.current_file.clone(),
736 language,
737 symbols,
738 dependencies,
739 metrics,
740 issues: vec![], complexity: Default::default(), structure: Default::default(), })
744 }
745}
746
747fn get_language(language: LanguageSupport) -> Result<Language> {
749 let lang = match language {
750 LanguageSupport::Rust => tree_sitter_rust::LANGUAGE,
751 LanguageSupport::Python => tree_sitter_python::LANGUAGE,
752 LanguageSupport::JavaScript => tree_sitter_javascript::LANGUAGE,
753 LanguageSupport::TypeScript => tree_sitter_typescript::LANGUAGE_TYPESCRIPT,
754 LanguageSupport::Go => tree_sitter_go::LANGUAGE,
755 LanguageSupport::Java => tree_sitter_java::LANGUAGE,
756 LanguageSupport::Swift => {
757 #[cfg(feature = "swift")]
758 {
759 tree_sitter_swift::LANGUAGE
760 }
761 #[cfg(not(feature = "swift"))]
762 {
763 return Err(TreeSitterError::UnsupportedLanguage("Swift".to_string()).into());
764 }
765 }
766 };
767 Ok(lang.into())
768}
769
770impl std::fmt::Display for LanguageSupport {
771 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
772 let language_name = match self {
773 LanguageSupport::Rust => "Rust",
774 LanguageSupport::Python => "Python",
775 LanguageSupport::JavaScript => "JavaScript",
776 LanguageSupport::TypeScript => "TypeScript",
777 LanguageSupport::Go => "Go",
778 LanguageSupport::Java => "Java",
779 LanguageSupport::Swift => "Swift",
780 };
781 write!(f, "{}", language_name)
782 }
783}
784
785impl TreeSitterAnalyzer {
786 pub fn detect_language_from_content(&self, content: &str) -> Option<LanguageSupport> {
787 if content.contains("fn ") && content.contains("{") && content.contains("}") {
789 Some(LanguageSupport::Rust)
790 } else if content.contains("def ") && content.contains(":") && !content.contains("{") {
791 Some(LanguageSupport::Python)
792 } else if content.contains("function") && content.contains("{") && content.contains("}") {
793 Some(LanguageSupport::JavaScript)
794 } else {
795 None
796 }
797 }
798}
799
800#[cfg(test)]
801mod tests {
802 use super::*;
803 use std::path::Path;
804
805 fn create_test_analyzer() -> TreeSitterAnalyzer {
806 TreeSitterAnalyzer::new().expect("Failed to create analyzer")
807 }
808
809 #[test]
810 fn test_analyzer_creation() {
811 let analyzer = create_test_analyzer();
812 assert!(
813 analyzer
814 .supported_languages
815 .contains(&LanguageSupport::Rust)
816 );
817 assert!(
818 analyzer
819 .supported_languages
820 .contains(&LanguageSupport::Python)
821 );
822 }
823
824 #[test]
825 fn test_language_detection_from_path() {
826 let analyzer = create_test_analyzer();
827
828 match analyzer.detect_language_from_path(Path::new("main.rs")) {
830 Ok(lang) => assert_eq!(lang, LanguageSupport::Rust),
831 Err(e) => panic!("Expected Rust language, got error: {}", e),
832 }
833
834 match analyzer.detect_language_from_path(Path::new("script.py")) {
835 Ok(lang) => assert_eq!(lang, LanguageSupport::Python),
836 Err(e) => panic!("Expected Python language, got error: {}", e),
837 }
838
839 assert!(
841 analyzer
842 .detect_language_from_path(Path::new("file.unknown"))
843 .is_err()
844 );
845 }
846
847 #[test]
848 fn test_language_detection_from_content() {
849 let analyzer = create_test_analyzer();
850
851 let rust_code = r#"fn main() { println!("Hello, world!"); let x = 42; }"#;
853 assert_eq!(
854 analyzer.detect_language_from_content(rust_code),
855 Some(LanguageSupport::Rust)
856 );
857
858 let python_code = r#"def main(): print("Hello, world!"); x = 42"#;
860 assert_eq!(
861 analyzer.detect_language_from_content(python_code),
862 Some(LanguageSupport::Python)
863 );
864
865 let unknown_code = "This is not code just plain text.";
867 assert_eq!(analyzer.detect_language_from_content(unknown_code), None);
868 }
869
870 #[test]
871 fn test_parse_rust_code() {
872 let mut analyzer = create_test_analyzer();
873
874 let rust_code = r#"fn main() { println!("Hello, world!"); let x = 42; }"#;
875
876 let result = analyzer.parse(rust_code, LanguageSupport::Rust);
877 assert!(result.is_ok());
878
879 let tree = result.unwrap();
880 assert!(!tree.root_node().has_error());
881 }
882
883 #[cfg(feature = "swift")]
884 #[test]
885 fn test_parse_swift_code() {
886 let mut analyzer = create_test_analyzer();
887 let swift_code = r#"print(\"Hello, World!\")"#;
888 let result = analyzer.parse(swift_code, LanguageSupport::Swift);
889 assert!(result.is_ok());
890 let tree = result.unwrap();
891 assert!(!tree.root_node().has_error());
892 }
893}