I'm trying to rename the parameter of an anonymous function using a semantic scalafix plugin. The relevant code looks like this:
case Term.Apply(func, args) =>
args.collect { case Term.Block(List(Term.Function(List(arg), _))) =>
Patch.replaceTree(arg, arg.copy(name = Term.Name("components")).toString())
The problem is, this is changing { implicit foo => to { components => (i.e. it's dropping the implicit modifier). I initially thought it was being dropped by the copy method for some reason, but I added some printlns and that's not the case: the implicit modifier exists on the copy, but just isn't being included in the toString output. Anyone know what's going on here? And how I can get the implicit to be included in the output?
printlns:
println("***********ORIGINAL***********")
println("toString:\t" + arg.toString())
println("name:\t\t" + arg.name)
println("modifiers:\t" + arg.mods)
println("syntax:\t\t" + arg.syntax)
println("structure:\t" + arg.structure)
println("***********COPY***********")
val copy = arg.copy(name = Term.Name("components"))
println("toString:\t" + copy.toString())
println("name:\t\t" + copy.name)
println("modifiers:\t" + copy.mods)
println("syntax:\t\t" + copy.syntax)
println("structure:\t" + copy.structure)
output:
***********ORIGINAL***********
toString: implicit app
name: app
modifiers: List(implicit)
syntax: implicit app
structure: Term.Param(List(Mod.Implicit), Term.Name("app"), None, None)
***********COPY***********
toString: components
name: components
modifiers: List(implicit)
syntax: components
structure: Term.Param(List(Mod.Implicit), Term.Name("components"), None, None)
(notice that the copy has implicit in its list of modifiers, but it doesn't show up in the outputs of toString or syntax)
The thing is that when Scalameta (4.5.13) prints a
Term.Paramit skipsMod.ImplicitandMod.UsingThen it prints
List[List[Term.Param]]correctlybut this doesn't help us.
The easiest fix is just to add
implicitwhen necessaryBecause Scalameta prints differently newly parsed trees and transformed/generated trees. For the former it preserves their original string representation with original formatting. For the latter it prints them with corresponding instance of
scala.meta.prettyprinters.Showi.e. skipsimplicitfor a parameter etc.arg.toStringcallsscala.meta.internal.prettyprinters.TreeSyntax.apply[Term.Param](Scala213).apply(arg).The method
TreeSyntax.applyisHere in the pattern matching for
Origin.Parsed(the origin of a newly parsed tree) the method returnsResult.Str, forOrigin.None(the origin of a transformed/generated tree) it returnsResult.Sequence.The method
scala.meta.internal.trees.InternalTree#originisprivate[meta]so if you play with it put your rule into the packagescala.meta.Term.Paramis not a case class and.copyis not a method of a case class.argandresare actually instances of macro-generated classTerm.Param.TermParamImpl.