use crate::adversarial::mutations::catalog::BinOpKind;
use syn::spanned::Spanned;
use syn::visit::Visit;
fn op_symbol(op: BinOpKind) -> &'static str {
match op {
BinOpKind::And => "&",
BinOpKind::Or => "|",
BinOpKind::Xor => "^",
_ => "",
}
}
#[inline]
pub fn apply_op_swap(source: &str, from: BinOpKind, to: BinOpKind) -> String {
crate::adversarial::mutations::catalog::replace_operator(
source,
op_symbol(from),
op_symbol(to),
1,
)
}
#[inline]
pub fn apply_not_delete(source: &str) -> String {
let mut result = String::with_capacity(source.len());
let mut replaced = false;
let mut i = 0;
while i < source.len() {
if !replaced
&& crate::adversarial::mutations::catalog::lexical::is_code_index(source, i)
&& source[i..].starts_with('!')
{
let left_ok =
crate::adversarial::mutations::catalog::lexical::previous_non_ws(source, i)
.map(|c| !c.is_alphanumeric() && c != '_' && c != ')' && c != ']')
.unwrap_or(true);
let right_idx = i + 1;
let right_ok = source
.get(right_idx..)
.and_then(|s| s.chars().next())
.map(|c| c.is_alphanumeric() || c == '_' || c == '(' || c == '[')
.unwrap_or(false);
if left_ok && right_ok {
i += 1;
replaced = true;
continue;
}
}
if let Some(c) = source[i..].chars().next() {
result.push(c);
i += c.len_utf8();
} else {
break;
}
}
result
}
#[inline]
pub fn apply_and_mask_delete(source: &str) -> String {
let syntax = match syn::parse_file(source) {
Ok(s) => s,
Err(_) => return source.to_string(),
};
let mut visitor = BitAndDeleteVisitor { target: None };
visitor.visit_file(&syntax);
if let Some((start_lc, end_lc)) = visitor.target {
let start = crate::adversarial::mutations::catalog::lexical::line_column_to_byte_offset(
source,
start_lc.line,
start_lc.column,
);
let end = crate::adversarial::mutations::catalog::lexical::line_column_to_byte_offset(
source,
end_lc.line,
end_lc.column,
);
let mut result = String::with_capacity(source.len() - (end - start));
result.push_str(&source[..start]);
result.push_str(&source[end..]);
result
} else {
source.to_string()
}
}
struct BitAndDeleteVisitor {
target: Option<(proc_macro2::LineColumn, proc_macro2::LineColumn)>,
}
impl<'ast> Visit<'ast> for BitAndDeleteVisitor {
fn visit_expr_binary(&mut self, i: &'ast syn::ExprBinary) {
if self.target.is_some() {
return;
}
if matches!(i.op, syn::BinOp::BitAnd(_)) {
self.target = Some((i.left.span().end(), i.right.span().end()));
}
syn::visit::visit_expr_binary(self, i);
}
}