Skip to content

Commit 29aca34

Browse files
Rewrite collection.{Map, Set}.+ and collection.Set.-
This is incomplete since, it's blocked by scalameta/scalameta#1212 (Add support to query type of a term) iset: immutable.Set[Int] cset: collecion.Set[Int] both have +/- implemented via SetLike given iset + 1 cset + 1 we know that + is from SetLike, but it's not possible to get the type of iset/cset Rewrite collection.Set.+ restrict type of lhs scalafix allow us to match on SetLike.+ but we cannot check the type of the expression on the lhs We can work arround this limitation if the lhs is a identifier (simple case). This allow us to document the expected type on the lhs and prepare the implementation once scalameta/scalameta#1212 is resolved
1 parent 9804a16 commit 29aca34

File tree

3 files changed

+92
-16
lines changed

3 files changed

+92
-16
lines changed

scalafix/input/src/main/scala/fix/SetMapSrc.scala

+22-7
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,27 @@ rule = "scala:fix.Scalacollectioncompat_newcollections"
33
*/
44
package fix
55

6-
class SetMapSrc(set: Set[Int], map: Map[Int, Int]) {
7-
set + (2, 3)
8-
map + (2 -> 3, 3 -> 4)
9-
(set + (2, 3)).map(x => x)
10-
set + (2, 3) - 4
11-
map.mapValues(_ + 1)
12-
map.zip(List())
6+
import scala.collection
7+
import scala.collection.immutable
8+
import scala.collection.mutable.{Map, Set} // Challenge to make sure the scoping is correct
9+
10+
class SetMapSrc(iset: immutable.Set[Int],
11+
cset: collection.Set[Int],
12+
imap: immutable.Map[Int, Int],
13+
cmap: collection.Map[Int, Int]) {
14+
iset + (2, 3)
15+
imap + (2 -> 3, 3 -> 4)
16+
(iset + (2, 3)).toString
17+
iset + (2, 3) - 4
18+
imap.mapValues(_ + 1)
19+
iset + 1
20+
iset - 2
21+
cset + 1
22+
cset - 2
23+
cmap + (2 -> 3)
24+
cmap + ((4, 5))
25+
imap + (2 -> 3)
26+
imap + ((4, 5))
27+
imap.zip(List())
1328
List().zip(List())
1429
}

scalafix/output/src/main/scala/fix/SetMapSrc.scala

+22-7
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,27 @@
33

44
package fix
55

6-
class SetMapSrc(set: Set[Int], map: Map[Int, Int]) {
7-
set + 2 + 3
8-
map + (2 -> 3) + (3 -> 4)
9-
(set + 2 + 3).map(x => x)
10-
set + 2 + 3 - 4
11-
map.mapValues(_ + 1).toMap
12-
map.zip(List()).toMap
6+
import scala.collection
7+
import scala.collection.immutable
8+
import scala.collection.mutable.{Map, Set} // Challenge to make sure the scoping is correct
9+
10+
class SetMapSrc(iset: immutable.Set[Int],
11+
cset: collection.Set[Int],
12+
imap: immutable.Map[Int, Int],
13+
cmap: collection.Map[Int, Int]) {
14+
iset + 2 + 3
15+
imap + (2 -> 3) + (3 -> 4)
16+
(iset + 2 + 3).toString
17+
iset + 2 + 3 - 4
18+
imap.mapValues(_ + 1).toMap
19+
iset + 1
20+
iset - 2
21+
cset ++ _root_.scala.collection.Set(1)
22+
cset -- _root_.scala.collection.Set(2)
23+
cmap ++ _root_.scala.collection.Map(2 -> 3)
24+
cmap ++ _root_.scala.collection.Map((4, 5))
25+
imap + (2 -> 3)
26+
imap + ((4, 5))
27+
imap.zip(List()).toMap
1328
List().zip(List())
1429
}

scalafix/rules/src/main/scala/fix/Scalacollectioncompat_newcollections.scala

+48-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
3030
Symbol("_root_.scala.Predef.Map#")
3131
)
3232

33+
val CollectionSet: TypeMatcher = TypeMatcher(Symbol("_root_.scala.collection.Set#"))
34+
3335
def replaceSymbols(ctx: RuleCtx): Patch = {
3436
ctx.replaceSymbols(
3537
"scala.collection.LinearSeq" -> "scala.collection.immutable.List",
@@ -52,6 +54,18 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
5254
Symbol("_root_.scala.runtime.Tuple2Zipped.Ops.zipped."),
5355
Symbol("_root_.scala.runtime.Tuple3Zipped.Ops.zipped.")
5456
)
57+
val setPlus =
58+
SymbolMatcher.exact(
59+
Symbol("_root_.scala.collection.SetLike#`+`(Ljava/lang/Object;)Lscala/collection/Set;.")
60+
)
61+
val setMinus =
62+
SymbolMatcher.exact(
63+
Symbol("_root_.scala.collection.SetLike#`-`(Ljava/lang/Object;)Lscala/collection/Set;.")
64+
)
65+
val mapPlus =
66+
SymbolMatcher.exact(
67+
Symbol("_root_.scala.collection.MapLike#`+`(Lscala/Tuple2;)Lscala/collection/Map;.")
68+
)
5569
val setPlus2 = SymbolMatcher.exact(
5670
Symbol("_root_.scala.collection.SetLike#`+`(Ljava/lang/Object;Ljava/lang/Object;Lscala/collection/Seq;)Lscala/collection/Set;.")
5771
)
@@ -110,6 +124,9 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
110124
Symbol("_root_.scala.collection.IterableLike#zip(Lscala/collection/GenIterable;Lscala/collection/generic/CanBuildFrom;)Ljava/lang/Object;.")
111125
)
112126

127+
def startsWithParens(tree: Tree): Boolean =
128+
tree.tokens.headOption.map(_.is[Token.LeftParen]).getOrElse(false)
129+
113130
def replaceMutableSet(ctx: RuleCtx) =
114131
ctx.tree.collect {
115132
case retainSet(n: Name) =>
@@ -220,7 +237,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
220237
def replaceSetMapPlus2(ctx: RuleCtx): Patch = {
221238
def rewritePlus(ap: Term.ApplyInfix, lhs: Term, op: Term.Name, rhs1: Term, rhs2: Term): Patch = {
222239
val tokensToReplace =
223-
if(ap.tokens.headOption.map(_.is[Token.LeftParen]).getOrElse(false)) {
240+
if(startsWithParens(ap)) {
224241
// don't drop surrounding parens
225242
ap.tokens.slice(1, ap.tokens.size - 1)
226243
} else ap.tokens
@@ -245,6 +262,34 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
245262
}.asPatch
246263
}
247264

265+
def replaceSetMapPlusMinus(ctx: RuleCtx): Patch = {
266+
def rewriteOp(op: Tree, rhs: Tree, doubleOp: String, col0: String): Patch = {
267+
val col = "_root_.scala.collection." + col0
268+
val callSite =
269+
if (startsWithParens(rhs)) {
270+
ctx.addLeft(rhs, col)
271+
}
272+
else {
273+
ctx.addLeft(rhs, col + "(") +
274+
ctx.addRight(rhs, ")")
275+
}
276+
277+
ctx.addRight(op, doubleOp) + callSite
278+
}
279+
280+
ctx.tree.collect {
281+
case Term.ApplyInfix(CollectionSet(), op @ setPlus(_), Nil, List(rhs)) =>
282+
rewriteOp(op, rhs, "+", "Set")
283+
284+
case Term.ApplyInfix(CollectionSet(), op @ setMinus(_), Nil, List(rhs)) =>
285+
rewriteOp(op, rhs, "-", "Set")
286+
287+
case Term.ApplyInfix(_, op @ mapPlus(_), Nil, List(rhs)) =>
288+
rewriteOp(op, rhs, "+", "Map")
289+
}.asPatch
290+
}
291+
292+
248293
def replaceMutSetMapPlus(ctx: RuleCtx): Patch = {
249294
def rewriteMutPlus(lhs: Term, op: Term.Name): Patch = {
250295
ctx.addRight(lhs, ".clone()") +
@@ -299,7 +344,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
299344
ctx.addRight(ap, ".toMap")
300345
}.asPatch
301346
}
302-
347+
303348
override def fix(ctx: RuleCtx): Patch = {
304349
replaceToList(ctx) +
305350
replaceSymbols(ctx) +
@@ -310,6 +355,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
310355
replaceMutableSet(ctx) +
311356
replaceSymbolicFold(ctx) +
312357
replaceSetMapPlus2(ctx) +
358+
replaceSetMapPlusMinus(ctx) +
313359
replaceMutSetMapPlus(ctx) +
314360
replaceMutMapUpdated(ctx) +
315361
replaceIterableSameElements(ctx) +

0 commit comments

Comments
 (0)