@@ -7,9 +7,10 @@ import stryker4s.exception.{Stryker4sException, UnableToBuildPatternMatchExcepti
77import stryker4s .extension .TreeExtensions .{treeEq , TransformOnceExtension }
88import stryker4s .log .Logger
99import stryker4s .model .*
10+ import stryker4s .model .SourceReplacement .*
1011
12+ import scala .collection .immutable .SortedSet
1113import scala .meta .*
12- import scala .util .control .NonFatal
1314import scala .util .{Failure , Success , Try }
1415
1516/** Instrument (place) mutants in a tree
@@ -22,23 +23,29 @@ class MutantInstrumenter(options: InstrumenterOptions)(implicit log: Logger) {
2223
2324 def instrumentFile (context : SourceContext , mutantMap : Map [PlaceableTree , MutantsWithId ]): MutatedFile = {
2425
25- def instrumentWithMutants (mutantMap : Map [PlaceableTree , MutantsWithId ]): PartialFunction [Tree , Tree ] = {
26+ // Rendered mutation switches for each outermost mutated statement, used to splice the file together
27+ val spliceReplacements = SortedSet .newBuilder[SourceReplacement ]
28+
29+ def instrumentWithMutants (
30+ mutantMap : Map [PlaceableTree , MutantsWithId ],
31+ record : Boolean
32+ ): PartialFunction [Tree , Tree ] = {
2633
2734 Function .unlift { originalTree =>
2835 val p = PlaceableTree (originalTree)
2936 mutantMap.get(p).map { case mutations =>
3037 val mutableCases = mutations.map(mutantToCase)
3138
3239 // Continue deeper into the tree (without the currently placed mutants)
33- val withDefaultsTransformed = PlaceableTree (p.tree.transformOnce(instrumentWithMutants(mutantMap - p)))
40+ val withDefaultsTransformed =
41+ PlaceableTree (p.tree.transformOnce(instrumentWithMutants(mutantMap - p, record = false )))
3442 val default = defaultCase(withDefaultsTransformed, mutations.map(_.id).toNonEmptyList)
3543
3644 val cases = mutableCases :+ default
3745
38- try
39- buildMatch(cases)
40- catch {
41- case NonFatal (e) =>
46+ val mutationSwitch = Either
47+ .catchNonFatal(buildMatch(cases))
48+ .valueOr { e =>
4249 log.error(
4350 s " Failed to instrument mutants in ` ${context.path}`. Original statement: [ ${originalTree.text}] "
4451 )
@@ -50,12 +57,17 @@ class MutantInstrumenter(options: InstrumenterOptions)(implicit log: Logger) {
5057 e
5158 )
5259 throw UnableToBuildPatternMatchException (context.path)
53- }
60+ }
61+
62+ if (record)
63+ spliceReplacements += SourceReplacement (p.tree.begOffset, p.tree.endOffset, mutationSwitch)
64+
65+ mutationSwitch
5466 }
5567 }
5668 }
5769
58- val newTree = Try (context.source.transformOnce(instrumentWithMutants(mutantMap))) match {
70+ val newTree = Try (context.source.transformOnce(instrumentWithMutants(mutantMap, record = true ))) match {
5971 case Success (tree) => tree
6072 case Failure (e : Stryker4sException ) => throw e
6173 case Failure (e) =>
@@ -64,8 +76,9 @@ class MutantInstrumenter(options: InstrumenterOptions)(implicit log: Logger) {
6476 }
6577
6678 val mutations : MutantsWithId = mutantMap.map(_._2).toVector.toNev.get.flatten
79+ val splice = spliceReplacements.result().toNes.map(SourceSplice (context.source.pos.input.text, _))
6780
68- MutatedFile (context.path, newTree, mutations)
81+ MutatedFile (context.path, newTree, mutations, splice )
6982 }
7083
7184 def mutantToCase (mutant : MutantWithId ): Case = {
0 commit comments