Skip to content

Commit f823520

Browse files
authored
Merge pull request scala#66 from MasseGuillaume/experimental
Experimental
2 parents ec8230f + e1ca895 commit f823520

File tree

6 files changed

+183
-6
lines changed

6 files changed

+183
-6
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
rule = "scala:fix.Experimental"
3+
*/
4+
package fix
5+
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 ExperimentalSrc(iset: immutable.Set[Int],
11+
cset: collection.Set[Int],
12+
imap: immutable.Map[Int, Int],
13+
cmap: collection.Map[Int, Int]) {
14+
iset + 1
15+
iset - 2
16+
cset + 1
17+
cset - 2
18+
19+
cmap + (2 -> 3)
20+
cmap + ((4, 5))
21+
imap + (2 -> 3)
22+
imap + ((4, 5))
23+
24+
// Map.zip
25+
imap.zip(List())
26+
List().zip(List())
27+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package fix
66
class SetMapSrc(set: Set[Int], map: Map[Int, Int]) {
77
set + (2, 3)
88
map + (2 -> 3, 3 -> 4)
9-
(set + (2, 3)).map(x => x)
9+
(set + (2, 3)).toString
1010
set + (2, 3) - 4
1111
map.mapValues(_ + 1)
1212
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
2+
3+
4+
package fix
5+
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 ExperimentalSrc(iset: immutable.Set[Int],
11+
cset: collection.Set[Int],
12+
imap: immutable.Map[Int, Int],
13+
cmap: collection.Map[Int, Int]) {
14+
iset + 1
15+
iset - 2
16+
cset ++ _root_.scala.collection.Set(1)
17+
cset -- _root_.scala.collection.Set(2)
18+
19+
cmap ++ _root_.scala.collection.Map(2 -> 3)
20+
cmap ++ _root_.scala.collection.Map((4, 5))
21+
imap + (2 -> 3)
22+
imap + ((4, 5))
23+
24+
// Map.zip
25+
imap.zip(List()).toMap
26+
List().zip(List())
27+
}

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ package fix
66
class SetMapSrc(set: Set[Int], map: Map[Int, Int]) {
77
set + 2 + 3
88
map + (2 -> 3) + (3 -> 4)
9-
(set + 2 + 3).map(x => x)
9+
(set + 2 + 3).toString
1010
set + 2 + 3 - 4
1111
map.mapValues(_ + 1).toMap
1212
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package fix
2+
3+
import scalafix._
4+
import scalafix.syntax._
5+
import scalafix.util._
6+
import scala.meta._
7+
8+
case class Experimental(index: SemanticdbIndex) extends SemanticRule(index, "Experimental") {
9+
// WARNING: TOTAL HACK
10+
// this is only to unblock us until Term.tpe is available: https://github.com/scalameta/scalameta/issues/1212
11+
// if we have a simple identifier, we can look at his definition at query it's type
12+
// this should be improved in future version of scalameta
13+
object TypeMatcher {
14+
def apply(symbols: Symbol*)(implicit index: SemanticdbIndex): TypeMatcher =
15+
new TypeMatcher(symbols: _*)(index)
16+
}
17+
18+
final class TypeMatcher(symbols: Symbol*)(implicit index: SemanticdbIndex) {
19+
def unapply(tree: Tree): Boolean = {
20+
index.denotation(tree)
21+
.exists(_.names.headOption.exists(n => symbols.exists(_ == n.symbol)))
22+
}
23+
}
24+
25+
val CollectionMap: TypeMatcher = TypeMatcher(
26+
Symbol("_root_.scala.collection.immutable.Map#"),
27+
Symbol("_root_.scala.collection.mutable.Map#"),
28+
Symbol("_root_.scala.Predef.Map#")
29+
)
30+
31+
val CollectionSet: TypeMatcher = TypeMatcher(Symbol("_root_.scala.collection.Set#"))
32+
33+
val mapZip =
34+
SymbolMatcher.exact(
35+
Symbol("_root_.scala.collection.IterableLike#zip(Lscala/collection/GenIterable;Lscala/collection/generic/CanBuildFrom;)Ljava/lang/Object;.")
36+
)
37+
38+
val mapPlus =
39+
SymbolMatcher.exact(
40+
Symbol("_root_.scala.collection.MapLike#`+`(Lscala/Tuple2;)Lscala/collection/Map;.")
41+
)
42+
43+
val setPlus =
44+
SymbolMatcher.exact(
45+
Symbol("_root_.scala.collection.SetLike#`+`(Ljava/lang/Object;)Lscala/collection/Set;.")
46+
)
47+
48+
val setMinus =
49+
SymbolMatcher.exact(
50+
Symbol("_root_.scala.collection.SetLike#`-`(Ljava/lang/Object;)Lscala/collection/Set;.")
51+
)
52+
53+
def startsWithParens(tree: Tree): Boolean =
54+
tree.tokens.headOption.map(_.is[Token.LeftParen]).getOrElse(false)
55+
56+
def replaceMapZip(ctx: RuleCtx): Patch = {
57+
ctx.tree.collect {
58+
case ap @ Term.Apply(Term.Select(CollectionMap(), mapZip(_)), List(_)) =>
59+
ctx.addRight(ap, ".toMap")
60+
}.asPatch
61+
}
62+
63+
def replaceSetMapPlusMinus(ctx: RuleCtx): Patch = {
64+
def rewriteOp(op: Tree, rhs: Tree, doubleOp: String, col0: String): Patch = {
65+
val col = "_root_.scala.collection." + col0
66+
val callSite =
67+
if (startsWithParens(rhs)) {
68+
ctx.addLeft(rhs, col)
69+
}
70+
else {
71+
ctx.addLeft(rhs, col + "(") +
72+
ctx.addRight(rhs, ")")
73+
}
74+
75+
ctx.addRight(op, doubleOp) + callSite
76+
}
77+
78+
ctx.tree.collect {
79+
case Term.ApplyInfix(CollectionSet(), op @ setPlus(_), Nil, List(rhs)) =>
80+
rewriteOp(op, rhs, "+", "Set")
81+
82+
case Term.ApplyInfix(CollectionSet(), op @ setMinus(_), Nil, List(rhs)) =>
83+
rewriteOp(op, rhs, "-", "Set")
84+
85+
case Term.ApplyInfix(_, op @ mapPlus(_), Nil, List(rhs)) =>
86+
rewriteOp(op, rhs, "+", "Map")
87+
}.asPatch
88+
}
89+
90+
override def fix(ctx: RuleCtx): Patch =
91+
replaceSetMapPlusMinus(ctx) +
92+
replaceMapZip(ctx)
93+
}

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

+34-4
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,31 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
2626
close <- ctx.matchingParens.close(open)
2727
} yield (open, close)
2828

29+
// terms dont give us terms https://github.com/scalameta/scalameta/issues/1212
30+
// WARNING: TOTAL HACK
31+
// this is only to unblock us until Term.tpe is available: https://github.com/scalameta/scalameta/issues/1212
32+
// if we have a simple identifier, we can look at his definition at query it's type
33+
// this should be improved in future version of scalameta
34+
object TypeMatcher {
35+
def apply(symbols: Symbol*)(implicit index: SemanticdbIndex): TypeMatcher =
36+
new TypeMatcher(symbols: _*)(index)
37+
}
38+
39+
final class TypeMatcher(symbols: Symbol*)(implicit index: SemanticdbIndex) {
40+
def unapply(tree: Tree): Boolean = {
41+
index.denotation(tree)
42+
.exists(_.names.headOption.exists(n => symbols.exists(_ == n.symbol)))
43+
}
44+
}
45+
46+
val CollectionMap: TypeMatcher = TypeMatcher(
47+
Symbol("_root_.scala.collection.immutable.Map#"),
48+
Symbol("_root_.scala.collection.mutable.Map#"),
49+
Symbol("_root_.scala.Predef.Map#")
50+
)
51+
52+
val CollectionSet: TypeMatcher = TypeMatcher(Symbol("_root_.scala.collection.Set#"))
53+
2954
def replaceSymbols(ctx: RuleCtx): Patch = {
3055
ctx.replaceSymbols(
3156
"scala.collection.LinearSeq" -> "scala.collection.immutable.List",
@@ -110,11 +135,15 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
110135
Symbol("_root_.scala.collection.mutable.SetLike.retain.")
111136
)
112137

138+
113139
val arrayBuilderMake =
114140
SymbolMatcher.normalized(
115141
Symbol("_root_.scala.collection.mutable.ArrayBuilder.make(Lscala/reflect/ClassTag;)Lscala/collection/mutable/ArrayBuilder;.")
116142
)
117143

144+
def startsWithParens(tree: Tree): Boolean =
145+
tree.tokens.headOption.map(_.is[Token.LeftParen]).getOrElse(false)
146+
118147
def replaceMutableSet(ctx: RuleCtx) =
119148
ctx.tree.collect {
120149
case retainSet(n: Name) =>
@@ -216,7 +245,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
216245
def replaceSetMapPlus2(ctx: RuleCtx): Patch = {
217246
def rewritePlus(ap: Term.ApplyInfix, lhs: Term, op: Term.Name, rhs1: Term, rhs2: Term): Patch = {
218247
val tokensToReplace =
219-
if(ap.tokens.headOption.map(_.is[Token.LeftParen]).getOrElse(false)) {
248+
if(startsWithParens(ap)) {
220249
// don't drop surrounding parens
221250
ap.tokens.slice(1, ap.tokens.size - 1)
222251
} else ap.tokens
@@ -278,10 +307,11 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
278307
case ap @ Term.Apply(at @ Term.ApplyType(Term.Select(lhs, arrayBuilderMake(_)), args), Nil) =>
279308
val extraParens =
280309
ap.tokens.slice(at.tokens.size, ap.tokens.size)
281-
282310
ctx.removeTokens(extraParens)
283311
}.asPatch
284312
}
313+
314+
285315

286316
def replaceMapMapValues(ctx: RuleCtx): Patch = {
287317
ctx.tree.collect {
@@ -485,7 +515,7 @@ case class Scalacollectioncompat_newcollections(index: SemanticdbIndex)
485515
replaceMutSetMapPlus(ctx) +
486516
replaceMutMapUpdated(ctx) +
487517
replaceArrayBuilderMake(ctx) +
488-
replaceMapMapValues(ctx) +
489-
replaceIterableSameElements(ctx)
518+
replaceIterableSameElements(ctx) +
519+
replaceMapMapValues(ctx)
490520
}
491521
}

0 commit comments

Comments
 (0)