use super::types::MutationScope;
use crate::analyzers::scope_tracker::{ScopeTracker, SelfKind};
use syn::{Expr, ExprField};
pub fn determine_mutation_scope(expr: &Expr, scope: &ScopeTracker) -> MutationScope {
match expr {
Expr::Path(path) => {
let ident = path
.path
.get_ident()
.map(|i| i.to_string())
.unwrap_or_default();
if scope.is_local(&ident) {
MutationScope::Local
} else {
MutationScope::External
}
}
Expr::Field(field) => determine_field_mutation_scope(field, scope),
Expr::Index(index) => {
if let Expr::Path(path) = &*index.expr {
if let Some(ident) = path.path.get_ident() {
if scope.is_local(&ident.to_string()) {
return MutationScope::Local;
}
}
}
MutationScope::External
}
Expr::Unary(unary) if matches!(unary.op, syn::UnOp::Deref(_)) => {
MutationScope::External
}
_ => MutationScope::External,
}
}
pub fn determine_field_mutation_scope(field: &ExprField, scope: &ScopeTracker) -> MutationScope {
match &*field.base {
Expr::Path(path)
if scope.is_self(
&path
.path
.get_ident()
.map(|i| i.to_string())
.unwrap_or_default(),
) =>
{
if let Some(self_kind) = scope.get_self_kind() {
match self_kind {
SelfKind::MutRef => MutationScope::External, SelfKind::Owned | SelfKind::MutOwned => {
MutationScope::Local
}
_ => MutationScope::External,
}
} else {
MutationScope::External
}
}
Expr::Path(path) => {
let ident = path
.path
.get_ident()
.map(|i| i.to_string())
.unwrap_or_default();
if scope.is_local(&ident) {
MutationScope::Local
} else {
MutationScope::External
}
}
_ => MutationScope::External,
}
}
#[cfg(test)]
mod tests {
use super::*;
use syn::parse_quote;
#[test]
fn test_local_var_mutation() {
let scope = ScopeTracker::new();
let mut scope = scope;
scope.add_local_var("x".to_string());
let expr: Expr = parse_quote!(x);
assert_eq!(
determine_mutation_scope(&expr, &scope),
MutationScope::Local
);
}
#[test]
fn test_external_var_mutation() {
let scope = ScopeTracker::new();
let expr: Expr = parse_quote!(external);
assert_eq!(
determine_mutation_scope(&expr, &scope),
MutationScope::External
);
}
#[test]
fn test_pointer_dereference_external() {
let scope = ScopeTracker::new();
let expr: Expr = parse_quote!(*ptr);
assert_eq!(
determine_mutation_scope(&expr, &scope),
MutationScope::External
);
}
}