use ruff_python_ast::Expr;
use ruff_python_ast::visitor::source_order::{SourceOrderVisitor, walk_expr};
pub(super) const MUTATING_METHODS: &[&str] = &[
"append",
"extend",
"insert",
"pop",
"remove",
"clear",
"sort",
"reverse",
"update",
"setdefault",
"popitem",
"add",
"discard",
"intersection_update",
"difference_update",
"symmetric_difference_update",
];
pub(super) fn expr_contains_call(expr: &Expr) -> bool {
struct CallDetector {
found: bool,
}
impl<'a> SourceOrderVisitor<'a> for CallDetector {
fn visit_expr(&mut self, expr: &'a Expr) {
if self.found {
return;
}
if matches!(expr, Expr::Call(_)) {
self.found = true;
return;
}
walk_expr(self, expr);
}
}
let mut detector = CallDetector { found: false };
detector.visit_expr(expr);
detector.found
}
pub(super) fn in_place_mutated_receiver_expr(expr: &Expr) -> Option<&Expr> {
match expr {
Expr::Call(call) => {
let Expr::Attribute(attr) = call.func.as_ref() else {
return None;
};
if MUTATING_METHODS.contains(&attr.attr.as_str()) {
Some(&attr.value)
} else {
None
}
}
Expr::Subscript(sub)
if matches!(
sub.ctx,
ruff_python_ast::ExprContext::Store | ruff_python_ast::ExprContext::Del
) =>
{
Some(&sub.value)
}
_ => None,
}
}