forge/mutation/mutators/
assignment_mutator.rs1use alloy_primitives::U256;
2use eyre::Result;
3use solar::ast::{ExprKind, Span};
4
5use crate::mutation::{
6 mutant::{Mutant, MutationType, OwnedLiteral},
7 mutators::{MutationContext, Mutator},
8 visitor::AssignVarTypes,
9};
10
11pub struct AssignmentMutator;
12
13impl Mutator for AssignmentMutator {
14 fn generate_mutants(&self, context: &MutationContext<'_>) -> Result<Vec<Mutant>> {
15 let (assign_var_type, replacement_span) = match extract_rhs_info(context) {
16 Some(info) => info,
17 None => return Ok(vec![]), };
19
20 let original = context.original_text();
21 let source_line = context.source_line();
22 let line_number = context.line_number();
23 let column_number = context.column_number();
24
25 match assign_var_type {
26 AssignVarTypes::Literal(ref lit) => match lit {
27 OwnedLiteral::Bool(val) => Ok(vec![Mutant {
28 span: replacement_span,
29 mutation: MutationType::Assignment(AssignVarTypes::Literal(
30 OwnedLiteral::Bool(!val),
31 )),
32 path: context.path.clone(),
33 original,
34 source_line,
35 line_number,
36 column_number,
37 }]),
38 OwnedLiteral::Number(val) if *val == U256::ZERO => Ok(vec![]),
39 OwnedLiteral::Number(val) => Ok(vec![
40 Mutant {
41 span: replacement_span,
42 mutation: MutationType::Assignment(AssignVarTypes::Literal(
43 OwnedLiteral::Number(U256::ZERO),
44 )),
45 path: context.path.clone(),
46 original: original.clone(),
47 source_line: source_line.clone(),
48 line_number,
49 column_number,
50 },
51 Mutant {
56 span: replacement_span,
57 mutation: MutationType::Assignment(AssignVarTypes::Literal(
58 OwnedLiteral::NegatedNumber(*val),
59 )),
60 path: context.path.clone(),
61 original,
62 source_line,
63 line_number,
64 column_number,
65 },
66 ]),
67 OwnedLiteral::Str { .. } => Ok(vec![]),
68 OwnedLiteral::Rational(_) => Ok(vec![]),
69 OwnedLiteral::Address(_) => Ok(vec![]),
70 OwnedLiteral::Err(_) => Ok(vec![]),
71 OwnedLiteral::NegatedNumber(_) => Ok(vec![]),
75 },
76 AssignVarTypes::Identifier(ref ident) => Ok(vec![
77 Mutant {
78 span: replacement_span,
79 mutation: MutationType::Assignment(AssignVarTypes::Literal(
80 OwnedLiteral::Number(U256::ZERO),
81 )),
82 path: context.path.clone(),
83 original: original.clone(),
84 source_line: source_line.clone(),
85 line_number,
86 column_number,
87 },
88 Mutant {
89 span: replacement_span,
90 mutation: MutationType::Assignment(AssignVarTypes::Identifier(format!(
91 "-{ident}"
92 ))),
93 path: context.path.clone(),
94 original,
95 source_line,
96 line_number,
97 column_number,
98 },
99 ]),
100 }
101 }
102
103 fn is_applicable(&self, context: &MutationContext<'_>) -> bool {
106 if let Some(expr) = context.expr {
107 if let ExprKind::Assign(_lhs, _op_opt, rhs_actual_expr) = &expr.kind {
108 matches!(rhs_actual_expr.kind, ExprKind::Lit(..) | ExprKind::Ident(..))
109 } else {
110 false }
112 } else if let Some(var_definition) = context.var_definition {
113 if let Some(init) = &var_definition.initializer {
114 matches!(&init.kind, ExprKind::Lit(..) | ExprKind::Ident(..))
115 } else {
116 false }
118 } else {
119 false }
121 }
122}
123
124fn extract_rhs_info<'ast>(context: &MutationContext<'ast>) -> Option<(AssignVarTypes, Span)> {
125 let relevant_expr_for_rhs = if let Some(var_definition) = context.var_definition {
126 var_definition.initializer.as_ref()?
127 } else {
128 let expr = context.expr?;
129 match &expr.kind {
130 ExprKind::Assign(_lhs, _op_opt, rhs_actual_expr) => &**rhs_actual_expr,
131 ExprKind::Lit(..) | ExprKind::Ident(..) => expr,
134 _ => return None,
135 }
136 };
137
138 match &relevant_expr_for_rhs.kind {
139 ExprKind::Lit(kind, _) => {
140 let owned = OwnedLiteral::from(&kind.kind);
141 Some((AssignVarTypes::Literal(owned), relevant_expr_for_rhs.span))
142 }
143 ExprKind::Ident(val) => {
144 Some((AssignVarTypes::Identifier(val.to_string()), relevant_expr_for_rhs.span))
145 }
146 _ => None,
147 }
148}