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 type reference
81#[derive(Debug, Clone, PartialEq, Eq, Hash)]
82pub struct TypeReference {
83    /// Name of the type
84    pub name: String,
85    /// Module the type comes from (if known)
86    pub module: Option<String>,
87    /// Line number where reference appears
88    pub line: usize,
89    /// Path to the file that defines this type
90    pub definition_path: Option<PathBuf>,
91    /// Whether this type is from an external dependency
92    pub is_external: bool,
93    /// External package name and version (e.g., "serde v1.0.197")
94    pub external_package: Option<String>,
95}
96
97/// Results from semantic analysis
98#[derive(Debug, Default)]
99pub struct AnalysisResult {
100    /// Import statements found
101    pub imports: Vec<Import>,
102    /// Function calls found
103    pub function_calls: Vec<FunctionCall>,
104    /// Type references found
105    pub type_references: Vec<TypeReference>,
106    /// Errors encountered during analysis (non-fatal)
107    pub errors: Vec<String>,
108}
109
110/// Base trait for language-specific analyzers
111pub trait LanguageAnalyzer: Send + Sync {
112    /// Get the language name
113    fn language_name(&self) -> &'static str;
114
115    /// Analyze a file and extract semantic information
116    fn analyze_file(
117        &self,
118        path: &Path,
119        content: &str,
120        context: &SemanticContext,
121    ) -> SemanticResult<AnalysisResult>;
122
123    /// Parse and analyze imports from the file
124    fn analyze_imports(
125        &self,
126        content: &str,
127        context: &SemanticContext,
128    ) -> SemanticResult<Vec<Import>> {
129        // Default implementation - languages can override
130        let result = self.analyze_file(&context.current_file, content, context)?;
131        Ok(result.imports)
132    }
133
134    /// Parse and analyze function calls from the file
135    fn analyze_function_calls(
136        &self,
137        content: &str,
138        context: &SemanticContext,
139    ) -> SemanticResult<Vec<FunctionCall>> {
140        // Default implementation - languages can override
141        let result = self.analyze_file(&context.current_file, content, context)?;
142        Ok(result.function_calls)
143    }
144
145    /// Parse and analyze type references from the file
146    fn analyze_type_references(
147        &self,
148        content: &str,
149        context: &SemanticContext,
150    ) -> SemanticResult<Vec<TypeReference>> {
151        // Default implementation - languages can override
152        let result = self.analyze_file(&context.current_file, content, context)?;
153        Ok(result.type_references)
154    }
155
156    /// Check if this analyzer can handle the given file extension
157    fn can_handle_extension(&self, extension: &str) -> bool;
158
159    /// Get file extensions this analyzer handles
160    fn supported_extensions(&self) -> Vec<&'static str>;
161
162    /// Resolve a type reference to its definition file
163    /// Returns None if the type cannot be resolved or is external
164    fn resolve_type_definition(
165        &self,
166        _type_ref: &TypeReference,
167        _context: &SemanticContext,
168    ) -> Option<PathBuf> {
169        // Default implementation returns None
170        // Languages should override this to provide type resolution
171        None
172    }
173}