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, target_span) = match &expr.kind {
23 ExprKind::Unary(un_op, target) => (un_op.kind, target.span),
24 _ => unreachable!(),
25 };
26
27 let target_content = extract_span_text(context.source.unwrap_or(""), target_span);
28 if target_content.is_empty() {
29 return Ok(vec![]);
30 }
31
32 let original = context.original_text();
33 let source_line = context.source_line();
34 let line_number = context.line_number();
35 let column_number = context.column_number();
36
37 if op == UnOpKind::Not {
39 return Ok(vec![Mutant {
40 span: expr.span,
41 mutation: MutationType::UnaryOperator(UnaryOpMutated::new(
42 target_content,
43 UnOpKind::Not,
44 )),
45 path: context.path.clone(),
46 original,
47 source_line,
48 line_number,
49 column_number,
50 }]);
51 }
52
53 let mut mutations: Vec<Mutant>;
54
55 mutations = operations
56 .into_iter()
57 .filter(|&kind| kind != op)
58 .map(|kind| {
59 let new_expression = format!("{}{}", kind.to_str(), target_content);
60
61 let mutated = UnaryOpMutated::new(new_expression, kind);
62
63 Mutant {
64 span: expr.span,
65 mutation: MutationType::UnaryOperator(mutated),
66 path: context.path.clone(),
67 original: original.clone(),
68 source_line: source_line.clone(),
69 line_number,
70 column_number,
71 }
72 })
73 .collect();
74
75 mutations.extend(post_fixed_operations.into_iter().filter(|&kind| kind != op).map(
76 |kind| {
77 let new_expression = format!("{}{}", target_content, kind.to_str());
78
79 let mutated = UnaryOpMutated::new(new_expression, kind);
80
81 Mutant {
82 span: expr.span,
83 mutation: MutationType::UnaryOperator(mutated),
84 path: context.path.clone(),
85 original: original.clone(),
86 source_line: source_line.clone(),
87 line_number,
88 column_number,
89 }
90 },
91 ));
92
93 Ok(mutations)
94 }
95
96 fn is_applicable(&self, ctxt: &MutationContext<'_>) -> bool {
97 if let Some(expr) = ctxt.expr
98 && let ExprKind::Unary(_, _) = &expr.kind
99 {
100 return true;
101 }
102
103 false
104 }
105}
106
107fn extract_span_text(source: &str, span: Span) -> String {
108 let lo = span.lo().0 as usize;
109 let hi = span.hi().0 as usize;
110 source.get(lo..hi).map(str::trim).unwrap_or_default().to_string()
111}