Source Parsing & AST
Relevant source files
The following files were used as context for generating this wiki page:
Mutineer analyzes Ruby source code using the Prism parser to identify mutable code units and apply transformations. It represents these units as subjects and the transformations as mutations, which target specific byte offsets within the Abstract Syntax Tree (AST). The system ensures that every change is traceable via stable identifiers and accurately representable through unified diffs.
Sources: lib/mutineer.rb:6-8, test/json_reporter_test.rb:14-21, AGENTS.md:5
Core Components
The parsing and AST manipulation system relies on four primary data structures to represent the lifecycle of a mutant.
Architecture Overview
The diagram shows the flow from raw source code analysis to the generation of a specific mutant result.
Primary Data Structures
| Component | Description | Key Attributes |
|---|---|---|
Parser | Parses Ruby strings into Prism AST nodes. | parse_string |
Subject | Identifies a specific code unit (e.g., a method). | file, namespace, name, def_node |
Mutation | Defines a single transformation at a byte offset. | start_offset, end_offset, replacement, operator |
MutantId | Generates a 12-character stable hash for the mutant. | id, id_format |
Sources: test/json_reporter_test.rb:9-21, test/equivalent_mutant_test.rb:141-150, lib/mutineer/reporter.rb:112
Source Analysis Logic
AST Parsing
Mutineer uses Parser.parse_string to generate a Prism AST. The parser navigates the node tree, specifically looking for statements and body blocks to identify method definitions (def_node).
Sources: test/json_reporter_test.rb:14-16
Subject Identification
A Subject represents a specific scope in the source file. It tracks the fully qualified name (namespace and method name) and the specific AST node representing the definition. This allows Mutineer to isolate mutations to specific methods.
Sources: test/json_reporter_test.rb:18-21, lib/mutineer/reporter.rb:583
Suppression Mapping
Before generating mutants, the system parses the source for inline comments. It uses JobPlan.suppress_map to identify lines marked with # mutineer:disable-line.
- It detects specific operators or applies to
:all. - It ignores markers inside strings, heredocs, or regex literals.
- It excludes embedded documentation (
=begin/=end) and data after__END__.
Sources: test/equivalent_mutant_test.rb:32-75
Mutation and Identification
Mutation Application
A Mutation defines how to replace a segment of the raw source string.
- Offsets: Uses byte offsets (
start_offsettoend_offset) provided by the Prism parser. - Replacement: Provides the new string (e.g., replacing
>=with>). - Operator: Labels the type of change (e.g.,
:comparison,:arithmetic).
Sources: test/json_reporter_test.rb:9-12, lib/mutineer/reporter.rb:580-590
Stable Identifier Generation
MutantId creates a byte-stable identifier for each mutant.
- Format 2: Includes the project-relative file path in the hash to prevent collisions between files sharing method names.
- Legacy Support: Maintains compatibility with older ID formats to allow migration of baseline files.
- Collisions: If multiple mutants share a line and operator, the system uses ordinals or specific tokens to maintain uniqueness.
Sources: test/equivalent_mutant_test.rb:141-210, lib/mutineer/reporter.rb:112-118
Diff Generation and Reporting
The Reporter converts AST-based mutations back into human-readable unified diffs.
The sequence shows the transformation from byte-offset mutations back to line-based unified diffs.
Diff Calculation (diff_for)
The system aligns byte offsets to line boundaries to produce valid patches.
- Line Begin: Finds the last newline before
start_offset. - Line End: Finds the first newline after
end_offset. - Tokenization: Extracts the original code snippet (up to 50 characters) to serve as a label.
- Final Newline Handling: Detects if a block ends the file without a newline to append the
\ No newline at end of filemarker, ensuringgit applycompatibility.
Sources: lib/mutineer/reporter.rb:580-600, test/json_reporter_test.rb:140-180
Multi-byte and Encoding Safety
The printable method ensures that source text and diffs are safe for terminal display.
- It forces UTF-8 encoding.
- It scrubs invalid bytes and represents them as hex escapes (
\xNN). - It escapes control characters (e.g., ESC becomes
\e) to prevent terminal corruption.
Sources: lib/mutineer/reporter.rb:698-705
Mutineer effectively bridges AST-level analysis with line-level reporting by maintaining strict byte-offset accuracy throughout the mutant lifecycle. This ensures that every surviving mutant is actionable through standard developer tools like git apply.