From 13935bf18409918836657ce482bfad1b87543741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=93lafur=20P=C3=A1ll=20Geirsson?= Date: Tue, 13 Dec 2016 12:30:24 +0100 Subject: [PATCH] WIP --- .../main/scala/scalafix/rewrite/Rewrite.scala | 2 +- .../scala/scalafix/rewrite/SemanticApi.scala | 4 +++ .../scala/scalafix/rewrite/Xor2Either.scala | 24 ++++++++++++++ core/src/test/resources/Xor/basic.source | 10 ++++++ .../scala/scalafix/nsc/NscSemanticApi.scala | 32 +++++++++++++++++++ .../src/test/scala/cats/data/Xor.scala | 8 +++++ 6 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 core/src/main/scala/scalafix/rewrite/Xor2Either.scala create mode 100644 core/src/test/resources/Xor/basic.source create mode 100644 scalafix-nsc/src/test/scala/cats/data/Xor.scala diff --git a/core/src/main/scala/scalafix/rewrite/Rewrite.scala b/core/src/main/scala/scalafix/rewrite/Rewrite.scala index 77a5c3cbeb..e9af5fdb3d 100644 --- a/core/src/main/scala/scalafix/rewrite/Rewrite.scala +++ b/core/src/main/scala/scalafix/rewrite/Rewrite.scala @@ -17,7 +17,7 @@ object Rewrite { } val syntaxRewrites: Seq[Rewrite] = Seq(ProcedureSyntax, VolatileLazyVal) - val semanticRewrites: Seq[Rewrite] = Seq(ExplicitImplicit) + val semanticRewrites: Seq[Rewrite] = Seq(ExplicitImplicit, Xor2Either) val allRewrites: Seq[Rewrite] = syntaxRewrites ++ semanticRewrites val name2rewrite: Map[String, Rewrite] = allRewrites.map(x => x.toString -> x).toMap diff --git a/core/src/main/scala/scalafix/rewrite/SemanticApi.scala b/core/src/main/scala/scalafix/rewrite/SemanticApi.scala index dfd0b5262c..d938249656 100644 --- a/core/src/main/scala/scalafix/rewrite/SemanticApi.scala +++ b/core/src/main/scala/scalafix/rewrite/SemanticApi.scala @@ -1,7 +1,9 @@ package scalafix.rewrite import scala.meta.Defn +import scala.meta.Tree import scala.meta.Type +import scala.meta.parsers.Parse /** A custom semantic api for scalafix rewrites. * @@ -18,4 +20,6 @@ trait SemanticApi { /** Returns the type annotation for given val/def. */ def typeSignature(defn: Defn): Option[Type] + + def desugared[A <: Tree](tree: A)(implicit parse: Parse[A]): Option[A] } diff --git a/core/src/main/scala/scalafix/rewrite/Xor2Either.scala b/core/src/main/scala/scalafix/rewrite/Xor2Either.scala new file mode 100644 index 0000000000..94e4ce8aa5 --- /dev/null +++ b/core/src/main/scala/scalafix/rewrite/Xor2Either.scala @@ -0,0 +1,24 @@ +package scalafix.rewrite + +import scala.{meta => m} +import scalafix.util.Patch +import scalafix.util.Whitespace +import scalafix.util.logger + +case object Xor2Either extends Rewrite { + override def rewrite(ast: m.Tree, ctx: RewriteCtx): Seq[Patch] = { + import scala.meta._ + val semantic = getSemanticApi(ctx) + ast.collect { + case t: m.Type.Ref + if semantic + .desugared(t: m.Type)(m.parsers.Parse.parseType) + .exists(_.syntax.contains("Xor")) => + logger.elem(t.syntax, semantic.desugared(t: Type)) + val tok = t.tokens.head + Seq( + Patch(tok, tok, tok.syntax + "BANANA") + ) + }.flatten + } +} diff --git a/core/src/test/resources/Xor/basic.source b/core/src/test/resources/Xor/basic.source new file mode 100644 index 0000000000..5dadce393b --- /dev/null +++ b/core/src/test/resources/Xor/basic.source @@ -0,0 +1,10 @@ +rewrites = [Xor2Either] +<<< ONLY xor 1 +import cats.data.Xor +trait A { + val r: Xor[Int, String] +} +>>> +trait A { + val r: Either[Int, String] +} diff --git a/scalafix-nsc/src/main/scala/scalafix/nsc/NscSemanticApi.scala b/scalafix-nsc/src/main/scala/scalafix/nsc/NscSemanticApi.scala index 0a849dc518..c49302f817 100644 --- a/scalafix-nsc/src/main/scala/scalafix/nsc/NscSemanticApi.scala +++ b/scalafix-nsc/src/main/scala/scalafix/nsc/NscSemanticApi.scala @@ -2,7 +2,9 @@ package scalafix.nsc import scala.collection.mutable import scala.meta.Dialect +import scala.meta.Tree import scala.meta.Type +import scala.meta.parsers.Parse import scala.reflect.internal.util.SourceFile import scala.{meta => m} import scalafix.Fixed @@ -97,6 +99,18 @@ trait NscSemanticApi extends ReflectToolkit { builder } + private def collect[T](gtree: g.Tree)( + pf: PartialFunction[g.Tree, T]): Seq[T] = { + val builder = Seq.newBuilder[T] + val f = pf.lift + def iter(gtree: g.Tree): Unit = { + f(gtree).foreach(builder += _) + gtree.children.foreach(iter) + } + iter(gtree) + builder.result() + } + private def getSemanticApi(unit: g.CompilationUnit, config: ScalafixConfig): SemanticApi = { val offsets = offsetToType(unit.body, config.dialect) @@ -111,6 +125,24 @@ trait NscSemanticApi extends ReflectToolkit { None } } + + override def desugared[T <: Tree](tree: T)( + implicit parse: Parse[T]): Option[T] = { + logger.elem(tree, unit.body) + val result = collect[Option[T]](unit.body) { + case t + if t.pos.isDefined && + t.pos.start == tree.pos.start.offset => + import scala.meta._ + parse(m.Input.String(t.toString()), config.dialect) match { + case Parsed.Success(x) => Some(x) + case _ => None + } + case t if { logger.elem(t.toString(), g.showRaw(t)); false } => None + }.flatten + logger.elem(result) + result.headOption + } } } diff --git a/scalafix-nsc/src/test/scala/cats/data/Xor.scala b/scalafix-nsc/src/test/scala/cats/data/Xor.scala new file mode 100644 index 0000000000..9aa8b65980 --- /dev/null +++ b/scalafix-nsc/src/test/scala/cats/data/Xor.scala @@ -0,0 +1,8 @@ +package cats.data + +sealed abstract class Xor[+A, +B] extends Product with Serializable + +object Xor { + final case class Left[+A](a: A) extends (A Xor Nothing) + final case class Right[+B](b: B) extends (Nothing Xor B) +}