scalacenter / scalafix

Refactoring and linting tool for Scala
https://scalacenter.github.io/scalafix/
BSD 3-Clause "New" or "Revised" License
831 stars 186 forks source link

More flexible tree rewriting rules? #550

Closed erik-stripe closed 6 years ago

erik-stripe commented 6 years ago

Hi folks,

Apologies if I misunderstood the documentation/examples, but I didn't see an obvious way to pattern match trees and then produce new (potentially unrelated) trees using subtrees that were bound during the match.

Here's a fairly concrete example:

// before
_root_.scala.Predef.doubleWrapper($x).min($y)

// after
_root_.java.lang.Math.min($x, $y)

Is there a preexisting rule that does this? If not, do you think it's possible to define a rule like this?

gabro commented 6 years ago

In the specific case, I would do something like

val richDoubleMin = SymbolMatcher.normalized(Symbol("_root_.scala.runtime.RichDouble.min."))
ctx.tree.collect {
  case t @ Term.ApplyInfix(lhs, richDoubleMin(_), targs, List(rhs)) =>
    ctx.replaceTree(t,  q"_root_.java.lang.Math.min($lhs, $rhs)".syntax)
  }.asPatch
}

This will rewrite

1.2 min 2.2

to

_root_.java.lang.Math.min(1.2, 2.2)

In the above example, the quasi-quote is re-using the matched sub-trees. Is that what you were looking for?

erik-stripe commented 6 years ago

Yes, I think I can use that to write the rules I want.

Thanks for your help!

gabro commented 6 years ago

Cool. I'm going to close this, if you have have further problems we can re-open it.

olafurpg commented 6 years ago

@erik-stripe tree rewriting facilities are still quite primitive, beware that .syntax on synthetic trees (quasiquotes) will not preserve comments or formatting in the spliced tree nodes. There is no "easy way" to implement a robust rewrite yet, the token api forces you on a much lower level

I'm aiming to work on https://github.com/scalacenter/scalafix/issues/502 in the coming few weeks which should hopefully make tree rewriting more robust.