diff --git a/scalafix/build.sbt b/scalafix/build.sbt index 5cf2d509..61b464a3 100644 --- a/scalafix/build.sbt +++ b/scalafix/build.sbt @@ -1,23 +1,31 @@ def scalafixVersion = _root_.scalafix.Versions.version -inScope(Global)( - List( - scalaVersion := _root_.scalafix.Versions.scala212 - ) + +lazy val baseSettings = Seq( + scalaVersion := _root_.scalafix.Versions.scala212 ) lazy val root = project .in(file(".")) - .aggregate( - rules, input, output, tests - ) + .settings(baseSettings) + .aggregate(rules, input, output, tests) -lazy val rules = project.settings( - libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % scalafixVersion -) +lazy val rules = project + .settings(baseSettings) + .settings( + libraryDependencies += "ch.epfl.scala" %% "scalafix-core" % scalafixVersion + ) lazy val input = project +.settings(baseSettings) .settings( - scalafixSourceroot := sourceDirectory.in(Compile).value + addCompilerPlugin(scalafixSemanticdb), + scalacOptions ++= { + val sourceroot = sourceDirectory.in(Compile).value / "scala" + Seq( + "-Yrangepos", + s"-P:semanticdb:sourceroot:$sourceroot" + ) + } ) lazy val output = project @@ -27,18 +35,15 @@ lazy val output = project ) lazy val tests = project + .settings(baseSettings) .settings( libraryDependencies += "ch.epfl.scala" % "scalafix-testkit" % scalafixVersion % 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 - ), - test in Test := (test in Test).dependsOn(compile in (output, Compile)).value + scalafixTestkitOutputSourceDirectories := + sourceDirectories.in(output, Compile).value, + scalafixTestkitInputSourceDirectories := + sourceDirectories.in(input, Compile).value, + scalafixTestkitInputClasspath := + fullClasspath.in(input, Compile).value ) .dependsOn(input, rules) - .enablePlugins(BuildInfoPlugin) + .enablePlugins(ScalafixTestkitPlugin) diff --git a/scalafix/input/src/main/scala/fix/FunctionTupledSrc.scala b/scalafix/input/src/main/scala/fix/FunctionTupledSrc.scala new file mode 100644 index 00000000..3ce9b5d5 --- /dev/null +++ b/scalafix/input/src/main/scala/fix/FunctionTupledSrc.scala @@ -0,0 +1,27 @@ +/* +rule = "scala:fix.Scalacollectioncompat_newcollections" + */ +package fix + +import Function.tupled + +object FunctionTupledSrc { + def m2(i: Int, j: Int): Int = i + j + def m3(i: Int, j: Int): Int = i + j + def m4(i: Int, j: Int): Int = i + j + def m5(i: Int, j: Int): Int = i + j + + val f2 = m2 _ + val f3 = m3 _ + val f4 = m4 _ + val f5 = m5 _ + + Function.tupled(m2 _) + Function.tupled(f2) + tupled(f2) + + Function.tupled(f2) + Function.tupled(f3) + Function.tupled(f4) + Function.tupled(f5) +} \ No newline at end of file diff --git a/scalafix/input/src/main/scala/fix/ShiftingSrc.scala b/scalafix/input/src/main/scala/fix/ShiftingSrc.scala new file mode 100644 index 00000000..dee7a6e7 --- /dev/null +++ b/scalafix/input/src/main/scala/fix/ShiftingSrc.scala @@ -0,0 +1,32 @@ +/* +rule = "scala:fix.Scalacollectioncompat_newcollections" + */ +package fix + +object ShiftingSrc { + val b = 1.toByte + val c = 'c' + val i = 1 + val s = 1.toShort + val l = 1L + + b << l + b >>> l + b >> l + + c << l + c >>> l + c >> l + + i << l + i >>> l + i >> l + + s << l + s >>> l + s >> l + + l << l + l >>> l + l >> l +} diff --git a/scalafix/output/src/main/scala/fix/FunctionTupledSrc.scala b/scalafix/output/src/main/scala/fix/FunctionTupledSrc.scala new file mode 100644 index 00000000..d0614b3d --- /dev/null +++ b/scalafix/output/src/main/scala/fix/FunctionTupledSrc.scala @@ -0,0 +1,27 @@ + + + +package fix + +import Function.tupled + +object FunctionTupledSrc { + def m2(i: Int, j: Int): Int = i + j + def m3(i: Int, j: Int): Int = i + j + def m4(i: Int, j: Int): Int = i + j + def m5(i: Int, j: Int): Int = i + j + + val f2 = m2 _ + val f3 = m3 _ + val f4 = m4 _ + val f5 = m5 _ + + (m2 _).tupled + f2.tupled + f2.tupled + + f2.tupled + f3.tupled + f4.tupled + f5.tupled +} \ No newline at end of file diff --git a/scalafix/output/src/main/scala/fix/ShiftingSrc.scala b/scalafix/output/src/main/scala/fix/ShiftingSrc.scala new file mode 100644 index 00000000..62bce2c3 --- /dev/null +++ b/scalafix/output/src/main/scala/fix/ShiftingSrc.scala @@ -0,0 +1,32 @@ + + + +package fix + +object ShiftingSrc { + val b = 1.toByte + val c = 'c' + val i = 1 + val s = 1.toShort + val l = 1L + + b.toLong << l + b.toLong >>> l + b.toLong >> l + + c.toLong << l + c.toLong >>> l + c.toLong >> l + + i.toLong << l + i.toLong >>> l + i.toLong >> l + + s.toLong << l + s.toLong >>> l + s.toLong >> l + + l << l + l >>> l + l >> l +} diff --git a/scalafix/project/plugins.sbt b/scalafix/project/plugins.sbt index 0fd7a907..5782adbf 100644 --- a/scalafix/project/plugins.sbt +++ b/scalafix/project/plugins.sbt @@ -1,3 +1,3 @@ -addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.5.10") +addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.6.0-M8") addSbtPlugin("io.get-coursier" % "sbt-coursier" % "1.0.0") addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.6.1") \ No newline at end of file diff --git a/scalafix/rules/src/main/scala/fix/Scalacollectioncompat_newcollections.scala b/scalafix/rules/src/main/scala/fix/Scalacollectioncompat_newcollections.scala index a310662f..499b18d0 100644 --- a/scalafix/rules/src/main/scala/fix/Scalacollectioncompat_newcollections.scala +++ b/scalafix/rules/src/main/scala/fix/Scalacollectioncompat_newcollections.scala @@ -8,6 +8,20 @@ import scala.meta._ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex) extends SemanticRule(index, "Scalacollectioncompat_newcollections") { + val tupledSymbols = (2 to 5).map(i => Symbol(s"scala.Function.tupled(Function$i).")) + val tupledMatcher = SymbolMatcher.exact(tupledSymbols: _*) + val scalaFunction = SymbolMatcher.exact(Symbol("scala.Function.")) + + val naturalNumberTypes = List("Byte", "Char", "Int", "Short") + val shiffingOperators = List("<<", ">>>", ">>") + val naturalNumberShiftingSymbols = + for { + tpe <- naturalNumberTypes + op <- shiffingOperators + } yield Symbol(s"scala.$tpe#`$op`(Long).") + + val naturalShiffting = SymbolMatcher.exact(naturalNumberShiftingSymbols: _*) + def replaceSymbols(ctx: RuleCtx): Patch = { ctx.replaceSymbols( "scala.Stream" -> "scala.LazyList", @@ -49,13 +63,13 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex) Symbol("_root_.scala.collection.mutable.SetLike.retain.") ) - def replaceMutableSet(ctx: RuleCtx) = + def replaceMutableSet(ctx: RuleCtx): Patch = ctx.tree.collect { case retainSet(n: Name) => ctx.replaceTree(n, "filterInPlace") }.asPatch - def replaceMutableMap(ctx: RuleCtx) = + def replaceMutableMap(ctx: RuleCtx): Patch = ctx.tree.collect { case Term.Apply(Term.Select(_, retainMap(n: Name)), List(_: Term.PartialFunction)) => ctx.replaceTree(n, "filterInPlace") @@ -72,7 +86,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex) ).asPatch }.asPatch - def replaceSymbolicFold(ctx: RuleCtx) = + def replaceSymbolicFold(ctx: RuleCtx): Patch = ctx.tree.collect { case Term.Apply(ap @ Term.ApplyInfix(rhs, foldRightSymbol(_), _, List(lhs)), _) => ctx.replaceTree(ap, s"$rhs.foldRight($lhs)") @@ -81,7 +95,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex) ctx.replaceTree(ap, s"$rhs.foldLeft($lhs)") }.asPatch - def replaceToList(ctx: RuleCtx) = + def replaceToList(ctx: RuleCtx): Patch = ctx.tree.collect { case iterator(t: Name) => ctx.replaceTree(t, "iterator") @@ -96,7 +110,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex) ).asPatch }.asPatch - def replaceTupleZipped(ctx: RuleCtx) = + def replaceTupleZipped(ctx: RuleCtx): Patch = ctx.tree.collect { case tupleZipped(Term.Select(Term.Tuple(args), name)) => val removeTokensPatch = @@ -156,9 +170,61 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex) ctx.replaceTree(t, "lazyAppendedAll") }.asPatch - override def fix(ctx: RuleCtx): Patch = { - // println(ctx.index.database) + def replaceNaturalShiffting(ctx: RuleCtx): Patch = + ctx.tree.collect { + case Term.ApplyInfix(lhs, naturalShiffting(_), Nil, List(_)) => + ctx.addRight(lhs, ".toLong") + }.asPatch + + def replaceFunctionTupled(ctx: RuleCtx): Patch = { + def toTupled(n: Term, f: Term): Patch = { + val needsParens = + f match { + case _: Term.Eta => true + case _ => false + } + + (for { + name <- n.tokens.lastOption + open <- ctx.tokenList.find(name)(_.is[Token.LeftParen]) + close <- ctx.matchingParens.close(open.asInstanceOf[Token.LeftParen]) + } yield { + + val parens = + if (needsParens) { + ctx.addLeft(f, "(") + + ctx.addRight(f, ")") + } + else Patch.empty + + ctx.removeToken(open) + + ctx.removeToken(close) + + ctx.removeToken(name) + + parens + + ctx.addRight(f, ".tupled") + }).asPatch + } + + ctx.tree.collect { + case Term.Apply(Term.Select(f @ scalaFunction(_), n @ tupledMatcher(_)), List(f1)) => + (for { + fun <- f.tokens.lastOption + dot <- ctx.tokenList.find(fun)(_.is[Token.Dot]) + } yield + ctx.removeTokens(f.tokens) + + ctx.removeToken(dot) + + toTupled(n, f1) + ).asPatch + + case Term.Apply(n @ tupledMatcher(_), List(f)) => + toTupled(n, f) + + }.asPatch + } + + + override def fix(ctx: RuleCtx): Patch = { replaceToList(ctx) + replaceSymbols(ctx) + replaceTupleZipped(ctx) + @@ -166,6 +232,8 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex) replaceStreamAppend(ctx) + replaceMutableMap(ctx) + replaceMutableSet(ctx) + - replaceSymbolicFold(ctx) + replaceSymbolicFold(ctx) + + replaceNaturalShiffting(ctx) + + replaceFunctionTupled(ctx) } } diff --git a/scalafix/tests/src/test/scala/fix/Collectionstrawman_Tests.scala b/scalafix/tests/src/test/scala/fix/Collectionstrawman_Tests.scala index ef75d77e..ffe92f45 100644 --- a/scalafix/tests/src/test/scala/fix/Collectionstrawman_Tests.scala +++ b/scalafix/tests/src/test/scala/fix/Collectionstrawman_Tests.scala @@ -1,14 +1,10 @@ package fix import scala.meta._ -import scalafix.testkit._ import scalafix._ +import scalafix.v0._ -class Collectionstrawman_Tests - extends SemanticRuleSuite( - SemanticdbIndex.load(Classpath(AbsolutePath(BuildInfo.inputClassdirectory))), - AbsolutePath(BuildInfo.inputSourceroot), - Seq(AbsolutePath(BuildInfo.outputSourceroot)) - ) { +class Scalafixplayground_Tests + extends scalafix.testkit.SemanticRuleSuite { runAllTests() }