-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add RemoveUnapply and RemoveCartesianBuilder Scalafix rewrites
- Loading branch information
Showing
13 changed files
with
378 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
align = none |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Use a scala version supported by scalafix. | ||
scalaVersion in ThisBuild := org.scalameta.BuildInfo.supportedScalaVersions.last | ||
|
||
lazy val rewrites = project.settings( | ||
libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % "0.5.0-M1" | ||
) | ||
|
||
lazy val input = project.settings( | ||
scalametaSourceroot := sourceDirectory.in(Compile).value, | ||
libraryDependencies ++= Seq( | ||
"org.typelevel" %% "cats" % "0.9.0" | ||
) | ||
) | ||
|
||
lazy val output = project.settings( | ||
libraryDependencies ++= Seq( | ||
"org.typelevel" %% "cats-core" % "1.0.0-MF", | ||
"org.typelevel" %% "cats-free" % "1.0.0-MF" | ||
) | ||
) | ||
|
||
lazy val tests = project | ||
.settings( | ||
libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % "0.5.0-M1" % Test cross CrossVersion.full, | ||
buildInfoPackage := "fix", | ||
buildInfoKeys := Seq[BuildInfoKey]( | ||
"inputSourceroot" -> | ||
sourceDirectory.in(input, Compile).value, | ||
"outputSourceroot" -> | ||
sourceDirectory.in(output, Compile).value, | ||
"inputClassdirectory" -> | ||
classDirectory.in(input, Compile).value | ||
) | ||
) | ||
.dependsOn(input, rewrites) | ||
.enablePlugins(BuildInfoPlugin) |
31 changes: 31 additions & 0 deletions
31
scalafix/input/src/main/scala/fix/to1_0_0/RemoveCartesianBuilder.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
/* | ||
rewrite = "scala:fix.to1_0_0.RemoveCartesianBuilder" | ||
*/ | ||
package fix | ||
package to1_0_0 | ||
|
||
object RemoveCartesianBuilderTests { | ||
{ | ||
import cats.instances.option._ | ||
import cats.syntax.cartesian._ | ||
val o1: Option[Int] = Some(42) | ||
val o2: Option[String] = Some("hello") | ||
val o3: Option[Int] = Some(2) | ||
(o1 |@| o2).map((i: Int, s: String) => i.toString ++ s) | ||
(o1 |@| o2).tupled | ||
(o1 |@| o2 |@| o3).map(_ + _ + _) | ||
} | ||
|
||
{ | ||
import cats.{Semigroup, Eq} | ||
import cats.implicits._ | ||
case class Foo(a: String, c: List[Double]) | ||
|
||
(Semigroup[String] |@| Semigroup[List[Double]]) | ||
.imap(Foo.apply)(Function.unlift(Foo.unapply)) | ||
|
||
(Eq[Double] |@| Eq[String]).contramap { (a: Foo) => | ||
(2, "bar") | ||
} | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
scalafix/input/src/main/scala/fix/to1_0_0/RemoveUnapply.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
rewrite = "scala:fix.to1_0_0.RemoveUnapply" | ||
*/ | ||
package fix | ||
package to1_0_0 | ||
|
||
object RemoveUnapplyTests { | ||
import cats.implicits._ | ||
import cats.Foldable | ||
def parseInt(s: String): Either[String, Int] = | ||
Either.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number") | ||
val ns = List("1", "2", "3") | ||
ns.traverseU(parseInt) | ||
ns.traverseU_(parseInt) | ||
Foldable[List].traverseU_(ns)(parseInt) | ||
|
||
import cats.data.{Validated, ValidatedNel} | ||
val x: List[ValidatedNel[String, Int]] = | ||
List(Validated.valid(1), Validated.invalid("a"), Validated.invalid("b")) | ||
.map(_.toValidatedNel) | ||
x.sequenceU | ||
x.sequenceU_ | ||
Foldable[List].sequenceU_(x) | ||
|
||
import cats.data.Func.{appFuncU, appFunc} | ||
import cats.data.State.{get, set} | ||
import cats.data.Const | ||
type Count[A] = Const[Int, A] | ||
def liftInt(i: Int): Count[Unit] = Const(i) | ||
def isSpace(c: Char): Boolean = (c == ' ' || c == '\n') | ||
def testIf(b: Boolean): Int = if (b) 1 else 0 | ||
appFuncU { (c: Char) => | ||
for { | ||
x <- get[Boolean] | ||
y = !isSpace(c) | ||
_ <- set(y) | ||
} yield testIf(y && !x) | ||
} andThen appFunc(liftInt) | ||
|
||
import cats.free.FreeT | ||
val a: Either[String, Int] = Right(42) | ||
FreeT.liftTU(a) | ||
|
||
} |
28 changes: 28 additions & 0 deletions
28
scalafix/output/src/main/scala/fix/to1_0_0/RemoveCartesianBuilder.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package fix | ||
package to1_0_0 | ||
|
||
object RemoveCartesianBuilderTests { | ||
{ | ||
import cats.instances.option._ | ||
import cats.syntax.apply._ | ||
val o1: Option[Int] = Some(42) | ||
val o2: Option[String] = Some("hello") | ||
val o3: Option[Int] = Some(2) | ||
(o1, o2).mapN((i: Int, s: String) => i.toString ++ s) | ||
(o1, o2).tupled | ||
(o1, o2, o3).mapN(_ + _ + _) | ||
} | ||
|
||
{ | ||
import cats.{Semigroup, Eq} | ||
import cats.implicits._ | ||
case class Foo(a: String, c: List[Double]) | ||
|
||
(Semigroup[String], Semigroup[List[Double]]) | ||
.imapN(Foo.apply)(Function.unlift(Foo.unapply)) | ||
|
||
(Eq[Double], Eq[String]).contramapN { (a: Foo) => | ||
(2, "bar") | ||
} | ||
} | ||
} |
41 changes: 41 additions & 0 deletions
41
scalafix/output/src/main/scala/fix/to1_0_0/RemoveUnapply.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
package fix | ||
package to1_0_0 | ||
|
||
object RemoveUnapplyTests { | ||
import cats.implicits._ | ||
import cats.Foldable | ||
def parseInt(s: String): Either[String, Int] = | ||
Either.catchOnly[NumberFormatException](s.toInt).leftMap(_ => "no number") | ||
val ns = List("1", "2", "3") | ||
ns.traverse(parseInt) | ||
ns.traverse_(parseInt) | ||
Foldable[List].traverse_(ns)(parseInt) | ||
|
||
import cats.data.{Validated, ValidatedNel} | ||
val x: List[ValidatedNel[String, Int]] = | ||
List(Validated.valid(1), Validated.invalid("a"), Validated.invalid("b")) | ||
.map(_.toValidatedNel) | ||
x.sequence | ||
x.sequence_ | ||
Foldable[List].sequence_(x) | ||
|
||
import cats.data.Func.appFunc | ||
import cats.data.State.{get, set} | ||
import cats.data.Const | ||
type Count[A] = Const[Int, A] | ||
def liftInt(i: Int): Count[Unit] = Const(i) | ||
def isSpace(c: Char): Boolean = (c == ' ' || c == '\n') | ||
def testIf(b: Boolean): Int = if (b) 1 else 0 | ||
appFunc { (c: Char) => | ||
for { | ||
x <- get[Boolean] | ||
y = !isSpace(c) | ||
_ <- set(y) | ||
} yield testIf(y && !x) | ||
} andThen appFunc(liftInt) | ||
|
||
import cats.free.FreeT | ||
val a: Either[String, Int] = Right(42) | ||
FreeT.liftT(a) | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
sbt.version=0.13.13 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.0-M1") | ||
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.0-M1") | ||
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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
# Scalafix rewrites for cats | ||
|
||
## 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 | ||
|
||
- [x] All Unapply enabled methods, e.g. sequenceU, traverseU, etc. are removed. Unapply enabled syntax ops are also removed. Please use the partial unification SI-2712 fix instead. The easiest way might be this sbt-plugin. | ||
|
||
- [ ] 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. | ||
|
||
- [x] CartesianBuilder (i.e. |@|) syntax is deprecated, use the apply syntax on tuples instead. E.g. (x |@| y |@| z).map(...) should be replaced by (x, y, z).mapN(...) | ||
|
||
- [ ] 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. | ||
|
||
- [ ] The creation methods (left, right, apply, pure, etc.) in EitherT were improved to take less type arguments. | ||
|
||
- [ ] 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. | ||
|
||
- [ ] Free.suspend is renamed to Free.defer for consistency. | ||
|
||
- [ ] traverse1_, intercalate1 and sequence1_ in Reducible were renamed to nonEmptyTraverse_, nonEmptyIntercalate and nonEmptySequence_ respectively. | ||
|
||
- [ ] 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
package fix | ||
package to1_0_0 | ||
|
||
import scalafix._ | ||
import scala.meta._ | ||
|
||
case class All(mirror: Mirror) extends SemanticRewrite(mirror) { | ||
|
||
def rewrite(ctx: RewriteCtx): Patch = | ||
Seq( | ||
RemoveUnapply(mirror), | ||
RemoveCartesianBuilder(mirror) | ||
).reduce(Rewrite.merge).rewrite(ctx) | ||
} |
78 changes: 78 additions & 0 deletions
78
scalafix/rewrites/src/main/scala/fix/to1_0_0/RemoveCartesianBuilder.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package fix | ||
package to1_0_0 | ||
|
||
import scalafix._ | ||
import scalafix.syntax._ | ||
import scala.meta.{Symbol => _, _} | ||
|
||
// ref: https://github.com/typelevel/cats/pull/1745 | ||
case class RemoveCartesianBuilder(mirror: Mirror) | ||
extends SemanticRewrite(mirror) { | ||
|
||
private[this] val cartesianImport = | ||
"cats." | ||
|
||
private[this] val cartesianBuilders = | ||
(1 to 22) | ||
.map(arity => | ||
s"_root_.cats.syntax.CartesianBuilder#CartesianBuilder$arity.`|@|`.") | ||
.toSet + | ||
"_root_.cats.syntax.CartesianOps.`|@|`." | ||
|
||
private[this] val cartesianFixes: Map[String, String] = | ||
(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" | ||
) | ||
} | ||
.flatten | ||
.toMap | ||
|
||
private[this] def replace( | ||
ctx: RewriteCtx, | ||
t: Term.Name, | ||
fixes: Map[String, String]): Patch = { | ||
fixes.collect { | ||
case (target, fix) if t.symbolOpt.exists(_.normalized.syntax == target) => | ||
ctx.replaceTree(t, fix) | ||
}.asPatch | ||
} | ||
|
||
// Hackish to work around duplicate fixes due to recursion | ||
val alreadyFixedOps = collection.mutable.Set.empty[Term.Name] | ||
private[this] def replaceOpWithComma(ctx: RewriteCtx, op: Term.Name): Patch = | ||
if (op.symbolOpt.exists(s => | ||
cartesianBuilders.contains(s.normalized.syntax)) && !alreadyFixedOps | ||
.contains(op)) { | ||
alreadyFixedOps += op | ||
// remove the space before |@| | ||
ctx.removeToken(ctx.tokenList.prev(op.tokens.head)) + | ||
// replace |@| with , | ||
ctx.replaceTree(op, ",") | ||
} else { | ||
Patch.empty | ||
} | ||
|
||
private[this] def removeCartesianBuilderOp( | ||
ctx: RewriteCtx, | ||
applyInfix: Term.ApplyInfix): Patch = { | ||
applyInfix match { | ||
case Term.ApplyInfix(lhs: Term.ApplyInfix, op, _, _) => | ||
removeCartesianBuilderOp(ctx, lhs) + replaceOpWithComma(ctx, op) | ||
case Term.ApplyInfix(_, op, _, _) => | ||
replaceOpWithComma(ctx, op) | ||
} | ||
} | ||
|
||
def rewrite(ctx: RewriteCtx): Patch = { | ||
ctx.tree.collect { | ||
case t: Term.ApplyInfix => removeCartesianBuilderOp(ctx, t) | ||
case Term.Select(_, fun) => replace(ctx, fun, cartesianFixes) | ||
case t @ q"import cats.syntax.cartesian._" => | ||
ctx.replaceTree(t, "import cats.syntax.apply._") | ||
}.asPatch | ||
} | ||
} |
57 changes: 57 additions & 0 deletions
57
scalafix/rewrites/src/main/scala/fix/to1_0_0/RemoveUnapply.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
package fix | ||
package to1_0_0 | ||
|
||
import scalafix._ | ||
import scalafix.syntax._ | ||
import scala.meta.{Symbol => _, _} | ||
|
||
// ref: https://github.com/typelevel/cats/pull/1583 | ||
case class RemoveUnapply(mirror: Mirror) extends SemanticRewrite(mirror) { | ||
private[this] val fixes = Map( | ||
"_root_.cats.Traverse.Ops.traverseU." -> "traverse", | ||
"_root_.cats.Foldable.Ops.traverseU_." -> "traverse_", | ||
"_root_.cats.Foldable.traverseU_." -> "traverse_", | ||
"_root_.cats.Traverse.Ops.sequenceU." -> "sequence", | ||
"_root_.cats.Foldable.Ops.sequenceU_." -> "sequence_", | ||
"_root_.cats.Foldable.sequenceU_." -> "sequence_", | ||
"_root_.cats.data.Func.appFuncU." -> "appFunc", | ||
"_root_.cats.free.FreeT.liftTU." -> "liftT" | ||
) | ||
|
||
private[this] def replace( | ||
ctx: RewriteCtx, | ||
t: Term.Name, | ||
fixes: Map[String, String]): Patch = { | ||
fixes.collect { | ||
case (target, fix) if t.symbolOpt.exists(_.normalized.syntax == target) => | ||
ctx.replaceTree(t, fix) | ||
}.asPatch | ||
} | ||
|
||
private[this] def importeeName(importee: Importee): Option[Name] = | ||
importee match { | ||
case Importee.Name(name) => Some(name) | ||
case Importee.Rename(name, _) => Some(name) | ||
case _ => None | ||
} | ||
|
||
private[this] def removeImportee( | ||
ctx: RewriteCtx, | ||
importee: Importee, | ||
fixes: Map[String, String]): Patch = | ||
fixes.collect { | ||
case (target, _) | ||
if importeeName(importee) | ||
.flatMap(_.symbolOpt) | ||
.exists(_.normalized.syntax == target) => | ||
ctx.removeImportee(importee) | ||
}.asPatch | ||
|
||
def rewrite(ctx: RewriteCtx): Patch = { | ||
ctx.tree.collect { | ||
case Term.Select(_, fun) => replace(ctx, fun, fixes) | ||
case Term.Apply(fun: Term.Name, _) => replace(ctx, fun, fixes) | ||
case t: Importee.Name => removeImportee(ctx, t, fixes) | ||
}.asPatch | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package fix | ||
|
||
import scala.meta._ | ||
import scalafix.testkit._ | ||
|
||
class Cats_Tests | ||
extends SemanticRewriteSuite( | ||
Database.load(Classpath(AbsolutePath(BuildInfo.inputClassdirectory))), | ||
AbsolutePath(BuildInfo.inputSourceroot), | ||
Seq(AbsolutePath(BuildInfo.outputSourceroot)) | ||
) { | ||
runAllTests() | ||
} |