Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RenameInjectProdAndCoproduct, RenameTupleApplySyntax and RemoveSplit Scalafix rewrites #1813

Merged
merged 6 commits into from
Aug 11, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions scalafix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
Install the scalafix sbt plugin (globally or in a specific project):

```scala
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.0-M2")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.0-M3")
```

run
Expand All @@ -26,20 +26,19 @@ sbt scalafix github:typelevel/cats/v1.0.0

- [x] traverse1_, intercalate1 and sequence1_ in Reducible were renamed to nonEmptyTraverse_, nonEmptyIntercalate and nonEmptySequence_ respectively.

- [x] cats.free.Inject is moved from cats-free to cats-core and renamed to cats.InjectK; cats.data.Prod is renamed to cats.data.Tuple2K; cats.data.Coproduct is renamed to cats.data.EitherK

- [x] Apply syntax on tuple (e.g. (x, y, z).map3(...)) was moved from cats.syntax.tuple._ to cats.syntax.apply._ and renamed to mapN, contramapN and imapN respectively.

- [x] Split is removed, and the method split is moved to Arrow. Note that only under CommutativeArrow does it guarantee the non-interference between the effects. see #1567
# WIP

- [ ] cats no longer publishes the all-inclusive bundle package "org.typelevel" % "cats", use cats-core, cats-free, or cats-law accordingly instead. If you need cats.free, use "org.typelevel" % "cats-free", if you need cats-laws use "org.typelevel" % "cats-laws", if neither, use "org.typelevel" % "cats-core".

- [ ] cats.free.Inject is moved from cats-free to cats-core and renamed to cats.InjectK; cats.data.Prod is renamed to cats.data.Tuple2K; cats.data.Coproduct is renamed to cats.data.EitherK

- [ ] FunctorFilter, MonadCombine, MonadFilter, MonadReader, MonadState, MonadTrans, MonadWriter and TraverseFilter are no longer in cats, the functionalities they provided are inhereted by the new cats-mtl project. Please check here for migration guide.

- [ ] Apply syntax on tuple (e.g. (x, y, z).map3(...)) was moved from cats.syntax.tuple._ to cats.syntax.apply._ and renamed to mapN, contramapN and imapN respectively.

- [ ] Several cats-core type class instances for cats.kernel were moved from their companion objects to separate traits and thus require imports from cats.instances.xxx._ (or the recommended import cats.implicits._) now. See #1659 for more details.

- [ ] foldLeftM is removed from Free, use foldM on Foldable instead, see #1117 for detail.

- [ ] iteratorFoldM was removed from Foldable due to #1716

- [ ] Split is removed, and the method split is moved to Arrow. Note that only under CommutativeArrow does it guarantee the non-interference between the effects. see #1567
15 changes: 9 additions & 6 deletions scalafix/build.sbt
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
// Use a scala version supported by scalafix.
scalaVersion in ThisBuild := org.scalameta.BuildInfo.supportedScalaVersions.last
val V = _root_.scalafix.Versions
scalaVersion in ThisBuild := V.scala212

lazy val rewrites = project.settings(
libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % "0.5.0-M2"
libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % V.version
)

lazy val input = project.settings(
scalametaSourceroot := sourceDirectory.in(Compile).value,
scalafixSourceroot := sourceDirectory.in(Compile).value,
libraryDependencies ++= Seq(
"org.typelevel" %% "cats" % "0.9.0"
)
),
scalacOptions += "-language:higherKinds"
)

lazy val output = project.settings(
libraryDependencies ++= Seq(
"org.typelevel" %% "cats-core" % "1.0.0-MF",
"org.typelevel" %% "cats-free" % "1.0.0-MF"
)
),
scalacOptions += "-language:higherKinds"
)

lazy val tests = project
.settings(
libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % "0.5.0-M2" % Test cross CrossVersion.full,
libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % V.version % Test cross CrossVersion.full,
buildInfoPackage := "fix",
buildInfoKeys := Seq[BuildInfoKey](
"inputSourceroot" ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* ONLY
/*
rewrite = "scala:fix.v1_0_0.RemoveCartesianBuilder"
*/
package fix
Expand Down
17 changes: 17 additions & 0 deletions scalafix/input/src/main/scala/fix/v1_0_0/RemoveSplit.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
rewrite = "scala:fix.v1_0_0.RemoveSplit"
*/
package fix
package to1_0_0

import cats.implicits._
import cats.arrow.Split
import cats.syntax.split._
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The split syntax isn't actually used below, and if it was used it, this import would clash with cats.implicits._.

If you want to test the split syntax you would have

import cats.syntax.split._ // afterwards => import cats.syntax.arrow._
import cats.instances.function._

val f = toLong split toDouble

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, good catch, thanks. That's a half-baked attempt that I accidentally left in there.
I just pushed a proper version of that fix.

By the way, it'd be nice if scalafix tests were run by the CI (so that these kind of mistake would be caught). Do you think it's possible to add them to the build?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should add them to the build. We can tackle it in another PR.


object RemoveSplitTests {
val toLong: Int => Long = _.toLong
val toDouble: Float => Double = _.toDouble
val f: ((Int, Float)) => (Long, Double) =
Split[Function1].split(toLong, toDouble)
f((3, 4.0f))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
rewrite = "scala:fix.v1_0_0.RenameInjectProdAndCoproduct"
*/
package fix
package to1_0_0

import cats.free.Inject
import cats.data.{ Coproduct, Prod }

object RenameInjectProdAndCoproductTests {
def inject[F[_], G[_]](implicit inj: Inject[F, G]) = ???
def prod[F[_], G[_], A](implicit prod: Prod[F, G, A]) = ???
def coprod[F[_], G[_], A](implicit coprod: Coproduct[F, G, A]) = ???
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
rewrite = "scala:fix.v1_0_0.RenameTupleApplySyntax"
*/
package fix
package to1_0_0

object RenameTupleApplySyntaxTests {
import cats.{Eq, Semigroup}
import cats.instances.all._
import cats.syntax.tuple._

(Option(1), Option(2)).map2(_ + _)
(Option(1), Option(2), Option(3)).map3(_ + _ + _)
(Option(1), Option(2), Option(3), Option(4)).map4(_ + _ + _ + _)

case class Foo2(a: Int, b: Int)
case class Foo3(a: Int, b: Int, c: Int)
case class Foo4(a: Int, b: Int, c: Int, d: Int)

(Eq[Int], Eq[Int]).contramap2((f: Foo2) => (f.a, f.b))
(Eq[Int], Eq[Int], Eq[Int]).contramap3((f: Foo3) => (f.a, f.b, f.c))
(Eq[Int], Eq[Int], Eq[Int], Eq[Int]).contramap4((f: Foo4) =>
(f.a, f.b, f.c, f.d))

(Semigroup[Int], Semigroup[Int])
.imap2(Foo2.apply)(Function.unlift(Foo2.unapply))
(Semigroup[Int], Semigroup[Int], Semigroup[Int])
.imap3(Foo3.apply)(Function.unlift(Foo3.unapply))
(Semigroup[Int], Semigroup[Int], Semigroup[Int], Semigroup[Int])
.imap4(Foo4.apply)(Function.unlift(Foo4.unapply))
}
13 changes: 13 additions & 0 deletions scalafix/output/src/main/scala/fix/v1_0_0/RemoveSplit.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package fix
package to1_0_0

import cats.implicits._
import cats.arrow.Arrow

object RemoveSplitTests {
val toLong: Int => Long = _.toLong
val toDouble: Float => Double = _.toDouble
val f: ((Int, Float)) => (Long, Double) =
Arrow[Function1].split(toLong, toDouble)
f((3, 4.0f))
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package fix
package to1_0_0

import cats.InjectK
import cats.data.{ EitherK, Tuple2K }

object RenameInjectProdAndCoproductTests {
def inject[F[_], G[_]](implicit inj: InjectK[F, G]) = ???
def prod[F[_], G[_], A](implicit prod: Tuple2K[F, G, A]) = ???
def coprod[F[_], G[_], A](implicit coprod: EitherK[F, G, A]) = ???
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package fix
package to1_0_0

object RenameTupleApplySyntaxTests {
import cats.{Eq, Semigroup}
import cats.instances.all._
import cats.syntax.apply._

(Option(1), Option(2)).mapN(_ + _)
(Option(1), Option(2), Option(3)).mapN(_ + _ + _)
(Option(1), Option(2), Option(3), Option(4)).mapN(_ + _ + _ + _)

case class Foo2(a: Int, b: Int)
case class Foo3(a: Int, b: Int, c: Int)
case class Foo4(a: Int, b: Int, c: Int, d: Int)

(Eq[Int], Eq[Int]).contramapN((f: Foo2) => (f.a, f.b))
(Eq[Int], Eq[Int], Eq[Int]).contramapN((f: Foo3) => (f.a, f.b, f.c))
(Eq[Int], Eq[Int], Eq[Int], Eq[Int]).contramapN((f: Foo4) =>
(f.a, f.b, f.c, f.d))

(Semigroup[Int], Semigroup[Int])
.imapN(Foo2.apply)(Function.unlift(Foo2.unapply))
(Semigroup[Int], Semigroup[Int], Semigroup[Int])
.imapN(Foo3.apply)(Function.unlift(Foo3.unapply))
(Semigroup[Int], Semigroup[Int], Semigroup[Int], Semigroup[Int])
.imapN(Foo4.apply)(Function.unlift(Foo4.unapply))
}
2 changes: 1 addition & 1 deletion scalafix/project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.0-M2")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.0-M3")
addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0-RC3")
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.6.1")
addSbtPlugin("org.lyranthe.sbt" % "partial-unification" % "1.0.0")
90 changes: 70 additions & 20 deletions scalafix/rewrites/src/main/scala/fix/Cats_v1_0_0.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,37 @@ object Utils {
private[fix] def rename(
ctx: RewriteCtx,
t: Term.Name,
renames: Map[String, String])(implicit mirror: Mirror): Patch = {
renames: Map[String, String])(implicit semanticCtx: SemanticCtx): Patch = {
renames.collect {
case (target, rename) if t.isSymbol(target) =>
ctx.replaceTree(t, rename)
}.asPatch
}

implicit class TermNameOps(t: Name) {
def isSymbol(s: String)(implicit mirror: Mirror): Boolean =
t.symbolOpt.exists(_.normalized.syntax == s)
def isSymbol(s: String)(implicit semanticCtx: SemanticCtx): Boolean =
t.symbol.exists(_.normalized.syntax == s)

def isOneOfSymbols(symbols: Set[String])(implicit mirror: Mirror): Boolean =
t.symbolOpt.exists(s => symbols.contains(s.normalized.syntax))
def isOneOfSymbols(symbols: Set[String])(implicit semanticCtx: SemanticCtx): Boolean =
t.symbol.exists(s => symbols.contains(s.normalized.syntax))
}

implicit class OptionTermNameOps(t: Option[Name]) {
def isSymbol(s: String)(implicit mirror: Mirror): Boolean =
t.flatMap(_.symbolOpt).exists(_.normalized.syntax == s)
def isSymbol(s: String)(implicit semanticCtx: SemanticCtx): Boolean =
t.flatMap(_.symbol).exists(_.normalized.syntax == s)
}

}
import Utils._

// ref: https://github.com/typelevel/cats/pull/1745
case class RemoveCartesianBuilder(mirror: Mirror)
extends SemanticRewrite(mirror) {
case class RemoveCartesianBuilder(semanticCtx: SemanticCtx)
extends SemanticRewrite(semanticCtx) {

private[this] val cartesianBuilders =
(1 to 22)
.map(arity =>
s"_root_.cats.syntax.CartesianBuilder#CartesianBuilder$arity.`|@|`.")
s"_root_.cats.syntax.CartesianBuilder.CartesianBuilder$arity.`|@|`.")
.toSet +
"_root_.cats.syntax.CartesianOps.`|@|`."

Expand All @@ -53,9 +53,9 @@ case class RemoveCartesianBuilder(mirror: Mirror)
(1 to 22)
.map { arity =>
Seq(
s"_root_.cats.syntax.CartesianBuilder#CartesianBuilder$arity.map." -> "mapN",
s"_root_.cats.syntax.CartesianBuilder#CartesianBuilder$arity.imap." -> "imapN",
s"_root_.cats.syntax.CartesianBuilder#CartesianBuilder$arity.contramap." -> "contramapN"
s"_root_.cats.syntax.CartesianBuilder.CartesianBuilder$arity.map." -> "mapN",
s"_root_.cats.syntax.CartesianBuilder.CartesianBuilder$arity.imap." -> "imapN",
s"_root_.cats.syntax.CartesianBuilder.CartesianBuilder$arity.contramap." -> "contramapN"
)
}
.flatten
Expand Down Expand Up @@ -86,7 +86,8 @@ case class RemoveCartesianBuilder(mirror: Mirror)
}

private[this] def wrapInParensIfNeeded(ctx: RewriteCtx, t: Term): Patch = {
if (t.tokens.head.is[Token.LeftParen] && t.tokens.last.is[Token.RightParen]) {
if (t.tokens.head.is[Token.LeftParen] && t.tokens.last
.is[Token.RightParen]) {
Patch.empty
} else {
ctx.addLeft(t.tokens.head, "(") + ctx.addRight(t.tokens.last, ")")
Expand All @@ -102,7 +103,7 @@ case class RemoveCartesianBuilder(mirror: Mirror)
case t: Term.Name => rename(ctx, t, renames)
case t @ q"import cats.syntax.cartesian._" =>
val usesPartialApplies = ctx.tree.collect {
case t: Term.Name if t.isOneOfSymbols(partialApplies) => ()
case t: Term.Name if t.isOneOfSymbols(partialApplies) => ()
}.length > 0
if (usesPartialApplies) {
ctx.addRight(t.tokens.last, "\n import cats.syntax.apply._")
Expand All @@ -114,7 +115,7 @@ case class RemoveCartesianBuilder(mirror: Mirror)
}

// ref: https://github.com/typelevel/cats/pull/1583
case class RemoveUnapply(mirror: Mirror) extends SemanticRewrite(mirror) {
case class RemoveUnapply(semanticCtx: SemanticCtx) extends SemanticRewrite(semanticCtx) {

private[this] val renames = Map(
"_root_.cats.Traverse.Ops.traverseU." -> "traverse",
Expand Down Expand Up @@ -152,7 +153,7 @@ case class RemoveUnapply(mirror: Mirror) extends SemanticRewrite(mirror) {
}

// ref: https://github.com/typelevel/cats/pull/1709
case class RenameFreeSuspend(mirror: Mirror) extends SemanticRewrite(mirror) {
case class RenameFreeSuspend(semanticCtx: SemanticCtx) extends SemanticRewrite(semanticCtx) {

private[this] val renames = Map(
"_root_.cats.free.Free.suspend." -> "defer",
Expand All @@ -168,8 +169,8 @@ case class RenameFreeSuspend(mirror: Mirror) extends SemanticRewrite(mirror) {
}

// ref: https://github.com/typelevel/cats/pull/1611
case class RenameReducibleMethods(mirror: Mirror)
extends SemanticRewrite(mirror) {
case class RenameReducibleMethods(semanticCtx: SemanticCtx)
extends SemanticRewrite(semanticCtx) {

private[this] val renames = Map(
"_root_.cats.Reducible.traverse1_." -> "nonEmptyTraverse_",
Expand All @@ -189,7 +190,7 @@ case class RenameReducibleMethods(mirror: Mirror)
}

// ref: https://github.com/typelevel/cats/pull/1614
case class SimplifyEitherTLift(mirror: Mirror) extends SemanticRewrite(mirror) {
case class SimplifyEitherTLift(semanticCtx: SemanticCtx) extends SemanticRewrite(semanticCtx) {

private[this] val leftSymbol = "_root_.cats.data.EitherTFunctions.left."
private[this] val rightSymbol = "_root_.cats.data.EitherTFunctions.right."
Expand All @@ -216,3 +217,52 @@ case class SimplifyEitherTLift(mirror: Mirror) extends SemanticRewrite(mirror) {
}

}

// ref: https://github.com/typelevel/cats/pull/1589
// https://github.com/typelevel/cats/pull/1596
case class RenameInjectProdAndCoproduct(semanticCtx: SemanticCtx) extends SemanticRewrite(semanticCtx) {

def rewrite(ctx: RewriteCtx): Patch = {
ctx.replaceSymbols(
"_root_.cats.free.Inject." -> "_root_.cats.InjectK.",
"_root_.cats.data.Prod." -> "_root_.cats.data.Tuple2K.",
"_root_.cats.data.Coproduct." -> "_root_.cats.data.EitherK."
)
}

}

// ref: https://github.com/typelevel/cats/pull/1487
case class RenameTupleApplySyntax(semanticCtx: SemanticCtx) extends SemanticRewrite(semanticCtx) {

private[this] val renames: Map[String, String] =
(1 to 22)
.map { arity =>
Seq(
s"_root_.cats.syntax.Tuple${arity}CartesianOps.map$arity." -> "mapN",
s"_root_.cats.syntax.Tuple${arity}CartesianOps.contramap$arity." -> "contramapN",
s"_root_.cats.syntax.Tuple${arity}CartesianOps.imap$arity." -> "imapN"
)
}
.flatten
.toMap

def rewrite(ctx: RewriteCtx): Patch = {
ctx.tree.collect {
case t: Term.Name => rename(ctx, t, renames)
case t @ q"import cats.syntax.tuple._" =>
ctx.replaceTree(t, "import cats.syntax.apply._")
}.asPatch
}
}

// ref: https://github.com/typelevel/cats/pull/1766
case class RemoveSplit(semanticCtx: SemanticCtx) extends SemanticRewrite(semanticCtx) {

def rewrite(ctx: RewriteCtx): Patch = {
ctx.replaceSymbols(
"_root_.cats.arrow.Split." -> "_root_.cats.arrow.Arrow."
)
}

}