forge/mutation/mutators/
mod.rs1use std::path::PathBuf;
2
3use eyre::Result;
4use solar::ast::{Expr, Span, VariableDefinition, yul};
5
6use crate::mutation::Mutant;
7
8pub mod assembly_mutator;
9pub mod assignment_mutator;
10pub mod binary_op_mutator;
11pub mod delete_expression_mutator;
12pub mod elim_delegate_mutator;
13pub mod mutator_registry;
14pub mod require_mutator;
15pub mod unary_op_mutator;
16
17pub trait Mutator: Send + Sync {
18 fn generate_mutants(&self, ctxt: &MutationContext<'_>) -> Result<Vec<Mutant>>;
20 fn is_applicable(&self, ctxt: &MutationContext<'_>) -> bool;
22}
23
24#[derive(Debug)]
25pub struct MutationContext<'a> {
26 pub path: PathBuf,
27 pub span: Span,
28 pub expr: Option<&'a Expr<'a>>,
30 pub var_definition: Option<&'a VariableDefinition<'a>>,
31 pub yul_expr: Option<&'a yul::Expr<'a>>,
33 pub source: Option<&'a str>,
35}
36
37impl MutationContext<'_> {
38 pub fn original_text(&self) -> String {
40 self.source
41 .and_then(|src| {
42 let lo = self.span.lo().0 as usize;
43 let hi = self.span.hi().0 as usize;
44 src.get(lo..hi).map(|s| s.to_string())
45 })
46 .unwrap_or_default()
47 }
48
49 pub fn line_number(&self) -> usize {
51 self.source
52 .map(|src| {
53 let pos = self.span.lo().0 as usize;
54 src.get(..pos).map(|s| s.lines().count()).unwrap_or(0).max(1)
55 })
56 .unwrap_or(1)
57 }
58
59 pub fn column_number(&self) -> usize {
61 self.source
62 .map(|src| {
63 let pos = self.span.lo().0 as usize;
64 let line_start =
65 src.get(..pos).and_then(|s| s.rfind('\n')).map(|i| i + 1).unwrap_or(0);
66 pos - line_start + 1
67 })
68 .unwrap_or(1)
69 }
70
71 pub fn source_line(&self) -> String {
73 self.source
74 .and_then(|src| {
75 let pos = self.span.lo().0 as usize;
76 let line_start = src[..pos].rfind('\n').map(|i| i + 1).unwrap_or(0);
78 let line_end = src[pos..].find('\n').map(|i| pos + i).unwrap_or(src.len());
80 src.get(line_start..line_end).map(|s| s.trim().to_string())
81 })
82 .unwrap_or_default()
83 }
84}
85
86impl<'a> MutationContext<'a> {
87 #[must_use]
88 pub const fn builder() -> MutationContextBuilder<'a> {
89 MutationContextBuilder::new()
90 }
91}
92
93pub struct MutationContextBuilder<'a> {
94 path: Option<PathBuf>,
95 span: Option<Span>,
96 expr: Option<&'a Expr<'a>>,
97 var_definition: Option<&'a VariableDefinition<'a>>,
98 yul_expr: Option<&'a yul::Expr<'a>>,
99 source: Option<&'a str>,
100}
101
102impl<'a> MutationContextBuilder<'a> {
103 pub const fn new() -> Self {
105 MutationContextBuilder {
106 path: None,
107 span: None,
108 expr: None,
109 var_definition: None,
110 yul_expr: None,
111 source: None,
112 }
113 }
114
115 pub fn with_path(mut self, path: PathBuf) -> Self {
117 self.path = Some(path);
118 self
119 }
120
121 pub const fn with_span(mut self, span: Span) -> Self {
123 self.span = Some(span);
124 self
125 }
126
127 pub const fn with_expr(mut self, expr: &'a Expr<'a>) -> Self {
129 self.expr = Some(expr);
130 self
131 }
132
133 pub const fn with_var_definition(mut self, var_definition: &'a VariableDefinition<'a>) -> Self {
135 self.var_definition = Some(var_definition);
136 self
137 }
138
139 pub const fn with_yul_expr(mut self, yul_expr: &'a yul::Expr<'a>) -> Self {
141 self.yul_expr = Some(yul_expr);
142 self
143 }
144
145 pub const fn with_source(mut self, source: &'a str) -> Self {
147 self.source = Some(source);
148 self
149 }
150
151 pub fn build(self) -> Result<MutationContext<'a>, &'static str> {
152 let span = self.span.ok_or("Span is required for MutationContext")?;
153 let path = self.path.ok_or("Path is required for MutationContext")?;
154
155 Ok(MutationContext {
156 path,
157 span,
158 expr: self.expr,
159 var_definition: self.var_definition,
160 yul_expr: self.yul_expr,
161 source: self.source,
162 })
163 }
164}
165
166#[cfg(test)]
167mod tests;