forge/mutation/mutators/
unary_op_mutator.rs1use eyre::Result;
2use solar::ast::{ExprKind, Span, UnOpKind};
3
4use super::{MutationContext, Mutator};
5use crate::mutation::mutant::{Mutant, MutationType, UnaryOpMutated};
6
7pub struct UnaryOpMutator;
8
9impl Mutator for UnaryOpMutator {
10 fn generate_mutants(&self, context: &MutationContext<'_>) -> Result<Vec<Mutant>> {
11 let operations = vec![
12 UnOpKind::PreInc, UnOpKind::PreDec, UnOpKind::Neg, UnOpKind::BitNot, ];
17
18 let post_fixed_operations = vec![UnOpKind::PostInc, UnOpKind::PostDec];
19
20 let expr = context.expr.unwrap();
21
22 let op;
23 let target_span;
24
25 match &expr.kind {
26 ExprKind::Unary(un_op, target) => {
27 op = un_op.kind;
28 target_span = target.span;
29 }
30 _ => unreachable!(),
31 };
32
33 let target_content = extract_span_text(context.source.unwrap_or(""), target_span);
34 if target_content.is_empty() {
35 return Ok(vec![]);
36 }
37
38 let original = context.original_text();
39 let source_line = context.source_line();
40 let line_number = context.line_number();
41 let column_number = context.column_number();
42
43 if op == UnOpKind::Not {
45 return Ok(vec![Mutant {
46 span: expr.span,
47 mutation: MutationType::UnaryOperator(UnaryOpMutated::new(
48 target_content,
49 UnOpKind::Not,
50 )),
51 path: context.path.clone(),
52 original,
53 source_line,
54 line_number,
55 column_number,
56 }]);
57 }
58
59 let mut mutations: Vec<Mutant>;
60
61 mutations = operations
62 .into_iter()
63 .filter(|&kind| kind != op)
64 .map(|kind| {
65 let new_expression = format!("{}{}", kind.to_str(), target_content);
66
67 let mutated = UnaryOpMutated::new(new_expression, kind);
68
69 Mutant {
70 span: expr.span,
71 mutation: MutationType::UnaryOperator(mutated),
72 path: context.path.clone(),
73 original: original.clone(),
74 source_line: source_line.clone(),
75 line_number,
76 column_number,
77 }
78 })
79 .collect();
80
81 mutations.extend(post_fixed_operations.into_iter().filter(|&kind| kind != op).map(
82 |kind| {
83 let new_expression = format!("{}{}", target_content, kind.to_str());
84
85 let mutated = UnaryOpMutated::new(new_expression, kind);
86
87 Mutant {
88 span: expr.span,
89 mutation: MutationType::UnaryOperator(mutated),
90 path: context.path.clone(),
91 original: original.clone(),
92 source_line: source_line.clone(),
93 line_number,
94 column_number,
95 }
96 },
97 ));
98
99 Ok(mutations)
100 }
101
102 fn is_applicable(&self, ctxt: &MutationContext<'_>) -> bool {
103 if let Some(expr) = ctxt.expr
104 && let ExprKind::Unary(_, _) = &expr.kind
105 {
106 return true;
107 }
108
109 false
110 }
111}
112
113fn extract_span_text(source: &str, span: Span) -> String {
114 let lo = span.lo().0 as usize;
115 let hi = span.hi().0 as usize;
116 source.get(lo..hi).map(str::trim).unwrap_or_default().to_string()
117}