|
| 1 | +package fix |
| 2 | + |
| 3 | +import scalafix._ |
| 4 | +import scalafix.util._ |
| 5 | +import scala.meta._ |
| 6 | + |
| 7 | +import metaconfig.{ConfDecoder, Conf, Configured} |
| 8 | +import metaconfig.annotation.Description |
| 9 | +import metaconfig.annotation.ExampleValue |
| 10 | +import metaconfig.generic |
| 11 | +import metaconfig.generic.Surface |
| 12 | + |
| 13 | +/* 2.12 Cross-Compatible |
| 14 | + * |
| 15 | + * This rules is *roughly* correct, they compile but might have a different runtime semantic |
| 16 | + * |
| 17 | + * Map.{mapValues, filterKeys}, Seq.reverseMap were lazy but with a strict interface |
| 18 | + * (ex returning Map where they should return MapView). |
| 19 | + * |
| 20 | + * LazyList has a lazy head, were Stream has a strict head |
| 21 | + * |
| 22 | + */ |
| 23 | +final case class Roughly(index: SemanticdbIndex, config: RoughlyConfig) extends SemanticRule(index, "Roughly") { |
| 24 | + def this(index: SemanticdbIndex) = this(index, RoughlyConfig.default) |
| 25 | + |
| 26 | + val mapValues = |
| 27 | + SymbolMatcher.exact( |
| 28 | + Symbol("_root_.scala.collection.immutable.MapLike#mapValues(Lscala/Function1;)Lscala/collection/immutable/Map;."), |
| 29 | + Symbol("_root_.scala.collection.MapLike#filterKeys(Lscala/Function1;)Lscala/collection/Map;.") |
| 30 | + ) |
| 31 | + |
| 32 | + val filterKeys = |
| 33 | + SymbolMatcher.exact( |
| 34 | + Symbol("_root_.scala.collection.immutable.MapLike#filterKeys(Lscala/Function1;)Lscala/collection/immutable/Map;."), |
| 35 | + Symbol("_root_.scala.collection.MapLike#mapValues(Lscala/Function1;)Lscala/collection/Map;.") |
| 36 | + ) |
| 37 | + |
| 38 | + // Not supported: SortedMap |
| 39 | + // Symbol("_root_.scala.collection.immutable.SortedMap#mapValues(Lscala/Function1;)Lscala/collection/immutable/SortedMap;."), |
| 40 | + // Symbol("_root_.scala.collection.SortedMapLike#mapValues(Lscala/Function1;)Lscala/collection/SortedMap;.") |
| 41 | + // Symbol("_root_.scala.collection.immutable.SortedMap#filterKeys(Lscala/Function1;)Lscala/collection/immutable/SortedMap;.") |
| 42 | + // Symbol("_root_.scala.collection.SortedMapLike#filterKeys(Lscala/Function1;)Lscala/collection/SortedMap;.") |
| 43 | + |
| 44 | + val streamAppend = SymbolMatcher.normalized( |
| 45 | + Symbol("_root_.scala.collection.immutable.Stream.append.") |
| 46 | + ) |
| 47 | + |
| 48 | + def replaceSymbols(ctx: RuleCtx): Patch = { |
| 49 | + if (config.withLazyList) { |
| 50 | + ctx.replaceSymbols( |
| 51 | + "scala.Stream" -> "scala.LazyList", |
| 52 | + "scala.collection.immutable.Stream" -> "scala.collection.immutable.LazyList" |
| 53 | + ) |
| 54 | + } else Patch.empty |
| 55 | + } |
| 56 | + |
| 57 | + override def description: String = "" |
| 58 | + |
| 59 | + override def init(config: Conf): Configured[Rule] = |
| 60 | + config |
| 61 | + .getOrElse("roughly", "Roughly")(RoughlyConfig.default) |
| 62 | + .map(Roughly(index, _)) |
| 63 | + |
| 64 | + override def fix(ctx: RuleCtx): Patch = { |
| 65 | + import config._ |
| 66 | + |
| 67 | + val collectFixes = |
| 68 | + ctx.tree.collect { |
| 69 | + case ap @ Term.Apply(Term.Select(_, mapValues(_)), List(_)) if strictMapValues => |
| 70 | + ctx.addRight(ap, ".toMap") |
| 71 | + |
| 72 | + case ap @ Term.Apply(Term.Select(_, filterKeys(_)), List(_)) if strictFilterKeys => |
| 73 | + ctx.addRight(ap, ".toMap") |
| 74 | + |
| 75 | + case streamAppend(t: Name) if withLazyAppendedAll => |
| 76 | + ctx.replaceTree(t, "lazyAppendedAll") |
| 77 | + |
| 78 | + }.asPatch |
| 79 | + |
| 80 | + collectFixes + replaceSymbols(ctx) |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +case class RoughlyConfig( |
| 85 | + strictMapValues: Boolean = false, |
| 86 | + strictFilterKeys: Boolean = false, |
| 87 | + withLazyAppendedAll: Boolean = false, |
| 88 | + withLazyList: Boolean = false |
| 89 | +) |
| 90 | + |
| 91 | +object RoughlyConfig { |
| 92 | + val default: RoughlyConfig = RoughlyConfig() |
| 93 | + implicit val surface: Surface[RoughlyConfig] = generic.deriveSurface[RoughlyConfig] |
| 94 | + implicit val decoder: ConfDecoder[RoughlyConfig] = generic.deriveDecoder[RoughlyConfig](default) |
| 95 | +} |
0 commit comments