feat: implement expression-level mutations (Stages 1-3) #263
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Implement Expression-Level Mutations
Implements issue #145 - Adds support for expression-level mutations and the
!→!!mutation.Overview
This PR implements the first 3 stages of expression-level mutation support:
!→!!mutation (INVERT_LOGICAL_NOT)Note: Stage 4 (Clean Architecture refactoring) is tracked separately in issue #262.
What Changed
Stage 1: Node Context Awareness ✅
Problem: The SUB token (
-) was ambiguous - it appears in both unary expressions (-x) and binary expressions (a - b), leading to incorrect mutation types being applied.Solution:
NodeTokento store the original AST node for contextGetMutantTypesForToken()that filters mutations based on node typeInvertNegativesArithmeticBaseFiles:
internal/engine/node.go- AddednodeType ast.Nodefieldinternal/engine/mappings.go- Context-aware filteringinternal/engine/mappings_test.go- Tests for context filteringinternal/engine/engine.go- Use context-aware discoveryStage 2: Expression Mutation Infrastructure ✅
Problem: Token-based mutations can only swap tokens. Some mutations require AST reconstruction (e.g., wrapping
!xwith another NOT to create!!x).Solution:
NodeExprstruct for expression-level mutation pointsExprMutator(parallel to TokenMutator) with AST reconstructionfindParentAndReplacer()supporting 9+ parent node typesFiles:
internal/engine/node.go-NodeExprstructinternal/engine/exprmutator.go- New ExprMutator implementation (231 lines)internal/engine/exprmutator_test.go- Comprehensive test suiteinternal/engine/engine.go- Expression discovery integrationStage 3: INVERT_LOGICAL_NOT Mutation ✅
Problem: The
!operator wasn't being mutated. Double negation (!!x) is logically equivalent tox, testing if the negation is truly necessary.Solution:
InvertLogicalNotmutation typeinvertLogicalNot()to wrap!x→!!xFiles:
internal/mutator/mutator.go- New mutation typeinternal/engine/mappings.go-GetExprMutantTypes()detection logicinternal/engine/exprmutator.go-invertLogicalNot()implementationinternal/configuration/mutantenabled.go- Enabled by defaultinternal/report/report.go- Statistics trackinginternal/report/internal/structure.go- JSON output fielddocs/docs/usage/mutations/invert_logical_not.md- User documentationdocs/docs/usage/mutations/index.md- Updated mutation listTesting
Test Coverage
All Tests Pass ✅
Mutations Self-Test
Gremlins run on itself shows the new mutation works:
internal/engine/mappings.go:148:5- INVERT_LOGICAL_NOT mutation detected and KILLED ✅Documentation
docs/docs/usage/mutations/invert_logical_not.mddocs/docs/usage/mutations/index.mdIMPLEMENTATION_PLAN.mddocumenting all 4 stagesKnown Issues
Test Coverage: 87.98% (Threshold: 90%)
The new expression mutation infrastructure code has some uncovered paths:
findParentAndReplacer()- nil checks for various parent types (12 mutations)Resolution: These will be addressed in Stage 4 (#262) when we refactor to clean architecture and improve testability.
Temporary mitigation: The uncovered code is defensive nil-checking and configuration handling, low-risk code paths.
Breaking Changes
None. This is fully backward compatible:
Migration Guide
No migration needed. To use the new mutation:
Technical Debt / Follow-up
Tracked in issue #262:
Benchmarks
No performance regression detected:
Related Issues
!mutation to INVERT_LOGICAL #145 (Expression-level mutations)Checklist
make lint)make test)