"""AST-level null-pointer injection for Java using javalang.""" from typing import Optional import javalang from app.dataset.rules.base import Mutation, MutationRule class JavaNoneReferenceRule(MutationRule): """Remove a null-check guard in Java source. Uses the pure-Python `javalang` parser to locate an `if (x != null)` guard and remove it, leaving the dereference unprotected. This keeps mutation semantics precise without regex/text replacement. """ name = "java_none_reference" language = "java" defect_type = "null_pointer" def detect_and_mutate(self, source: str, filename: str = "") -> Optional[Mutation]: try: tree = javalang.parse.parse(source) except Exception: return None for path, node in tree: if not isinstance(node, javalang.tree.IfStatement): continue cond = node.condition # Match: x != null if ( isinstance(cond, javalang.tree.BinaryOperation) and cond.operator == "!=" and isinstance(cond.operandr, javalang.tree.Literal) and cond.operandr.value == "null" ): var_name = getattr(cond.operandl, "member", str(cond.operandl)) lines = source.splitlines(keepends=True) start = node.position.line if node.position else 1 # Estimate end line by finding matching brace (simplistic) end = start depth = 0 for idx in range(start - 1, len(lines)): depth += lines[idx].count("{") - lines[idx].count("}") if depth > 0: end = idx + 1 if depth <= 0 and idx > start - 1: end = idx + 1 break body_lines = lines[start - 1:end] # keep body lines between header and closing brace, dedent one level inner = body_lines[1:-1] if len(body_lines) > 2 else [] dedented = [] for line in inner: if line.startswith(" "): dedented.append(" " + line[12:]) elif line.startswith(" "): dedented.append(" " + line[8:]) elif line.startswith(" "): dedented.append(line[4:]) else: dedented.append(line) mutated = "".join(lines[: start - 1] + dedented + lines[end:]) return Mutation( defect_type=self.defect_type, language=self.language, line_start=start, line_end=end, mutated_source=mutated, reference_fix=f"Add `if ({var_name} != null)` guard before dereferencing.", description=f"Removed null-check guard for '{var_name}'.", ) return None