impl TreeSitterMutationOperator for RustBinaryOpMutation {
fn name(&self) -> &str {
"RustBinaryOp"
}
fn can_mutate(&self, node: &Node, source: &[u8]) -> bool {
if node.kind() != "binary_expression" {
return false;
}
if let Some(operator_node) = node.child_by_field_name("operator") {
let op = &source[operator_node.byte_range()];
matches!(op, b"+" | b"-" | b"*" | b"/" | b"%")
} else {
false
}
}
fn mutate(&self, node: &Node, source: &[u8]) -> Vec<MutatedSource> {
let mut mutations = Vec::new();
if let Some(operator_node) = node.child_by_field_name("operator") {
let original_op = &source[operator_node.byte_range()];
let replacements: &[&[u8]] = match original_op {
b"+" => &[b"-", b"*", b"/", b"%"],
b"-" => &[b"+", b"*", b"/", b"%"],
b"*" => &[b"+", b"-", b"/", b"%"],
b"/" => &[b"+", b"-", b"*", b"%"],
b"%" => &[b"+", b"-", b"*", b"/"],
_ => &[],
};
for replacement in replacements {
let mut mutated = Vec::new();
mutated.extend_from_slice(&source[..operator_node.start_byte()]);
mutated.extend_from_slice(replacement);
mutated.extend_from_slice(&source[operator_node.end_byte()..]);
let description = format!(
"{} → {}",
String::from_utf8_lossy(original_op),
String::from_utf8_lossy(replacement)
);
mutations.push(MutatedSource {
source: String::from_utf8_lossy(&mutated).into_owned(),
description,
location: SourceLocation {
line: operator_node.start_position().row + 1,
column: operator_node.start_position().column + 1,
end_line: operator_node.end_position().row + 1,
end_column: operator_node.end_position().column + 1,
},
});
}
}
mutations
}
}
impl TreeSitterMutationOperator for RustRelationalOpMutation {
fn name(&self) -> &str {
"RustRelationalOp"
}
fn can_mutate(&self, node: &Node, source: &[u8]) -> bool {
if node.kind() != "binary_expression" {
return false;
}
if let Some(operator_node) = node.child_by_field_name("operator") {
let op = &source[operator_node.byte_range()];
matches!(op, b">" | b"<" | b">=" | b"<=" | b"==" | b"!=")
} else {
false
}
}
fn mutate(&self, node: &Node, source: &[u8]) -> Vec<MutatedSource> {
let mut mutations = Vec::new();
if let Some(operator_node) = node.child_by_field_name("operator") {
let original_op = &source[operator_node.byte_range()];
let replacements: &[&[u8]] = match original_op {
b">" => &[b"<", b">=", b"<=", b"==", b"!="],
b"<" => &[b">", b">=", b"<=", b"==", b"!="],
b">=" => &[b">", b"<", b"<=", b"==", b"!="],
b"<=" => &[b">", b"<", b">=", b"==", b"!="],
b"==" => &[b"!=", b">", b"<", b">=", b"<="],
b"!=" => &[b"==", b">", b"<", b">=", b"<="],
_ => &[],
};
for replacement in replacements {
let mut mutated = Vec::new();
mutated.extend_from_slice(&source[..operator_node.start_byte()]);
mutated.extend_from_slice(replacement);
mutated.extend_from_slice(&source[operator_node.end_byte()..]);
let description = format!(
"{} → {}",
String::from_utf8_lossy(original_op),
String::from_utf8_lossy(replacement)
);
mutations.push(MutatedSource {
source: String::from_utf8_lossy(&mutated).into_owned(),
description,
location: SourceLocation {
line: operator_node.start_position().row + 1,
column: operator_node.start_position().column + 1,
end_line: operator_node.end_position().row + 1,
end_column: operator_node.end_position().column + 1,
},
});
}
}
mutations
}
}
impl TreeSitterMutationOperator for RustLogicalOpMutation {
fn name(&self) -> &str {
"RustLogicalOp"
}
fn can_mutate(&self, node: &Node, source: &[u8]) -> bool {
if node.kind() != "binary_expression" {
return false;
}
if let Some(operator_node) = node.child_by_field_name("operator") {
let op = &source[operator_node.byte_range()];
matches!(op, b"&&" | b"||")
} else {
false
}
}
fn mutate(&self, node: &Node, source: &[u8]) -> Vec<MutatedSource> {
let mut mutations = Vec::new();
if let Some(operator_node) = node.child_by_field_name("operator") {
let original_op = &source[operator_node.byte_range()];
let replacements: &[&[u8]] = match original_op {
b"&&" => &[b"||"],
b"||" => &[b"&&"],
_ => &[],
};
for replacement in replacements {
let mut mutated = Vec::new();
mutated.extend_from_slice(&source[..operator_node.start_byte()]);
mutated.extend_from_slice(replacement);
mutated.extend_from_slice(&source[operator_node.end_byte()..]);
let description = format!(
"{} → {}",
String::from_utf8_lossy(original_op),
String::from_utf8_lossy(replacement)
);
mutations.push(MutatedSource {
source: String::from_utf8_lossy(&mutated).into_owned(),
description,
location: SourceLocation {
line: operator_node.start_position().row + 1,
column: operator_node.start_position().column + 1,
end_line: operator_node.end_position().row + 1,
end_column: operator_node.end_position().column + 1,
},
});
}
}
mutations
}
}
impl TreeSitterMutationOperator for RustBitwiseOpMutation {
fn name(&self) -> &str {
"RustBitwiseOp"
}
fn can_mutate(&self, node: &Node, source: &[u8]) -> bool {
if node.kind() != "binary_expression" {
return false;
}
if let Some(operator_node) = node.child_by_field_name("operator") {
let op = &source[operator_node.byte_range()];
matches!(op, b"&" | b"|" | b"^" | b"<<" | b">>")
} else {
false
}
}
fn mutate(&self, node: &Node, source: &[u8]) -> Vec<MutatedSource> {
let mut mutations = Vec::new();
if let Some(operator_node) = node.child_by_field_name("operator") {
let original_op = &source[operator_node.byte_range()];
let replacements: &[&[u8]] = match original_op {
b"&" => &[b"|", b"^"],
b"|" => &[b"&", b"^"],
b"^" => &[b"&", b"|"],
b"<<" => &[b">>"],
b">>" => &[b"<<"],
_ => &[],
};
for replacement in replacements {
let mut mutated = Vec::new();
mutated.extend_from_slice(&source[..operator_node.start_byte()]);
mutated.extend_from_slice(replacement);
mutated.extend_from_slice(&source[operator_node.end_byte()..]);
let description = format!(
"{} → {}",
String::from_utf8_lossy(original_op),
String::from_utf8_lossy(replacement)
);
mutations.push(MutatedSource {
source: String::from_utf8_lossy(&mutated).into_owned(),
description,
location: SourceLocation {
line: operator_node.start_position().row + 1,
column: operator_node.start_position().column + 1,
end_line: operator_node.end_position().row + 1,
end_column: operator_node.end_position().column + 1,
},
});
}
}
mutations
}
}
impl TreeSitterMutationOperator for RustRangeOpMutation {
fn name(&self) -> &str {
"RustRangeOp"
}
fn can_mutate(&self, node: &Node, _source: &[u8]) -> bool {
match node.kind() {
"range_expression" | "inclusive_range_expression" => {
node.child_by_field_name("operator").is_some()
}
_ => false,
}
}
fn mutate(&self, node: &Node, source: &[u8]) -> Vec<MutatedSource> {
let mut mutations = Vec::new();
if let Some(operator_node) = node.child_by_field_name("operator") {
let original_op = &source[operator_node.byte_range()];
let replacements: &[&[u8]] = match original_op {
b".." => &[b"..="], b"..=" => &[b".."], _ => &[],
};
for replacement in replacements {
let mut mutated = Vec::new();
mutated.extend_from_slice(&source[..operator_node.start_byte()]);
mutated.extend_from_slice(replacement);
mutated.extend_from_slice(&source[operator_node.end_byte()..]);
let description = format!(
"{} → {}",
String::from_utf8_lossy(original_op),
String::from_utf8_lossy(replacement)
);
mutations.push(MutatedSource {
source: String::from_utf8_lossy(&mutated).into_owned(),
description,
location: SourceLocation {
line: operator_node.start_position().row + 1,
column: operator_node.start_position().column + 1,
end_line: operator_node.end_position().row + 1,
end_column: operator_node.end_position().column + 1,
},
});
}
}
mutations
}
}
impl TreeSitterMutationOperator for RustPatternMutation {
fn name(&self) -> &str {
"RustPattern"
}
fn can_mutate(&self, node: &Node, source: &[u8]) -> bool {
match node.kind() {
"match_expression" => true,
"match_arm" => {
let text = &source[node.byte_range()];
let text_str = std::str::from_utf8(text).unwrap_or("");
text_str.contains("Some")
|| text_str.contains("None")
|| text_str.contains("Ok")
|| text_str.contains("Err")
}
_ => false,
}
}
fn mutate(&self, _node: &Node, _source: &[u8]) -> Vec<MutatedSource> {
Vec::new()
}
}
impl TreeSitterMutationOperator for RustMethodChainMutation {
fn name(&self) -> &str {
"RustMethodChain"
}
fn can_mutate(&self, node: &Node, source: &[u8]) -> bool {
if node.kind() != "call_expression" {
return false;
}
if let Some(function_node) = node.child_by_field_name("function") {
if function_node.kind() == "field_expression" {
if let Some(field_node) = function_node.child_by_field_name("field") {
let field_text = &source[field_node.byte_range()];
let field_str = std::str::from_utf8(field_text).unwrap_or("");
matches!(
field_str,
"map" | "filter" | "collect" | "fold" | "for_each" | "find"
)
} else {
false
}
} else {
false
}
} else {
false
}
}
fn mutate(&self, _node: &Node, _source: &[u8]) -> Vec<MutatedSource> {
Vec::new()
}
}
impl TreeSitterMutationOperator for RustBorrowMutation {
fn name(&self) -> &str {
"RustBorrow"
}
fn can_mutate(&self, node: &Node, source: &[u8]) -> bool {
match node.kind() {
"reference_expression" => true,
"parameter" => {
let text = &source[node.byte_range()];
let text_str = std::str::from_utf8(text).unwrap_or("");
text_str.contains("&mut") || text_str.contains('&')
}
"unary_expression" => {
if let Some(operator_node) = node.child_by_field_name("operator") {
let op = &source[operator_node.byte_range()];
op == b"*"
} else {
false
}
}
_ => false,
}
}
fn mutate(&self, _node: &Node, _source: &[u8]) -> Vec<MutatedSource> {
Vec::new()
}
}