"""AST-level logical operator misuse injection for Python.""" import ast from typing import Optional from app.dataset.rules.base import Mutation, MutationRule class LogicOperatorRule(MutationRule): """Swap `and` with `or` in a boolean expression. Uses `ast` to locate a BoolOp using `And` and replaces it with `Or`, preserving exact source position via line replacement. """ name = "logic_operator" language = "python" defect_type = "logic_operator_misuse" def detect_and_mutate(self, source: str, filename: str = "") -> Optional[Mutation]: try: tree = ast.parse(source) except SyntaxError: return None for node in ast.walk(tree): if isinstance(node, ast.BoolOp) and isinstance(node.op, ast.And): line_no = node.lineno lines = source.splitlines(keepends=True) line = lines[line_no - 1] mutated_line = line.replace(" and ", " or ", 1) if mutated_line == line: continue mutated = "".join(lines[:line_no - 1] + [mutated_line] + lines[line_no:]) return Mutation( defect_type=self.defect_type, language=self.language, line_start=line_no, line_end=getattr(node, "end_lineno", line_no) or line_no, mutated_source=mutated, reference_fix="Restore the original `and` operator for correct short-circuit logic.", description="Replaced boolean `and` with `or`.", ) return None