Skip to main content

forge/mutation/mutators/
elim_delegate_mutator.rs

1use std::fmt::Display;
2
3use eyre::Result;
4use solar::ast::ExprKind;
5
6use super::{MutationContext, Mutator};
7
8use crate::mutation::mutant::{Mutant, MutationType};
9
10pub struct ElimDelegateMutator;
11
12impl Mutator for ElimDelegateMutator {
13    fn generate_mutants(&self, context: &MutationContext<'_>) -> Result<Vec<Mutant>> {
14        // Narrow the span to just the `delegatecall` identifier so the replacement
15        // text ("call") does not clobber the surrounding call expression
16        // (e.g. `target.delegatecall(data)`).
17        let ident_span = context
18            .expr
19            .as_ref()
20            .and_then(|expr| match &expr.kind {
21                ExprKind::Call(callee, _) => Some(callee),
22                _ => None,
23            })
24            .and_then(|callee| match &callee.kind {
25                ExprKind::Member(_, ident) => Some(ident.span),
26                _ => None,
27            })
28            .unwrap_or(context.span);
29
30        Ok(vec![Mutant {
31            span: ident_span,
32            mutation: MutationType::ElimDelegate,
33            path: context.path.clone(),
34            // Use the narrowed identifier as the "original" text so the diff line
35            // ("- delegatecall" / "+ call") matches the actual textual replacement.
36            original: "delegatecall".to_string(),
37            source_line: context.source_line(),
38            line_number: context.line_number(),
39            column_number: context.column_number(),
40        }])
41    }
42
43    fn is_applicable(&self, ctxt: &MutationContext<'_>) -> bool {
44        ctxt.expr
45            .as_ref()
46            .and_then(|expr| match &expr.kind {
47                ExprKind::Call(callee, _) => Some(callee),
48                _ => None,
49            })
50            .and_then(|callee| match &callee.kind {
51                ExprKind::Member(_, ident) => Some(ident),
52                _ => None,
53            })
54            .is_some_and(|ident| ident.to_string() == "delegatecall")
55    }
56}
57
58impl Display for ElimDelegateMutator {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        write!(f, "")
61    }
62}