context_creator/core/semantic/
analyzer.rs

1//! Base trait and types for language-specific semantic analyzers
2
3use crate::utils::error::ContextCreatorError;
4use std::collections::HashSet;
5use std::path::{Path, PathBuf};
6
7/// Result type for semantic analysis operations
8pub type SemanticResult<T> = Result<T, ContextCreatorError>;
9
10/// Context information for semantic analysis
11#[derive(Debug, Clone)]
12pub struct SemanticContext {
13    /// Current file being analyzed
14    pub current_file: PathBuf,
15    /// Base directory for the project
16    pub base_dir: PathBuf,
17    /// Current depth in dependency traversal
18    pub current_depth: usize,
19    /// Maximum allowed depth
20    pub max_depth: usize,
21    /// Files already visited (for cycle detection)
22    pub visited_files: HashSet<PathBuf>,
23}
24
25impl SemanticContext {
26    /// Create a new semantic context
27    pub fn new(current_file: PathBuf, base_dir: PathBuf, max_depth: usize) -> Self {
28        Self {
29            current_file,
30            base_dir,
31            current_depth: 0,
32            max_depth,
33            visited_files: HashSet::new(),
34        }
35    }
36
37    /// Check if we've reached maximum depth
38    pub fn at_max_depth(&self) -> bool {
39        self.current_depth >= self.max_depth
40    }
41
42    /// Create a child context for analyzing a dependency
43    pub fn child_context(&self, file: PathBuf) -> Option<Self> {
44        if self.at_max_depth() || self.visited_files.contains(&file) {
45            return None;
46        }
47
48        let mut child = self.clone();
49        child.current_file = file.clone();
50        child.current_depth += 1;
51        child.visited_files.insert(file);
52        Some(child)
53    }
54}
55
56/// Information about an import statement
57#[derive(Debug, Clone, PartialEq, Eq, Hash)]
58pub struct Import {
59    /// The module/package being imported
60    pub module: String,
61    /// Specific items imported (if any)
62    pub items: Vec<String>,
63    /// Whether this is a relative import
64    pub is_relative: bool,
65    /// Line number where import appears
66    pub line: usize,
67}
68
69/// Information about a function call
70#[derive(Debug, Clone, PartialEq, Eq, Hash)]
71pub struct FunctionCall {
72    /// Name of the function being called
73    pub name: String,
74    /// Module the function comes from (if known)
75    pub module: Option<String>,
76    /// Line number where call appears
77    pub line: usize,
78}
79
80/// Information about a function definition
81#[derive(Debug, Clone, PartialEq, Eq, Hash)]
82pub struct FunctionDefinition {
83    /// Name of the function
84    pub name: String,
85    /// Whether the function is exported/public
86    pub is_exported: bool,
87    /// Line number where function is defined
88    pub line: usize,
89}
90
91/// Information about a type reference
92#[derive(Debug, Clone, PartialEq, Eq, Hash)]
93pub struct TypeReference {
94    /// Name of the type
95    pub name: String,
96    /// Module the type comes from (if known)
97    pub module: Option<String>,
98    /// Line number where reference appears
99    pub line: usize,
100    /// Path to the file that defines this type
101    pub definition_path: Option<PathBuf>,
102    /// Whether this type is from an external dependency
103    pub is_external: bool,
104    /// External package name and version (e.g., "serde v1.0.197")
105    pub external_package: Option<String>,
106}
107
108/// Results from semantic analysis
109#[derive(Debug, Default, Clone)]
110pub struct AnalysisResult {
111    /// Import statements found
112    pub imports: Vec<Import>,
113    /// Function calls found
114    pub function_calls: Vec<FunctionCall>,
115    /// Type references found
116    pub type_references: Vec<TypeReference>,
117    /// Function definitions found
118    pub exported_functions: Vec<FunctionDefinition>,
119    /// Errors encountered during analysis (non-fatal)
120    pub errors: Vec<String>,
121}
122
123/// Base trait for language-specific analyzers
124pub trait LanguageAnalyzer: Send + Sync {
125    /// Get the language name
126    fn language_name(&self) -> &'static str;
127
128    /// Analyze a file and extract semantic information
129    fn analyze_file(
130        &self,
131        path: &Path,
132        content: &str,
133        context: &SemanticContext,
134    ) -> SemanticResult<AnalysisResult>;
135
136    /// Parse and analyze imports from the file
137    fn analyze_imports(
138        &self,
139        content: &str,
140        context: &SemanticContext,
141    ) -> SemanticResult<Vec<Import>> {
142        // Default implementation - languages can override
143        let result = self.analyze_file(&context.current_file, content, context)?;
144        Ok(result.imports)
145    }
146
147    /// Parse and analyze function calls from the file
148    fn analyze_function_calls(
149        &self,
150        content: &str,
151        context: &SemanticContext,
152    ) -> SemanticResult<Vec<FunctionCall>> {
153        // Default implementation - languages can override
154        let result = self.analyze_file(&context.current_file, content, context)?;
155        Ok(result.function_calls)
156    }
157
158    /// Parse and analyze type references from the file
159    fn analyze_type_references(
160        &self,
161        content: &str,
162        context: &SemanticContext,
163    ) -> SemanticResult<Vec<TypeReference>> {
164        // Default implementation - languages can override
165        let result = self.analyze_file(&context.current_file, content, context)?;
166        Ok(result.type_references)
167    }
168
169    /// Check if this analyzer can handle the given file extension
170    fn can_handle_extension(&self, extension: &str) -> bool;
171
172    /// Get file extensions this analyzer handles
173    fn supported_extensions(&self) -> Vec<&'static str>;
174
175    /// Resolve a type reference to its definition file
176    /// Returns None if the type cannot be resolved or is external
177    fn resolve_type_definition(
178        &self,
179        _type_ref: &TypeReference,
180        _context: &SemanticContext,
181    ) -> Option<PathBuf> {
182        // Default implementation returns None
183        // Languages should override this to provide type resolution
184        None
185    }
186}