diff --git a/scalafix/input/src/main/scala/fix/Roughly212Src.scala b/scalafix/input/src/main/scala/fix/Roughly212Src.scala new file mode 100644 index 00000000..94df0c13 --- /dev/null +++ b/scalafix/input/src/main/scala/fix/Roughly212Src.scala @@ -0,0 +1,45 @@ +/* +rule = "scala:fix.Roughly" +Roughly = { + strictMapValues = true + strictFilterKeys = true +} +*/ +package fix + +import scala.{collection => c} +import scala.collection.{immutable => i, mutable => m} + +object Roughly212Src { + + def id[T](x: T): T = x + def f[T](x: T): Boolean = true + def f2[T](x: T): Int = 1 + val props = new scala.sys.SystemProperties() + val d = 1 -> 1 + val d2 = 1L -> 1 + val multi = new m.HashMap[Int, m.Set[Int]] with m.MultiMap[Int, Int] + + // i.SortedMap(d).mapValues(id): i.SortedMap[Int, Int] + // m.SortedMap(d).mapValues(id): c.SortedMap[Int, Int] + i.IntMap(d).mapValues(f2) : i.Map[Int, Int] + i.LongMap(d2).mapValues(f2) : i.Map[Long, Int] + i.Map(d).mapValues(id) : i.Map[Int, Int] + m.LongMap(d2).mapValues(f2) : c.Map[Long, Int] + m.Map(d).mapValues(id) : c.Map[Int, Int] + m.OpenHashMap(d).mapValues(id) : c.Map[Int, Int] + multi.mapValues(f2) : c.Map[Int, Int] + props.mapValues(id) : c.Map[String, String] + + // i.SortedMap(d).filterKeys(f): i.SortedMap[Int, Int] + // m.SortedMap(d).filterKeys(f): c.SortedMap[Int, Int] + i.IntMap(d).filterKeys(f) : i.Map[Int, Int] + i.LongMap(d2).filterKeys(f) : i.Map[Long, Int] + i.Map(d).filterKeys(f) : i.Map[Int, Int] + m.LongMap(d2).filterKeys(f) : c.Map[Long, Int] + m.Map(d).filterKeys(f) : c.Map[Int, Int] + m.Map(d).filterKeys(f) : c.Map[Int, Int] + multi.filterKeys(f) : c.Map[Int, m.Set[Int]] + props.filterKeys(f) : c.Map[String, String] + +} diff --git a/scalafix/input/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala b/scalafix/input/src/main/scala/fix/Roughly213Src.scala similarity index 64% rename from scalafix/input/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala rename to scalafix/input/src/main/scala/fix/Roughly213Src.scala index 25e0bbc0..b8f07dab 100644 --- a/scalafix/input/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala +++ b/scalafix/input/src/main/scala/fix/Roughly213Src.scala @@ -1,9 +1,13 @@ /* -rule = "scala:fix.RoughlyStreamToLazyList" +rule = "scala:fix.Roughly" +Roughly = { + withLazyAppendedAll = true + withLazyList = true +} */ package fix -class RoughlyStreamToLazyListSrc() { +class Roughly213Src() { val s = Stream(1, 2, 3) s.append(List(4, 5, 6)) 1 #:: 2 #:: 3 #:: Stream.Empty diff --git a/scalafix/input/src/main/scala/fix/RoughlyMapValuesSrc.scala b/scalafix/input/src/main/scala/fix/RoughlyMapValuesSrc.scala deleted file mode 100644 index 5a545ab8..00000000 --- a/scalafix/input/src/main/scala/fix/RoughlyMapValuesSrc.scala +++ /dev/null @@ -1,8 +0,0 @@ -/* -rule = "scala:fix.RoughlyMapValues" - */ -package fix - -class RoughlyMapValuesSrc(map: Map[Int, Int]) { - map.mapValues(_ + 1) -} diff --git a/scalafix/output212/src/main/scala/fix/Roughly212Src.scala b/scalafix/output212/src/main/scala/fix/Roughly212Src.scala new file mode 100644 index 00000000..249c52b4 --- /dev/null +++ b/scalafix/output212/src/main/scala/fix/Roughly212Src.scala @@ -0,0 +1,41 @@ + + + +package fix + +import scala.{collection => c} +import scala.collection.{immutable => i, mutable => m} + +object Roughly212Src { + + def id[T](x: T): T = x + def f[T](x: T): Boolean = true + def f2[T](x: T): Int = 1 + val props = new scala.sys.SystemProperties() + val d = 1 -> 1 + val d2 = 1L -> 1 + val multi = new m.HashMap[Int, m.Set[Int]] with m.MultiMap[Int, Int] + + // i.SortedMap(d).mapValues(id): i.SortedMap[Int, Int] + // m.SortedMap(d).mapValues(id): c.SortedMap[Int, Int] + i.IntMap(d).mapValues(f2).toMap : i.Map[Int, Int] + i.LongMap(d2).mapValues(f2).toMap : i.Map[Long, Int] + i.Map(d).mapValues(id).toMap : i.Map[Int, Int] + m.LongMap(d2).mapValues(f2).toMap : c.Map[Long, Int] + m.Map(d).mapValues(id).toMap : c.Map[Int, Int] + m.OpenHashMap(d).mapValues(id).toMap : c.Map[Int, Int] + multi.mapValues(f2).toMap : c.Map[Int, Int] + props.mapValues(id).toMap : c.Map[String, String] + + // i.SortedMap(d).filterKeys(f): i.SortedMap[Int, Int] + // m.SortedMap(d).filterKeys(f): c.SortedMap[Int, Int] + i.IntMap(d).filterKeys(f).toMap : i.Map[Int, Int] + i.LongMap(d2).filterKeys(f).toMap : i.Map[Long, Int] + i.Map(d).filterKeys(f).toMap : i.Map[Int, Int] + m.LongMap(d2).filterKeys(f).toMap : c.Map[Long, Int] + m.Map(d).filterKeys(f).toMap : c.Map[Int, Int] + m.Map(d).filterKeys(f).toMap : c.Map[Int, Int] + multi.filterKeys(f).toMap : c.Map[Int, m.Set[Int]] + props.filterKeys(f).toMap : c.Map[String, String] + +} diff --git a/scalafix/output212/src/main/scala/fix/RoughlyMapValuesSrc.scala b/scalafix/output212/src/main/scala/fix/RoughlyMapValuesSrc.scala deleted file mode 100644 index a7b1f620..00000000 --- a/scalafix/output212/src/main/scala/fix/RoughlyMapValuesSrc.scala +++ /dev/null @@ -1,8 +0,0 @@ - - - -package fix - -class RoughlyMapValuesSrc(map: Map[Int, Int]) { - map.mapValues(_ + 1).toMap -} diff --git a/scalafix/output213/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala b/scalafix/output213/src/main/scala/fix/Roughly213Src.scala similarity index 85% rename from scalafix/output213/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala rename to scalafix/output213/src/main/scala/fix/Roughly213Src.scala index 87ad201e..43e8ab56 100644 --- a/scalafix/output213/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala +++ b/scalafix/output213/src/main/scala/fix/Roughly213Src.scala @@ -3,7 +3,7 @@ package fix -class RoughlyStreamToLazyListSrc() { +class Roughly213Src() { val s = LazyList(1, 2, 3) s.lazyAppendedAll(List(4, 5, 6)) 1 #:: 2 #:: 3 #:: LazyList.Empty diff --git a/scalafix/rules/src/main/scala/fix/Roughly.scala b/scalafix/rules/src/main/scala/fix/Roughly.scala new file mode 100644 index 00000000..3cb245ed --- /dev/null +++ b/scalafix/rules/src/main/scala/fix/Roughly.scala @@ -0,0 +1,95 @@ +package fix + +import scalafix._ +import scalafix.util._ +import scala.meta._ + +import metaconfig.{ConfDecoder, Conf, Configured} +import metaconfig.annotation.Description +import metaconfig.annotation.ExampleValue +import metaconfig.generic +import metaconfig.generic.Surface + +/* 2.12 Cross-Compatible + * + * This rules is *roughly* correct, they compile but might have a different runtime semantic + * + * Map.{mapValues, filterKeys}, Seq.reverseMap were lazy but with a strict interface + * (ex returning Map where they should return MapView). + * + * LazyList has a lazy head, were Stream has a strict head + * + */ +final case class Roughly(index: SemanticdbIndex, config: RoughlyConfig) extends SemanticRule(index, "Roughly") { + def this(index: SemanticdbIndex) = this(index, RoughlyConfig.default) + + val mapValues = + SymbolMatcher.exact( + Symbol("_root_.scala.collection.immutable.MapLike#mapValues(Lscala/Function1;)Lscala/collection/immutable/Map;."), + Symbol("_root_.scala.collection.MapLike#filterKeys(Lscala/Function1;)Lscala/collection/Map;.") + ) + + val filterKeys = + SymbolMatcher.exact( + Symbol("_root_.scala.collection.immutable.MapLike#filterKeys(Lscala/Function1;)Lscala/collection/immutable/Map;."), + Symbol("_root_.scala.collection.MapLike#mapValues(Lscala/Function1;)Lscala/collection/Map;.") + ) + + // Not supported: SortedMap + // Symbol("_root_.scala.collection.immutable.SortedMap#mapValues(Lscala/Function1;)Lscala/collection/immutable/SortedMap;."), + // Symbol("_root_.scala.collection.SortedMapLike#mapValues(Lscala/Function1;)Lscala/collection/SortedMap;.") + // Symbol("_root_.scala.collection.immutable.SortedMap#filterKeys(Lscala/Function1;)Lscala/collection/immutable/SortedMap;.") + // Symbol("_root_.scala.collection.SortedMapLike#filterKeys(Lscala/Function1;)Lscala/collection/SortedMap;.") + + val streamAppend = SymbolMatcher.normalized( + Symbol("_root_.scala.collection.immutable.Stream.append.") + ) + + def replaceSymbols(ctx: RuleCtx): Patch = { + if (config.withLazyList) { + ctx.replaceSymbols( + "scala.Stream" -> "scala.LazyList", + "scala.collection.immutable.Stream" -> "scala.collection.immutable.LazyList" + ) + } else Patch.empty + } + + override def description: String = "" + + override def init(config: Conf): Configured[Rule] = + config + .getOrElse("roughly", "Roughly")(RoughlyConfig.default) + .map(Roughly(index, _)) + + override def fix(ctx: RuleCtx): Patch = { + import config._ + + val collectFixes = + ctx.tree.collect { + case ap @ Term.Apply(Term.Select(_, mapValues(_)), List(_)) if strictMapValues => + ctx.addRight(ap, ".toMap") + + case ap @ Term.Apply(Term.Select(_, filterKeys(_)), List(_)) if strictFilterKeys => + ctx.addRight(ap, ".toMap") + + case streamAppend(t: Name) if withLazyAppendedAll => + ctx.replaceTree(t, "lazyAppendedAll") + + }.asPatch + + collectFixes + replaceSymbols(ctx) + } +} + +case class RoughlyConfig( + strictMapValues: Boolean = false, + strictFilterKeys: Boolean = false, + withLazyAppendedAll: Boolean = false, + withLazyList: Boolean = false +) + +object RoughlyConfig { + val default: RoughlyConfig = RoughlyConfig() + implicit val surface: Surface[RoughlyConfig] = generic.deriveSurface[RoughlyConfig] + implicit val decoder: ConfDecoder[RoughlyConfig] = generic.deriveDecoder[RoughlyConfig](default) +} diff --git a/scalafix/rules/src/main/scala/fix/RoughlyMapValues.scala b/scalafix/rules/src/main/scala/fix/RoughlyMapValues.scala deleted file mode 100644 index 07838667..00000000 --- a/scalafix/rules/src/main/scala/fix/RoughlyMapValues.scala +++ /dev/null @@ -1,24 +0,0 @@ -package fix - -import scalafix._ -import scalafix.util._ -import scala.meta._ - - -/* 2.12 Cross-Compatible - * - * This rules is marked unstable since Map.mapValues was lazy - */ -case class RoughlyMapValues(index: SemanticdbIndex) extends SemanticRule(index, "RoughlyMapValues") { - val mapMapValues = - SymbolMatcher.exact( - Symbol("_root_.scala.collection.immutable.MapLike#mapValues(Lscala/Function1;)Lscala/collection/immutable/Map;.") - ) - - override def fix(ctx: RuleCtx): Patch = { - ctx.tree.collect { - case ap @ Term.Apply(Term.Select(_, mapMapValues(_)), List(_)) => - ctx.addRight(ap, ".toMap") - }.asPatch - } -} diff --git a/scalafix/rules/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala b/scalafix/rules/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala deleted file mode 100644 index 90a04b11..00000000 --- a/scalafix/rules/src/main/scala/fix/RoughlyStreamToLazyListSrc.scala +++ /dev/null @@ -1,35 +0,0 @@ -package fix - -import scalafix._ -import scalafix.util._ -import scala.meta._ - -/* Not 2.12 Cross-Compatible - * - * This rules is marked unstable since Stream is not strictly equivalent to LazyList. - * LazyList has a lazy head but not Stream - */ -case class RoughlyStreamToLazyList(index: SemanticdbIndex) extends SemanticRule(index, "RoughlyStreamToLazyList") { - - val streamAppend = SymbolMatcher.normalized( - Symbol("_root_.scala.collection.immutable.Stream.append.") - ) - - def replaceStreamAppend(ctx: RuleCtx): Patch = { - ctx.tree.collect { - case streamAppend(t: Name) => - ctx.replaceTree(t, "lazyAppendedAll") - }.asPatch - } - - def replaceSymbols(ctx: RuleCtx): Patch = { - ctx.replaceSymbols( - "scala.Stream" -> "scala.LazyList", - "scala.collection.immutable.Stream" -> "scala.collection.immutable.LazyList" - ) - } - - override def fix(ctx: RuleCtx): Patch = { - replaceStreamAppend(ctx) + replaceSymbols(ctx) - } -} diff --git a/scalafix/tests/src/test/scala/fix/ScalafixTests.scala b/scalafix/tests/src/test/scala/fix/ScalafixTests.scala index 0cd50cc4..d2f148b1 100644 --- a/scalafix/tests/src/test/scala/fix/ScalafixTests.scala +++ b/scalafix/tests/src/test/scala/fix/ScalafixTests.scala @@ -17,5 +17,5 @@ class ScalafixTests runAllTests() // to run only one test: - // testsToRun.filter(_.filename.toNIO.getFileName.toString == "IterableSrc.scala" ).foreach(runOn) + // testsToRun.filter(_.filename.toNIO.getFileName.toString == "Playground.scala" ).foreach(runOn) }