use crate::analyzers::function_registry::{FunctionSignatureRegistry, ReturnTypeInfo};
use crate::analyzers::type_registry::GlobalTypeRegistry;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use syn::{
Expr, ExprCall, ExprField, ExprMethodCall, ExprPath, ExprStruct, FnArg, ImplItemFn, ItemFn,
Pat, PatIdent, PatType, Type, TypePath,
};
#[derive(Debug, Clone)]
pub struct TypeTracker {
scopes: Vec<Scope>,
module_path: Vec<String>,
#[allow(dead_code)]
type_definitions: HashMap<String, TypeInfo>,
type_registry: Option<Arc<GlobalTypeRegistry>>,
function_registry: Option<Arc<FunctionSignatureRegistry>>,
current_file: Option<PathBuf>,
}
#[derive(Debug, Clone)]
pub struct Scope {
variables: HashMap<String, ResolvedType>,
kind: ScopeKind,
impl_type: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ResolvedType {
pub type_name: String,
pub source: TypeSource,
pub generics: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TypeSource {
Annotation,
Constructor,
StructLiteral,
FunctionReturn,
FieldAccess,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ScopeKind {
Module,
Function,
Block,
Impl,
}
#[derive(Debug, Clone)]
pub struct TypeInfo {
pub name: String,
pub kind: TypeKind,
}
#[derive(Debug, Clone)]
pub enum TypeKind {
Struct,
Enum,
Trait,
TypeAlias,
}
impl Default for TypeTracker {
fn default() -> Self {
Self::new()
}
}
impl TypeTracker {
pub fn new() -> Self {
Self {
scopes: vec![Scope {
variables: HashMap::new(),
kind: ScopeKind::Module,
impl_type: None,
}],
module_path: Vec::new(),
type_definitions: HashMap::new(),
type_registry: None,
function_registry: None,
current_file: None,
}
}
pub fn with_registry(registry: Arc<GlobalTypeRegistry>) -> Self {
Self {
scopes: vec![Scope {
variables: HashMap::new(),
kind: ScopeKind::Module,
impl_type: None,
}],
module_path: Vec::new(),
type_definitions: HashMap::new(),
type_registry: Some(registry),
function_registry: None,
current_file: None,
}
}
pub fn set_function_registry(&mut self, registry: Arc<FunctionSignatureRegistry>) {
self.function_registry = Some(registry);
}
pub fn set_current_file(&mut self, file: PathBuf) {
self.current_file = Some(file);
}
pub fn set_module_path(&mut self, path: Vec<String>) {
self.module_path = path;
}
pub fn enter_scope(&mut self, kind: ScopeKind, impl_type: Option<String>) {
self.scopes.push(Scope {
variables: HashMap::new(),
kind,
impl_type,
});
}
pub fn exit_scope(&mut self) {
if self.scopes.len() > 1 {
self.scopes.pop();
}
}
pub fn record_variable(&mut self, name: String, resolved_type: ResolvedType) {
if let Some(scope) = self.scopes.last_mut() {
scope.variables.insert(name, resolved_type);
}
}
pub fn resolve_variable_type(&self, name: &str) -> Option<ResolvedType> {
for scope in self.scopes.iter().rev() {
if let Some(resolved_type) = scope.variables.get(name) {
return Some(resolved_type.clone());
}
}
None
}
pub fn resolve_expr_type(&self, expr: &Expr) -> Option<ResolvedType> {
match expr {
Expr::Path(ExprPath { path, .. }) => {
if path.segments.len() == 1 {
let ident = path.segments.first()?.ident.to_string();
if ident == "self" {
if let Some(impl_type) = self.current_impl_type() {
return Some(ResolvedType {
type_name: impl_type,
source: TypeSource::Annotation,
generics: Vec::new(),
});
}
}
if let Some(resolved) = self.resolve_variable_type(&ident) {
return Some(resolved);
}
if ident.chars().next()?.is_uppercase() {
return Some(ResolvedType {
type_name: ident,
source: TypeSource::Annotation,
generics: Vec::new(),
});
}
None
} else {
None
}
}
Expr::Struct(expr_struct) => {
self.resolve_struct_literal_type(expr_struct)
}
Expr::MethodCall(method_call) => {
self.resolve_method_call_type(method_call)
}
Expr::Field(field_expr) => {
self.resolve_field_access(field_expr)
}
Expr::Call(call_expr) => {
self.resolve_function_call_type(call_expr)
}
_ => None,
}
}
fn resolve_struct_literal_type(&self, expr_struct: &ExprStruct) -> Option<ResolvedType> {
let type_name = expr_struct
.path
.segments
.last()
.map(|seg| seg.ident.to_string())?;
Some(ResolvedType {
type_name,
source: TypeSource::StructLiteral,
generics: Vec::new(),
})
}
fn resolve_field_access(&self, field_expr: &ExprField) -> Option<ResolvedType> {
let base_type = self.resolve_expr_type(&field_expr.base)?;
let field_name = match &field_expr.member {
syn::Member::Named(ident) => ident.to_string(),
syn::Member::Unnamed(index) => {
if let Some(registry) = &self.type_registry {
let field_type =
registry.resolve_tuple_field(&base_type.type_name, index.index as usize)?;
return Some(ResolvedType {
type_name: field_type.type_name,
source: TypeSource::FieldAccess,
generics: Vec::new(),
});
}
return None;
}
};
if let Some(registry) = &self.type_registry {
let resolved_type_name = if let Some(file) = &self.current_file {
registry
.resolve_type_with_imports(file, &base_type.type_name)
.unwrap_or_else(|| base_type.type_name.clone())
} else {
base_type.type_name.clone()
};
if let Some(field_type) = registry.resolve_field(&resolved_type_name, &field_name) {
return Some(ResolvedType {
type_name: field_type.type_name,
source: TypeSource::FieldAccess,
generics: field_type.generic_args,
});
}
}
None
}
pub fn current_impl_type(&self) -> Option<String> {
for scope in self.scopes.iter().rev() {
if scope.kind == ScopeKind::Impl {
return scope.impl_type.clone();
}
}
None
}
fn is_constructor_method(method_name: &str) -> bool {
matches!(
method_name,
"new" | "default" | "from" | "create" | "builder"
)
}
fn extract_function_path(func_expr: &Expr) -> Option<String> {
if let Expr::Path(path_expr) = func_expr {
Some(
path_expr
.path
.segments
.iter()
.map(|seg| seg.ident.to_string())
.collect::<Vec<_>>()
.join("::"),
)
} else {
None
}
}
fn parse_function_path(func_path: &str) -> Option<(String, String)> {
if func_path.contains("::") {
let parts: Vec<&str> = func_path.split("::").collect();
if parts.len() >= 2 {
let type_name = parts[..parts.len() - 1].join("::");
let method_name = parts.last()?.to_string();
return Some((type_name, method_name));
}
}
None
}
fn resolve_type_name(&self, return_info: &ReturnTypeInfo) -> String {
if return_info.is_self {
self.current_impl_type()
.unwrap_or_else(|| return_info.type_name.clone())
} else {
return_info.type_name.clone()
}
}
fn create_resolved_type(
type_name: String,
source: TypeSource,
generics: Vec<String>,
) -> ResolvedType {
ResolvedType {
type_name,
source,
generics,
}
}
fn resolve_function_call_type(&self, call_expr: &ExprCall) -> Option<ResolvedType> {
let func_path = Self::extract_function_path(&call_expr.func)?;
if let Some((type_name, method_name)) = Self::parse_function_path(&func_path) {
if Self::is_constructor_method(&method_name) {
if let Some(registry) = &self.function_registry {
if let Some(return_info) =
registry.resolve_method_return(&type_name, &method_name)
{
let resolved_type_name = if return_info.is_self {
type_name
} else {
return_info.type_name.clone()
};
return Some(Self::create_resolved_type(
resolved_type_name,
TypeSource::Constructor,
return_info.generic_args.clone(),
));
}
}
if matches!(method_name.as_str(), "new" | "default" | "from") {
return Some(Self::create_resolved_type(
type_name,
TypeSource::Constructor,
Vec::new(),
));
}
}
}
if let Some(registry) = &self.function_registry {
if let Some(return_info) = registry.resolve_function_return(&func_path, &[]) {
let type_name = self.resolve_type_name(&return_info);
return Some(Self::create_resolved_type(
type_name,
TypeSource::FunctionReturn,
return_info.generic_args.clone(),
));
}
}
None
}
fn resolve_method_call_type(&self, method_call: &ExprMethodCall) -> Option<ResolvedType> {
let receiver_type = self.resolve_expr_type(&method_call.receiver)?;
let method_name = method_call.method.to_string();
if let Some(registry) = &self.function_registry {
if let Some(return_info) =
registry.resolve_method_return(&receiver_type.type_name, &method_name)
{
let type_name = if return_info.is_self {
receiver_type.type_name.clone()
} else {
return_info.type_name.clone()
};
return Some(ResolvedType {
type_name,
source: TypeSource::FunctionReturn,
generics: return_info.generic_args.clone(),
});
}
if registry.is_builder(&receiver_type.type_name) {
if let Some(builder_info) = registry.get_builder(&receiver_type.type_name) {
if method_name == builder_info.build_method {
return Some(ResolvedType {
type_name: builder_info.target_type.clone(),
source: TypeSource::FunctionReturn,
generics: Vec::new(),
});
} else if builder_info.chain_methods.contains(&method_name) {
return Some(receiver_type);
}
}
}
}
None
}
pub fn track_self_param(&mut self, fn_item: Option<&ItemFn>, impl_fn: Option<&ImplItemFn>) {
if let Some(impl_fn) = impl_fn {
if let Some(_self_param) = extract_self_param(&impl_fn.sig) {
if let Some(impl_type) = self.current_impl_type() {
let resolved_type = ResolvedType {
type_name: impl_type,
source: TypeSource::Annotation,
generics: Vec::new(),
};
self.record_variable("self".to_string(), resolved_type);
}
}
}
if let Some(fn_item) = fn_item {
if let Some(_self_param) = extract_self_param(&fn_item.sig) {
if let Some(impl_type) = self.current_impl_type() {
let resolved_type = ResolvedType {
type_name: impl_type,
source: TypeSource::Annotation,
generics: Vec::new(),
};
self.record_variable("self".to_string(), resolved_type);
}
}
}
}
}
pub fn extract_self_param(sig: &syn::Signature) -> Option<SelfParam> {
if let Some(FnArg::Receiver(receiver)) = sig.inputs.first() {
Some(SelfParam {
is_reference: receiver.reference.is_some(),
is_mutable: receiver.mutability.is_some(),
})
} else {
None
}
}
#[derive(Debug, Clone)]
pub struct SelfParam {
pub is_reference: bool,
pub is_mutable: bool,
}
pub fn extract_type_from_pattern(pat: &Pat, init: &Option<Box<Expr>>) -> Option<ResolvedType> {
match pat {
Pat::Type(PatType { ty, .. }) => {
Some(extract_type_from_type(ty))
}
Pat::Ident(PatIdent { .. }) => {
init.as_ref().and_then(|expr| extract_type_from_expr(expr))
}
_ => None,
}
}
pub fn extract_type_from_type(ty: &Type) -> ResolvedType {
match ty {
Type::Path(TypePath { path, .. }) => {
let type_name = path
.segments
.iter()
.map(|seg| seg.ident.to_string())
.collect::<Vec<_>>()
.join("::");
ResolvedType {
type_name,
source: TypeSource::Annotation,
generics: Vec::new(),
}
}
_ => ResolvedType {
type_name: "Unknown".to_string(),
source: TypeSource::Annotation,
generics: Vec::new(),
},
}
}
pub fn extract_type_from_expr(expr: &Expr) -> Option<ResolvedType> {
match expr {
Expr::Call(ExprCall { func, .. }) => {
if let Expr::Path(ExprPath { path, .. }) = &**func {
extract_type_from_constructor_path(path)
} else {
None
}
}
Expr::Struct(ExprStruct { path, .. }) => {
Some(extract_type_from_path(path))
}
_ => None,
}
}
fn extract_type_from_constructor_path(path: &syn::Path) -> Option<ResolvedType> {
if path.segments.len() >= 2 {
let type_segments: Vec<String> = path
.segments
.iter()
.take(path.segments.len() - 1)
.map(|seg| seg.ident.to_string())
.collect();
if !type_segments.is_empty() {
return Some(ResolvedType {
type_name: type_segments.join("::"),
source: TypeSource::Constructor,
generics: Vec::new(),
});
}
}
None
}
fn extract_type_from_path(path: &syn::Path) -> ResolvedType {
let type_name = path
.segments
.iter()
.map(|seg| seg.ident.to_string())
.collect::<Vec<_>>()
.join("::");
ResolvedType {
type_name,
source: TypeSource::StructLiteral,
generics: Vec::new(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::analyzers::function_registry::{FunctionSignature, MethodSignature, VisibilityInfo};
#[test]
fn test_is_constructor_method() {
assert!(TypeTracker::is_constructor_method("new"));
assert!(TypeTracker::is_constructor_method("default"));
assert!(TypeTracker::is_constructor_method("from"));
assert!(TypeTracker::is_constructor_method("create"));
assert!(TypeTracker::is_constructor_method("builder"));
assert!(!TypeTracker::is_constructor_method("get"));
assert!(!TypeTracker::is_constructor_method("set"));
assert!(!TypeTracker::is_constructor_method("update"));
assert!(!TypeTracker::is_constructor_method("delete"));
assert!(!TypeTracker::is_constructor_method("process"));
}
#[test]
fn test_extract_function_path() {
let code = "MyType::new()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let path = TypeTracker::extract_function_path(&call.func);
assert_eq!(path, Some("MyType::new".to_string()));
}
let code = "process_data()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let path = TypeTracker::extract_function_path(&call.func);
assert_eq!(path, Some("process_data".to_string()));
}
let code = "std::collections::HashMap::new()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let path = TypeTracker::extract_function_path(&call.func);
assert_eq!(path, Some("std::collections::HashMap::new".to_string()));
}
}
#[test]
fn test_parse_function_path() {
let result = TypeTracker::parse_function_path("MyType::new");
assert_eq!(result, Some(("MyType".to_string(), "new".to_string())));
let result = TypeTracker::parse_function_path("std::collections::HashMap::new");
assert_eq!(
result,
Some(("std::collections::HashMap".to_string(), "new".to_string()))
);
let result = TypeTracker::parse_function_path("simple_function");
assert_eq!(result, None);
let result = TypeTracker::parse_function_path("crate::module::Type::method");
assert_eq!(
result,
Some(("crate::module::Type".to_string(), "method".to_string()))
);
}
#[test]
fn test_resolve_type_name() {
let tracker = TypeTracker::new();
let return_info = ReturnTypeInfo {
type_name: "String".to_string(),
is_self: false,
is_result: false,
is_option: false,
generic_args: vec![],
};
assert_eq!(tracker.resolve_type_name(&return_info), "String");
let return_info = ReturnTypeInfo {
type_name: "MyType".to_string(),
is_self: true,
is_result: false,
is_option: false,
generic_args: vec![],
};
assert_eq!(tracker.resolve_type_name(&return_info), "MyType");
}
#[test]
fn test_resolve_type_name_with_impl_context() {
let mut tracker = TypeTracker::new();
tracker.scopes.push(Scope {
variables: HashMap::new(),
kind: ScopeKind::Impl,
impl_type: Some("ActualType".to_string()),
});
let return_info = ReturnTypeInfo {
type_name: "FallbackType".to_string(),
is_self: true,
is_result: false,
is_option: false,
generic_args: vec![],
};
assert_eq!(tracker.resolve_type_name(&return_info), "ActualType");
}
#[test]
fn test_create_resolved_type() {
let resolved = TypeTracker::create_resolved_type(
"HashMap".to_string(),
TypeSource::FunctionReturn,
vec!["String".to_string(), "i32".to_string()],
);
assert_eq!(resolved.type_name, "HashMap");
assert_eq!(resolved.source, TypeSource::FunctionReturn);
assert_eq!(resolved.generics, vec!["String", "i32"]);
}
#[test]
fn test_resolve_function_call_type_simple_function() {
let mut tracker = TypeTracker::new();
let mut registry = FunctionSignatureRegistry::new();
registry.register_function(FunctionSignature {
name: "get_string".to_string(),
module_path: vec![],
return_type: ReturnTypeInfo {
type_name: "String".to_string(),
is_self: false,
is_result: false,
is_option: false,
generic_args: vec![],
},
is_async: false,
visibility: VisibilityInfo::Public,
generic_params: vec![],
});
tracker.function_registry = Some(Arc::new(registry));
let code = "get_string()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let result = tracker.resolve_function_call_type(&call);
assert!(result.is_some());
let resolved = result.unwrap();
assert_eq!(resolved.type_name, "String");
assert_eq!(resolved.source, TypeSource::FunctionReturn);
}
}
#[test]
fn test_resolve_function_call_type_constructor() {
let mut tracker = TypeTracker::new();
let mut registry = FunctionSignatureRegistry::new();
registry.register_method(
"MyType".to_string(),
MethodSignature {
name: "new".to_string(),
return_type: ReturnTypeInfo {
type_name: "MyType".to_string(),
is_self: true,
is_result: false,
is_option: false,
generic_args: vec![],
},
is_async: false,
takes_self: false,
takes_mut_self: false,
visibility: VisibilityInfo::Public,
param_types: vec![],
generic_params: vec![],
},
);
tracker.function_registry = Some(Arc::new(registry));
let code = "MyType::new()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let result = tracker.resolve_function_call_type(&call);
assert!(result.is_some());
let resolved = result.unwrap();
assert_eq!(resolved.type_name, "MyType");
assert_eq!(resolved.source, TypeSource::Constructor);
}
}
#[test]
fn test_resolve_function_call_type_default_constructor() {
let mut tracker = TypeTracker::new();
let registry = FunctionSignatureRegistry::new();
tracker.function_registry = Some(Arc::new(registry));
let code = "HashMap::default()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let result = tracker.resolve_function_call_type(&call);
assert!(result.is_some());
let resolved = result.unwrap();
assert_eq!(resolved.type_name, "HashMap");
assert_eq!(resolved.source, TypeSource::Constructor);
}
}
#[test]
fn test_resolve_function_call_type_no_registry() {
let tracker = TypeTracker::new();
let code = "some_function()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let result = tracker.resolve_function_call_type(&call);
assert!(result.is_none());
}
}
#[test]
fn test_resolve_function_call_type_unknown_function() {
let mut tracker = TypeTracker::new();
let registry = FunctionSignatureRegistry::new();
tracker.function_registry = Some(Arc::new(registry));
let code = "unknown_function()";
let expr: Expr = syn::parse_str(code).unwrap();
if let Expr::Call(call) = expr {
let result = tracker.resolve_function_call_type(&call);
assert!(result.is_none());
}
}
#[test]
fn test_extract_self_param_with_reference() {
let code = "fn method(&self) {}";
let item_fn: syn::ItemFn = syn::parse_str(code).unwrap();
let result = extract_self_param(&item_fn.sig);
assert!(result.is_some());
let self_param = result.unwrap();
assert!(self_param.is_reference);
assert!(!self_param.is_mutable);
}
#[test]
fn test_extract_self_param_with_mutable_reference() {
let code = "fn method(&mut self) {}";
let item_fn: syn::ItemFn = syn::parse_str(code).unwrap();
let result = extract_self_param(&item_fn.sig);
assert!(result.is_some());
let self_param = result.unwrap();
assert!(self_param.is_reference);
assert!(self_param.is_mutable);
}
#[test]
fn test_extract_self_param_owned() {
let code = "fn method(self) {}";
let item_fn: syn::ItemFn = syn::parse_str(code).unwrap();
let result = extract_self_param(&item_fn.sig);
assert!(result.is_some());
let self_param = result.unwrap();
assert!(!self_param.is_reference);
assert!(!self_param.is_mutable);
}
#[test]
fn test_extract_self_param_no_self() {
let code = "fn function(x: i32) {}";
let item_fn: syn::ItemFn = syn::parse_str(code).unwrap();
let result = extract_self_param(&item_fn.sig);
assert!(result.is_none());
}
#[test]
fn test_extract_self_param_static_method() {
let code = "fn new() -> Self {}";
let item_fn: syn::ItemFn = syn::parse_str(code).unwrap();
let result = extract_self_param(&item_fn.sig);
assert!(result.is_none());
}
#[test]
fn test_extract_type_from_pattern_with_explicit_type() {
let code = "let x: HashMap<String, i32> = HashMap::new();";
let stmt: syn::Stmt = syn::parse_str(code).unwrap();
if let syn::Stmt::Local(local) = stmt {
let result = extract_type_from_pattern(
&local.pat,
&local.init.as_ref().map(|init| Box::new(*init.expr.clone())),
);
assert!(result.is_some());
let resolved_type = result.unwrap();
assert_eq!(resolved_type.type_name, "HashMap");
assert_eq!(resolved_type.source, TypeSource::Annotation);
}
}
#[test]
fn test_extract_type_from_pattern_with_inference() {
let code = "let graph = DependencyGraph::new();";
let stmt: syn::Stmt = syn::parse_str(code).unwrap();
if let syn::Stmt::Local(local) = stmt {
let result = extract_type_from_pattern(
&local.pat,
&local.init.as_ref().map(|init| Box::new(*init.expr.clone())),
);
assert!(result.is_some());
let resolved_type = result.unwrap();
assert_eq!(resolved_type.type_name, "DependencyGraph");
assert_eq!(resolved_type.source, TypeSource::Constructor);
}
}
#[test]
fn test_extract_type_from_pattern_no_type_info() {
let code = "let x;";
let stmt: syn::Stmt = syn::parse_str(code).unwrap();
if let syn::Stmt::Local(local) = stmt {
let result = extract_type_from_pattern(
&local.pat,
&local.init.as_ref().map(|init| Box::new(*init.expr.clone())),
);
assert!(result.is_none());
}
}
#[test]
fn test_extract_type_from_type_simple() {
let ty: syn::Type = syn::parse_str("String").unwrap();
let result = extract_type_from_type(&ty);
assert_eq!(result.type_name, "String");
assert_eq!(result.source, TypeSource::Annotation);
}
#[test]
fn test_extract_type_from_type_path() {
let ty: syn::Type = syn::parse_str("std::collections::HashMap").unwrap();
let result = extract_type_from_type(&ty);
assert_eq!(result.type_name, "std::collections::HashMap");
assert_eq!(result.source, TypeSource::Annotation);
}
#[test]
fn test_extract_type_from_type_generic() {
let ty: syn::Type = syn::parse_str("Vec<String>").unwrap();
let result = extract_type_from_type(&ty);
assert_eq!(result.type_name, "Vec");
assert_eq!(result.source, TypeSource::Annotation);
}
#[test]
fn test_extract_type_from_type_reference() {
let ty: syn::Type = syn::parse_str("&str").unwrap();
let result = extract_type_from_type(&ty);
assert_eq!(result.type_name, "Unknown");
assert_eq!(result.source, TypeSource::Annotation);
}
#[test]
fn test_extract_type_from_expr_constructor() {
let expr: syn::Expr = syn::parse_str("HashMap::new()").unwrap();
let result = extract_type_from_expr(&expr);
assert!(result.is_some());
let resolved_type = result.unwrap();
assert_eq!(resolved_type.type_name, "HashMap");
assert_eq!(resolved_type.source, TypeSource::Constructor);
}
#[test]
fn test_extract_type_from_expr_nested_constructor() {
let expr: syn::Expr = syn::parse_str("std::collections::HashMap::new()").unwrap();
let result = extract_type_from_expr(&expr);
assert!(result.is_some());
let resolved_type = result.unwrap();
assert_eq!(resolved_type.type_name, "std::collections::HashMap");
assert_eq!(resolved_type.source, TypeSource::Constructor);
}
#[test]
fn test_extract_type_from_expr_struct_literal() {
let expr: syn::Expr = syn::parse_str("Point { x: 0, y: 0 }").unwrap();
let result = extract_type_from_expr(&expr);
assert!(result.is_some());
let resolved_type = result.unwrap();
assert_eq!(resolved_type.type_name, "Point");
assert_eq!(resolved_type.source, TypeSource::StructLiteral);
}
#[test]
fn test_extract_type_from_expr_module_struct_literal() {
let expr: syn::Expr = syn::parse_str("geometry::Point { x: 0, y: 0 }").unwrap();
let result = extract_type_from_expr(&expr);
assert!(result.is_some());
let resolved_type = result.unwrap();
assert_eq!(resolved_type.type_name, "geometry::Point");
assert_eq!(resolved_type.source, TypeSource::StructLiteral);
}
#[test]
fn test_extract_type_from_expr_non_constructor() {
let expr: syn::Expr = syn::parse_str("process_data()").unwrap();
let result = extract_type_from_expr(&expr);
assert!(result.is_none());
}
#[test]
fn test_extract_type_from_expr_literal() {
let expr: syn::Expr = syn::parse_str("42").unwrap();
let result = extract_type_from_expr(&expr);
assert!(result.is_none());
}
#[test]
fn test_extract_type_from_expr_binary() {
let expr: syn::Expr = syn::parse_str("x + y").unwrap();
let result = extract_type_from_expr(&expr);
assert!(result.is_none());
}
}