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