semantic.rs•31.7 kB
//! Main semantic analysis orchestration
#[cfg(feature = "napi-bindings")]
use napi_derive::napi;
use crate::types::{SemanticConcept, CodebaseAnalysisResult, ParseError, AnalysisConfig};
use crate::parsing::{ParserManager, FallbackExtractor, TreeWalker};
use crate::extractors::*;
use crate::analysis::{ComplexityAnalyzer, RelationshipLearner, FrameworkDetector};
use std::collections::HashMap;
use walkdir::WalkDir;
use std::fs;
/// Main semantic analyzer that orchestrates concept extraction across languages
#[cfg_attr(feature = "napi-bindings", napi)]
pub struct SemanticAnalyzer {
    parser_manager: ParserManager,
    config: AnalysisConfig,
    concepts: HashMap<String, SemanticConcept>,
    relationships: HashMap<String, Vec<String>>,
}
#[cfg_attr(feature = "napi-bindings", napi)]
impl SemanticAnalyzer {
    #[cfg_attr(feature = "napi-bindings", napi(constructor))]
    pub fn new() -> Result<Self, ParseError> {
        Ok(SemanticAnalyzer {
            parser_manager: ParserManager::new()?,
            config: AnalysisConfig::default(),
            concepts: HashMap::new(),
            relationships: HashMap::new(),
        })
    }
    /// Analyzes an entire codebase for semantic concepts and patterns
    /// 
    /// # Safety
    /// This function is marked unsafe for NAPI compatibility. It performs file system operations
    /// and language parsing that are inherently safe but marked unsafe for JavaScript interop.
    #[cfg_attr(feature = "napi-bindings", napi)]
    pub async unsafe fn analyze_codebase(
        &mut self,
        path: String,
    ) -> Result<CodebaseAnalysisResult, ParseError> {
        let languages = self.detect_languages(&path).await?;
        let framework_info = FrameworkDetector::detect_frameworks(path.clone()).await?;
        let frameworks: Vec<String> = framework_info.into_iter().map(|f| f.name).collect();
        let concepts = self.extract_concepts(&path).await?;
        let complexity = ComplexityAnalyzer::calculate_complexity(&concepts);
        Ok(CodebaseAnalysisResult {
            languages,
            frameworks,
            complexity,
            concepts,
        })
    }
    /// Analyzes the content of a specific file for semantic concepts
    /// 
    /// # Safety
    /// This function is marked unsafe for NAPI compatibility. It performs language parsing 
    /// operations that are inherently safe but marked unsafe for JavaScript interop.
    #[cfg_attr(feature = "napi-bindings", napi)]
    pub async unsafe fn analyze_file_content(
        &mut self,
        file_path: String,
        content: String,
    ) -> Result<Vec<SemanticConcept>, ParseError> {
        let language = self.config.detect_language_from_path(&file_path);
        let concepts = match self
            .parse_file_content(&file_path, &content, &language)
            .await
        {
            Ok(tree_concepts) => tree_concepts,
            Err(_) => {
                // Fallback to pattern-based extraction for unsupported languages
                FallbackExtractor::new().extract_concepts(&file_path, &content)
            }
        };
        // Store concepts for relationship analysis
        for concept in &concepts {
            self.concepts.insert(concept.id.clone(), concept.clone());
        }
        Ok(concepts)
    }
    /// Learns semantic concepts from analyzing an entire codebase
    /// 
    /// # Safety
    /// This function is marked unsafe for NAPI compatibility. It performs file system operations
    /// and language parsing that are inherently safe but marked unsafe for JavaScript interop.
    #[cfg_attr(feature = "napi-bindings", napi)]
    pub async unsafe fn learn_from_codebase(
        &mut self,
        path: String,
    ) -> Result<Vec<SemanticConcept>, ParseError> {
        // Add overall timeout for the entire learning process (5 minutes)
        let learning_result = match tokio::time::timeout(
            tokio::time::Duration::from_secs(300),
            self.extract_concepts(&path)
        ).await {
            Ok(concepts_result) => concepts_result?,
            Err(_timeout) => {
                eprintln!("Learning process timed out after 5 minutes");
                return Err(ParseError::from_reason(
                    "Learning process timed out. This can happen with very large codebases or complex file structures."
                ));
            }
        };
        // Learn relationships between concepts
        RelationshipLearner::learn_concept_relationships(&learning_result, &mut self.relationships);
        // Update internal knowledge
        for concept in &learning_result {
            self.concepts.insert(concept.id.clone(), concept.clone());
        }
        Ok(learning_result)
    }
    /// Updates the analyzer's internal state from analysis data (from original implementation)
    ///
    /// # Safety
    /// This function uses unsafe because it needs to interact with the Node.js runtime
    /// through N-API bindings. The caller must ensure the analysis data is valid JSON.
    #[cfg_attr(feature = "napi-bindings", napi)]
    pub async unsafe fn update_from_analysis(
        &mut self,
        _analysis_data: String,
    ) -> Result<bool, ParseError> {
        // Parse analysis data and update internal state
        // This would typically be called when file changes are detected
        Ok(true)
    }
    /// Get concept relationships for a specific concept ID (from original implementation)
    #[cfg_attr(feature = "napi-bindings", napi)]
    pub fn get_concept_relationships(&self, concept_id: String) -> Result<Vec<String>, ParseError> {
        Ok(self
            .relationships
            .get(&concept_id)
            .cloned()
            .unwrap_or_default())
    }
    /// Parse file content with tree-sitter and extract concepts
    pub async fn parse_file_content(
        &mut self,
        file_path: &str,
        content: &str,
        language: &str,
    ) -> Result<Vec<SemanticConcept>, ParseError> {
        // Add per-file timeout protection
        let parsing_result = tokio::time::timeout(
            tokio::time::Duration::from_secs(30), // 30 second timeout per file
            self.parse_file_with_language(file_path, content, language)
        ).await;
        match parsing_result {
            Ok(result) => result,
            Err(_timeout) => {
                eprintln!("Timeout parsing {}, using fallback", file_path);
                Ok(FallbackExtractor::new().extract_concepts(file_path, content))
            }
        }
    }
    /// Internal parsing with specific language
    async fn parse_file_with_language(
        &mut self,
        file_path: &str,
        content: &str,
        language: &str,
    ) -> Result<Vec<SemanticConcept>, ParseError> {
        let tree = self.parser_manager.parse(content, language)?;
        let mut concepts = Vec::new();
        // Use language-specific extraction
        match language {
            "typescript" | "javascript" => {
                let extractor = TypeScriptExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "rust" => {
                let extractor = RustExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "python" => {
                let extractor = PythonExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "sql" => {
                let extractor = SqlExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "go" => {
                let extractor = GoExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "java" => {
                let extractor = JavaExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "cpp" | "c" => {
                let extractor = CppExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "csharp" => {
                let extractor = CSharpExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            "svelte" => {
                let extractor = SvelteExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
            _ => {
                let extractor = GenericExtractor::new();
                self.walk_and_extract(tree.root_node(), file_path, content, &extractor, &mut concepts)?;
            }
        }
        Ok(concepts)
    }
    /// Walk tree and extract concepts using a specific extractor
    fn walk_and_extract<T>(
        &self,
        node: tree_sitter::Node<'_>,
        file_path: &str,
        content: &str,
        extractor: &T,
        concepts: &mut Vec<SemanticConcept>,
    ) -> Result<(), ParseError>
    where
        T: HasExtractConcepts,
    {
        let walker = TreeWalker::default();
        
        walker.walk(node, &mut |node| {
            extractor.extract_concepts(node, file_path, content, concepts)
                .map_err(|e| format!("Extraction error: {}", e))
        }).map_err(ParseError::from_reason)?;
        Ok(())
    }
    /// Extract concepts from entire codebase
    async fn extract_concepts(&mut self, path: &str) -> Result<Vec<SemanticConcept>, ParseError> {
        let mut all_concepts = Vec::new();
        let mut processed_count = 0;
        for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
            if entry.file_type().is_file() {
                let file_path = entry.path();
                // Skip non-source files and common directories
                if self.config.should_analyze_file(file_path) {
                    processed_count += 1;
                    
                    // Prevent processing too many files
                    if processed_count > self.config.max_files {
                        eprintln!("Warning: Reached maximum file limit ({}), stopping analysis", self.config.max_files);
                        break;
                    }
                    match fs::read_to_string(file_path) {
                        Ok(content) => {
                            let language = self.config.detect_language_from_path(
                                file_path.to_str().unwrap_or(""));
                            match self.parse_file_content(
                                file_path.to_str().unwrap_or(""),
                                &content,
                                &language,
                            ).await {
                                Ok(mut concepts) => {
                                    all_concepts.append(&mut concepts);
                                }
                                Err(_) => {
                                    // Fallback to regex-based extraction if tree-sitter fails
                                    eprintln!("Tree-sitter parsing failed for {}, using fallback", file_path.display());
                                    let fallback_concepts = FallbackExtractor::new()
                                        .extract_concepts(
                                            file_path.to_str().unwrap_or(""),
                                            &content,
                                        );
                                    all_concepts.extend(fallback_concepts);
                                }
                            };
                        }
                        Err(_) => {
                            // Skip files that can't be read
                            continue;
                        }
                    }
                }
            }
        }
        eprintln!("Processed {} source files and found {} concepts", processed_count, all_concepts.len());
        Ok(all_concepts)
    }
    /// Detect programming languages in codebase
    async fn detect_languages(&self, path: &str) -> Result<Vec<String>, ParseError> {
        let mut languages = std::collections::HashSet::new();
        for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
            if entry.file_type().is_file() {
                let file_path = entry.path();
                
                if let Some(extension) = file_path.extension().and_then(|s| s.to_str()) {
                    let language = match extension.to_lowercase().as_str() {
                        "ts" | "tsx" => Some("typescript"),
                        "js" | "jsx" => Some("javascript"),
                        "rs" => Some("rust"),
                        "py" => Some("python"),
                        "sql" => Some("sql"),
                        "go" => Some("go"),
                        "java" => Some("java"),
                        "c" => Some("c"),
                        "cpp" | "cc" | "cxx" => Some("cpp"),
                        "cs" => Some("csharp"),
                        "svelte" => Some("svelte"),
                        "vue" => Some("javascript"), // Fallback to JS for Vue
                        _ => None,
                    };
                    if let Some(lang) = language {
                        languages.insert(lang.to_string());
                    }
                }
            }
        }
        Ok(languages.into_iter().collect())
    }
}
/// Trait for extractors that can extract concepts from nodes
pub trait HasExtractConcepts {
    fn extract_concepts(
        &self,
        node: tree_sitter::Node<'_>,
        file_path: &str,
        content: &str,
        concepts: &mut Vec<SemanticConcept>,
    ) -> Result<(), ParseError>;
}
// Implement the trait for all extractors
impl HasExtractConcepts for TypeScriptExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        TypeScriptExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for RustExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        RustExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for PythonExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        PythonExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for SqlExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        SqlExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for GoExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        GoExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for JavaExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        JavaExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for CppExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        CppExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for CSharpExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        CSharpExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for SvelteExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        SvelteExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
impl HasExtractConcepts for GenericExtractor {
    fn extract_concepts(&self, node: tree_sitter::Node<'_>, file_path: &str, content: &str, concepts: &mut Vec<SemanticConcept>) -> Result<(), ParseError> {
        GenericExtractor::extract_concepts(self, node, file_path, content, concepts)
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    #[tokio::test]
    async fn test_semantic_analyzer_creation() {
        let analyzer = SemanticAnalyzer::new();
        assert!(analyzer.is_ok());
        
        let analyzer = analyzer.unwrap();
        assert!(analyzer.parser_manager.supports_language("typescript"));
        assert!(analyzer.parser_manager.supports_language("javascript"));
        assert!(analyzer.parser_manager.supports_language("rust"));
        assert!(analyzer.parser_manager.supports_language("python"));
    }
    #[tokio::test]
    async fn test_typescript_class_parsing() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        let content = "export class UserService { getName() { return 'test'; } }";
        println!("🔍 Testing TypeScript class parsing...");
        println!("Content: {}", content);
        let result = unsafe {
            analyzer.analyze_file_content("test.ts".to_string(), content.to_string()).await
        };
        match result {
            Ok(concepts) => {
                println!("✅ Parsing succeeded! Found {} concepts:", concepts.len());
                for concept in &concepts {
                    println!("  - {} ({})", concept.name, concept.concept_type);
                }
                assert!(!concepts.is_empty(), "Should find at least one concept");
            }
            Err(e) => {
                println!("❌ Parsing failed: {}", e);
                // Don't panic - assert with proper error message
                assert!(false, "TypeScript parsing should succeed, but failed with: {}", e);
            }
        }
    }
    #[tokio::test]
    async fn test_javascript_function_parsing() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        let content = "function hello() { return 'world'; }";
        println!("🔍 Testing JavaScript function parsing...");
        println!("Content: {}", content);
        let result = unsafe {
            analyzer.analyze_file_content("test.js".to_string(), content.to_string()).await
        };
        match result {
            Ok(concepts) => {
                println!("✅ Parsing succeeded! Found {} concepts:", concepts.len());
                for concept in &concepts {
                    println!("  - {} ({})", concept.name, concept.concept_type);
                }
                assert!(!concepts.is_empty(), "Should find at least one concept");
            }
            Err(e) => {
                println!("❌ Parsing failed: {}", e);
                // Don't panic - assert with proper error message
                assert!(false, "JavaScript parsing should succeed, but failed with: {}", e);
            }
        }
    }
    #[tokio::test]
    async fn test_python_class_parsing() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        let content = "class User:\n    def __init__(self):\n        pass";
        println!("🔍 Testing Python class parsing...");
        println!("Content: {}", content);
        let result = unsafe {
            analyzer.analyze_file_content("test.py".to_string(), content.to_string()).await
        };
        match result {
            Ok(concepts) => {
                println!("✅ Parsing succeeded! Found {} concepts:", concepts.len());
                for concept in &concepts {
                    println!("  - {} ({})", concept.name, concept.concept_type);
                }
                assert!(!concepts.is_empty(), "Should find at least one concept");
            }
            Err(e) => {
                println!("❌ Parsing failed: {}", e);
                // Don't panic - assert with proper error message
                assert!(false, "Python parsing should succeed, but failed with: {}", e);
            }
        }
    }
    #[tokio::test]
    async fn test_rust_struct_parsing() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        let content = "pub struct User { name: String }";
        println!("🔍 Testing Rust struct parsing...");
        println!("Content: {}", content);
        let result = unsafe {
            analyzer.analyze_file_content("test.rs".to_string(), content.to_string()).await
        };
        match result {
            Ok(concepts) => {
                println!("✅ Parsing succeeded! Found {} concepts:", concepts.len());
                for concept in &concepts {
                    println!("  - {} ({})", concept.name, concept.concept_type);
                }
                assert!(!concepts.is_empty(), "Should find at least one concept");
            }
            Err(e) => {
                println!("❌ Parsing failed: {}", e);
                // Don't panic - assert with proper error message
                assert!(false, "Rust parsing should succeed, but failed with: {}", e);
            }
        }
    }
    #[tokio::test]
    async fn test_learn_from_codebase() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        
        let result = unsafe {
            analyzer.learn_from_codebase(".".to_string()).await
        };
        
        assert!(result.is_ok());
        let concepts = result.unwrap();
        println!("Learned {} concepts from codebase", concepts.len());
        
        // Should find some concepts in the current Rust codebase
        assert!(concepts.len() > 0);
    }
    #[tokio::test]
    async fn test_update_from_analysis() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        let analysis_data = r#"{"patterns": [], "concepts": []}"#.to_string();
        
        let result = unsafe {
            analyzer.update_from_analysis(analysis_data).await
        };
        
        assert!(result.is_ok());
        assert!(result.unwrap());
    }
    #[test]
    fn test_get_concept_relationships() {
        let analyzer = SemanticAnalyzer::new().unwrap();
        
        let result = analyzer.get_concept_relationships("nonexistent".to_string());
        assert!(result.is_ok());
        assert_eq!(result.unwrap().len(), 0);
    }
    #[tokio::test]
    async fn test_detect_languages() {
        let analyzer = SemanticAnalyzer::new().unwrap();
        
        let result = analyzer.detect_languages(".").await;
        assert!(result.is_ok());
        
        let languages = result.unwrap();
        println!("Detected languages: {:?}", languages);
        
        // Should detect Rust in the current codebase
        assert!(languages.contains(&"rust".to_string()));
    }
    #[tokio::test]
    async fn test_analyze_codebase_structure() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        
        let result = unsafe {
            analyzer.analyze_codebase(".".to_string()).await
        };
        
        assert!(result.is_ok());
        let analysis = result.unwrap();
        
        println!("Analysis result:");
        println!("- Languages: {:?}", analysis.languages);
        println!("- Frameworks: {:?}", analysis.frameworks);
        println!("- Concepts: {}", analysis.concepts.len());
        println!("- Complexity: {:?}", analysis.complexity);
        
        assert!(!analysis.languages.is_empty());
        assert!(analysis.concepts.len() > 0);
        assert!(analysis.complexity.file_count > 0);
    }
    #[tokio::test] 
    async fn test_analyze_simple_typescript() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        let code = "function test() { return 42; }";
        
        let result = unsafe {
            analyzer.analyze_file_content("test.ts".to_string(), code.to_string()).await
        };
        
        assert!(result.is_ok());
        let concepts = result.unwrap();
        assert!(concepts.len() > 0);
        
        // Verify concept properties
        let concept = &concepts[0];
        assert!(!concept.name.is_empty());
        assert!(!concept.concept_type.is_empty());
        assert!(concept.confidence > 0.0);
        assert!(concept.confidence <= 1.0);
        assert_eq!(concept.file_path, "test.ts");
        assert!(concept.line_range.start > 0);
        assert!(concept.line_range.end >= concept.line_range.start);
    }
    #[tokio::test]
    async fn test_new_language_support() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        
        // Test SQL
        let sql_content = "CREATE TABLE users (id INTEGER PRIMARY KEY, name VARCHAR(255));";
        println!("🔍 Testing SQL parsing...");
        let sql_result = unsafe {
            analyzer.analyze_file_content("test.sql".to_string(), sql_content.to_string()).await
        };
        match &sql_result {
            Ok(concepts) => println!("✅ SQL: Found {} concepts", concepts.len()),
            Err(e) => println!("❌ SQL failed: {}", e),
        }
        assert!(sql_result.is_ok(), "SQL parsing should succeed: {:?}", sql_result.err());
        
        // Test Go
        let go_content = "package main\nfunc main() {\n    println(\"Hello World\")\n}";
        println!("🔍 Testing Go parsing...");
        let go_result = unsafe {
            analyzer.analyze_file_content("test.go".to_string(), go_content.to_string()).await
        };
        match &go_result {
            Ok(concepts) => println!("✅ Go: Found {} concepts", concepts.len()),
            Err(e) => println!("❌ Go failed: {}", e),
        }
        assert!(go_result.is_ok(), "Go parsing should succeed: {:?}", go_result.err());
        
        // Test Java
        let java_content = "public class HelloWorld {\n    public static void main(String[] args) {\n        System.out.println(\"Hello\");\n    }\n}";
        println!("🔍 Testing Java parsing...");
        let java_result = unsafe {
            analyzer.analyze_file_content("test.java".to_string(), java_content.to_string()).await
        };
        match &java_result {
            Ok(concepts) => println!("✅ Java: Found {} concepts", concepts.len()),
            Err(e) => println!("❌ Java failed: {}", e),
        }
        assert!(java_result.is_ok(), "Java parsing should succeed: {:?}", java_result.err());
        
        // Test C
        let c_content = "#include <stdio.h>\nint main() {\n    printf(\"Hello World\");\n    return 0;\n}";
        println!("🔍 Testing C parsing...");
        let c_result = unsafe {
            analyzer.analyze_file_content("test.c".to_string(), c_content.to_string()).await
        };
        assert!(c_result.is_ok(), "C parsing should succeed");
        
        // Test C++
        let cpp_content = "#include <iostream>\nclass HelloWorld {\npublic:\n    void sayHello() {\n        std::cout << \"Hello\";\n    }\n};";
        println!("🔍 Testing C++ parsing...");
        let cpp_result = unsafe {
            analyzer.analyze_file_content("test.cpp".to_string(), cpp_content.to_string()).await
        };
        assert!(cpp_result.is_ok(), "C++ parsing should succeed");
        
        // Test C#
        let csharp_content = "using System;\npublic class Program {\n    public static void Main() {\n        Console.WriteLine(\"Hello World\");\n    }\n}";
        println!("🔍 Testing C# parsing...");
        let csharp_result = unsafe {
            analyzer.analyze_file_content("test.cs".to_string(), csharp_content.to_string()).await
        };
        assert!(csharp_result.is_ok(), "C# parsing should succeed");
        
        // Test Svelte
        let svelte_content = "<script>\n  let name = \"world\";\n  function greet() {\n    alert(`Hello ${name}!`);\n  }\n</script>";
        println!("🔍 Testing Svelte parsing...");
        let svelte_result = unsafe {
            analyzer.analyze_file_content("test.svelte".to_string(), svelte_content.to_string()).await
        };
        assert!(svelte_result.is_ok(), "Svelte parsing should succeed");
        
        println!("✅ All language parsing tests passed!");
    }
    #[tokio::test]
    async fn test_timeout_handling() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        
        // Test that timeout doesn't cause crashes - use normal content
        let content = "function test() { return 42; }";
        let result = unsafe {
            analyzer.analyze_file_content("test.js".to_string(), content.to_string()).await
        };
        
        assert!(result.is_ok());
    }
    #[test]
    fn test_semantic_concept_creation() {
        use crate::types::LineRange;
        use std::collections::HashMap;
        
        let concept = SemanticConcept {
            id: "test_concept".to_string(),
            name: "TestClass".to_string(),
            concept_type: "class".to_string(),
            confidence: 0.9,
            file_path: "test.ts".to_string(),
            line_range: LineRange { start: 1, end: 10 },
            relationships: HashMap::new(),
            metadata: HashMap::new(),
        };
        assert_eq!(concept.name, "TestClass");
        assert_eq!(concept.concept_type, "class");
        assert_eq!(concept.confidence, 0.9);
        assert_eq!(concept.file_path, "test.ts");
        assert_eq!(concept.line_range.start, 1);
        assert_eq!(concept.line_range.end, 10);
    }
    #[test]
    fn test_complexity_metrics() {
        use crate::types::ComplexityMetrics;
        
        let metrics = ComplexityMetrics {
            cyclomatic_complexity: 5.0,
            cognitive_complexity: 8.0,
            function_count: 10,
            class_count: 5,
            file_count: 100,
            avg_functions_per_file: 2.0,
            avg_lines_per_concept: 15.0,
            max_nesting_depth: 3,
        };
        assert!(metrics.cyclomatic_complexity > 0.0);
        assert!(metrics.cognitive_complexity >= metrics.cyclomatic_complexity);
        assert!(metrics.file_count > 0);
        assert!(metrics.function_count > 0);
        assert!(metrics.class_count > 0);
    }
    #[tokio::test]
    async fn test_fallback_extraction() {
        let mut analyzer = SemanticAnalyzer::new().unwrap();
        
        // Test with a language that might not have full tree-sitter support
        // The system should fall back to regex-based extraction
        let content = "function calculate() { return 42; }";
        let result = unsafe {
            analyzer.analyze_file_content("test.unknown".to_string(), content.to_string()).await
        };
        
        assert!(result.is_ok());
        let concepts = result.unwrap();
        assert!(concepts.len() > 0);
        
        // Check that fallback extraction worked
        let concept = &concepts[0];
        assert!(!concept.name.is_empty());
        assert!(concept.confidence > 0.0);
    }
}