Skip to content

Commit e1a5c59

Browse files
Rewrite: CrossCompat CanBuildFrom
1 parent 0aacee7 commit e1a5c59

File tree

7 files changed

+115
-126
lines changed

7 files changed

+115
-126
lines changed

scalafix-input/src/main/scala/fix/CanBuildFromNegSrc.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
rule = "scala:fix.NewCollections"
2+
rule = "scala:fix.CrossCompat"
33
*/
44
package fix
55

scalafix-input/src/main/scala/fix/CanBuildFromSrc.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
rule = "scala:fix.NewCollections"
2+
rule = "scala:fix.CrossCompat"
33
*/
44
package fix
55

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
3+
4+
package fix
5+
6+
import scala.language.higherKinds
7+
8+
import scala.collection.compat._
9+
10+
class CanBuildFromSrc() {
11+
12+
def f[C0, A, C1[_]](c0: C0)(implicit
13+
cbf: Factory[Int, C1[Int]],
14+
cbf2: Factory[String, C1[String]],
15+
cbf3: BuildFrom[C0, A, C1[A]]): C1[Int] = {
16+
17+
val b = cbf.newBuilder
18+
val b2 = cbf.newBuilder
19+
val b3 = cbf.newBuilder
20+
val b4 = cbf2.newBuilder
21+
val b5 = cbf3.newBuilder(c0)
22+
val b6 = cbf3.newBuilder(c0)
23+
24+
cbf.fromSpecific(List.empty[Int])
25+
cbf2.fromSpecific(List.empty[String])
26+
b.result()
27+
}
28+
29+
def kind(implicit cbf: BuildFrom[String, Char, String],
30+
cbf2: BuildFrom[String, (Int, Boolean), Map[Int, Boolean]]): Unit = {
31+
32+
cbf.newBuilder("")
33+
cbf2.newBuilder("")
34+
()
35+
}
36+
}

scalafix-output213/src/main/scala/fix/CanBuildFromSrc.scala

-35
This file was deleted.

scalafix-rules/src/main/scala/fix/CanBuildFrom.scala

+34-44
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ case class CanBuildFrom(param: Name, cbf: Type) {
6666
}.asPatch
6767

6868
val parameterType =
69-
ctx.replaceTree(cbf, "collection.BuildFrom")
69+
ctx.replaceTree(cbf, "BuildFrom")
7070

7171
parameterType + cbfCalls
7272
}
@@ -78,37 +78,28 @@ object CanBuildFromNothing {
7878
ctx: RuleCtx,
7979
collectionCanBuildFrom: SymbolMatcher,
8080
nothing: SymbolMatcher,
81-
toTpe: SymbolMatcher)(implicit index: SemanticdbIndex): (Patch, Set[Tree]) = {
82-
val handledTo = Set.newBuilder[Tree]
83-
84-
val patches =
85-
paramss.flatten.collect{
86-
case
87-
Term.Param(
88-
List(Mod.Implicit()),
89-
param,
90-
Some(
91-
tpe @ Type.Apply(
92-
collectionCanBuildFrom(_),
93-
List(
94-
nothing(_),
95-
t,
96-
cct @ Type.Apply(
97-
cc,
98-
_
99-
)
81+
toTpe: SymbolMatcher)(implicit index: SemanticdbIndex): Patch = {
82+
paramss.flatten.collect{
83+
case
84+
Term.Param(
85+
List(Mod.Implicit()),
86+
param,
87+
Some(
88+
tpe @ Type.Apply(
89+
collectionCanBuildFrom(_),
90+
List(
91+
nothing(_),
92+
t,
93+
cct @ Type.Apply(
94+
cc,
95+
_
10096
)
10197
)
102-
),
103-
_
104-
) => new CanBuildFromNothing(param, tpe, t, cct, cc, body, ctx, toTpe)
105-
}.map{cbf =>
106-
val (ps, ht) = cbf.toFactory
107-
handledTo ++= ht
108-
ps
109-
}.asPatch
110-
111-
(patches, handledTo.result())
98+
)
99+
),
100+
_
101+
) => new CanBuildFromNothing(param, tpe, t, cct, cc, body, ctx, toTpe)
102+
}.map(_.toFactory).asPatch
112103
}
113104
}
114105

@@ -129,9 +120,7 @@ case class CanBuildFromNothing(param: Name,
129120
body: Term,
130121
ctx: RuleCtx,
131122
toTpe: SymbolMatcher) {
132-
def toFactory(implicit index: SemanticdbIndex): (Patch, Set[Tree]) = {
133-
val handledTo = Set.newBuilder[Tree]
134-
123+
def toFactory(implicit index: SemanticdbIndex): Patch = {
135124
val matchCbf = SymbolMatcher.exact(ctx.index.symbol(param).get)
136125

137126
// cbf() / cbf.apply => cbf.newBuilder
@@ -160,11 +149,10 @@ case class CanBuildFromNothing(param: Name,
160149

161150
val matchCC = SymbolMatcher.exact(ctx.index.symbol(cc).get)
162151

163-
// e.to[CC] => e.to(cbf)
152+
// e.to[CC] => cbf.fromSpecific(e)
164153
val toCalls =
165154
body.collect {
166-
case ap @ Term.ApplyType(Term.Select(_, to @ toTpe(_)), List(cc2 @ matchCC(_))) =>
167-
handledTo += to
155+
case ap @ Term.ApplyType(Term.Select(e, to @ toTpe(_)), List(cc2 @ matchCC(_))) =>
168156

169157
// e.to[CC](*cbf*) extract implicit parameter
170158
val synth = ctx.index.synthetics.find(_.position.end == ap.pos.end).get
@@ -173,23 +161,25 @@ case class CanBuildFromNothing(param: Name,
173161
// This is a bit unsafe
174162
// https://github.com/scalameta/scalameta/issues/1636
175163
if (implicitCbf.syntax == param.syntax) {
176-
trailingBrackets(to, ctx).map { case (open, close) =>
177-
ctx.replaceTree(cc2, implicitCbf.syntax) +
178-
ctx.replaceToken(open, "(") +
179-
ctx.replaceToken(close, ")")
180-
}.asPatch
164+
165+
// .to[CC]
166+
val apToRemove = ap.tokens.slice(e.tokens.end - ap.tokens.start, ap.tokens.size)
167+
168+
ctx.removeTokens(apToRemove) +
169+
ctx.addLeft(e, implicitCbf.syntax + ".fromSpecific(") +
170+
ctx.addRight(e, ")")
181171
} else Patch.empty
182172

183173
}.asPatch
184174

185175
// implicit cbf: collection.generic.CanBuildFrom[Nothing, Int, CC[Int]] =>
186-
// implicit cbf: collection.Factory[Int, CC[Int]]
176+
// implicit cbf: Factory[Int, CC[Int]]
187177
val parameterType =
188178
ctx.replaceTree(
189179
tpe,
190-
Type.Apply(Type.Name("collection.Factory"), List(t, cct)).syntax
180+
Type.Apply(Type.Name("Factory"), List(t, cct)).syntax
191181
)
192182

193-
(parameterType + cbfCalls + toCalls, handledTo.result())
183+
parameterType + cbfCalls + toCalls
194184
}
195185
}

scalafix-rules/src/main/scala/fix/NewCollections.scala

+9-37
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,15 @@ import scala.meta._
77

88
// Not 2.12 Cross-Compatible
99
case class NewCollections(index: SemanticdbIndex) extends SemanticRule(index, "NewCollections") with Stable212Base {
10-
// Two rules triggers the same rewrite TraversableLike.to and CanBuildFrom
11-
// we keep track of what is handled in CanBuildFrom and guard against TraversableLike.to
12-
val handledTo = scala.collection.mutable.Set[Tree]()
13-
1410
// == Symbols ==
15-
val collectionCanBuildFrom = exact("_root_.scala.collection.generic.CanBuildFrom#")
16-
val collectionCanBuildFromImport = exact("_root_.scala.collection.generic.CanBuildFrom.;_root_.scala.collection.generic.CanBuildFrom#")
17-
val nothing = exact("_root_.scala.Nothing#")
18-
val iterableSameElement = exact("_root_.scala.collection.IterableLike#sameElements(Lscala/collection/GenIterable;)Z.")
19-
val toTpe = normalized("_root_.scala.collection.TraversableLike.to.")
20-
val iterator = normalized("_root_.scala.collection.TraversableLike.toIterator.")
21-
val tupleZipped = normalized("_root_.scala.runtime.Tuple2Zipped.Ops.zipped.",
22-
"_root_.scala.runtime.Tuple3Zipped.Ops.zipped.")
23-
val retainMap = normalized("_root_.scala.collection.mutable.MapLike.retain.")
24-
val retainSet = normalized("_root_.scala.collection.mutable.SetLike.retain.")
11+
val iterableSameElement = exact("_root_.scala.collection.IterableLike#sameElements(Lscala/collection/GenIterable;)Z.")
12+
val iterator = normalized("_root_.scala.collection.TraversableLike.toIterator.")
13+
val tupleZipped = normalized(
14+
"_root_.scala.runtime.Tuple2Zipped.Ops.zipped.",
15+
"_root_.scala.runtime.Tuple3Zipped.Ops.zipped."
16+
)
17+
val retainMap = normalized("_root_.scala.collection.mutable.MapLike.retain.")
18+
val retainSet = normalized("_root_.scala.collection.mutable.SetLike.retain.")
2519

2620
object Breakout {
2721
implicit class RichSymbol(val symbol: Symbol) {
@@ -112,7 +106,7 @@ case class NewCollections(index: SemanticdbIndex) extends SemanticRule(index, "N
112106
case iterator(t: Name) =>
113107
ctx.replaceTree(t, "iterator")
114108

115-
case t @ toTpe(n: Name) if !handledTo.contains(n) =>
109+
case t @ toTpe(n: Name) =>
116110
trailingBrackets(n, ctx).map { case (open, close) =>
117111
ctx.replaceToken(open, "(") + ctx.replaceToken(close, ")")
118112
}.asPatch
@@ -167,27 +161,6 @@ case class NewCollections(index: SemanticdbIndex) extends SemanticRule(index, "N
167161
}.asPatch
168162
}
169163

170-
def replaceCanBuildFrom(ctx: RuleCtx): Patch = {
171-
val useSites =
172-
ctx.tree.collect {
173-
case Defn.Def(_, _, _, paramss, _, body) =>
174-
val (ps1, ht) = CanBuildFromNothing(paramss, body, ctx, collectionCanBuildFrom, nothing, toTpe)
175-
val ps2 = CanBuildFrom (paramss, body, ctx, collectionCanBuildFrom, nothing)
176-
handledTo ++= ht
177-
178-
ps1 + ps2
179-
}.asPatch
180-
181-
val imports =
182-
ctx.tree.collect {
183-
case i: Importee if collectionCanBuildFromImport.matches(i) =>
184-
ctx.removeImportee(i)
185-
}.asPatch
186-
187-
if (useSites.nonEmpty) useSites + imports
188-
else Patch.empty
189-
}
190-
191164
def replaceBreakout(ctx: RuleCtx): Patch = {
192165
import Breakout._
193166

@@ -244,7 +217,6 @@ case class NewCollections(index: SemanticdbIndex) extends SemanticRule(index, "N
244217

245218
override def fix(ctx: RuleCtx): Patch = {
246219
super.fix(ctx) +
247-
replaceCanBuildFrom(ctx) +
248220
replaceToList(ctx) +
249221
replaceSymbols(ctx) +
250222
replaceTupleZipped(ctx) +

scalafix-rules/src/main/scala/fix/Stable212Base.scala

+34-8
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,19 @@ trait Stable212Base { self: SemanticRule =>
1717
normalized(s"_root_.scala.collection.TraversableOnce.`$op`.")
1818
}
1919

20-
val copyToBuffer = normalized("_root_.scala.collection.TraversableOnce.copyToBuffer.")
20+
val toTpe = normalized("_root_.scala.collection.TraversableLike.to.")
21+
val copyToBuffer = normalized("_root_.scala.collection.TraversableOnce.copyToBuffer.")
2122
val arrayBuilderMake = normalized("_root_.scala.collection.mutable.ArrayBuilder.make(Lscala/reflect/ClassTag;)Lscala/collection/mutable/ArrayBuilder;.")
22-
val setPlus2 = exact("_root_.scala.collection.SetLike#`+`(Ljava/lang/Object;Ljava/lang/Object;Lscala/collection/Seq;)Lscala/collection/Set;.")
23-
val mapPlus2 = exact("_root_.scala.collection.immutable.MapLike#`+`(Lscala/Tuple2;Lscala/Tuple2;Lscala/collection/Seq;)Lscala/collection/immutable/Map;.")
24-
val mutSetPlus = exact("_root_.scala.collection.mutable.SetLike#`+`(Ljava/lang/Object;)Lscala/collection/mutable/Set;.")
25-
val mutMapPlus = exact("_root_.scala.collection.mutable.MapLike#`+`(Lscala/Tuple2;)Lscala/collection/mutable/Map;.")
26-
val mutMapUpdate = exact("_root_.scala.collection.mutable.MapLike#updated(Ljava/lang/Object;Ljava/lang/Object;)Lscala/collection/mutable/Map;.")
27-
val foldLeftSymbol = foldSymbol(isLeft = true)
28-
val foldRightSymbol = foldSymbol(isLeft = false)
23+
val collectionCanBuildFrom = exact("_root_.scala.collection.generic.CanBuildFrom#")
24+
val collectionCanBuildFromImport = exact("_root_.scala.collection.generic.CanBuildFrom.;_root_.scala.collection.generic.CanBuildFrom#")
25+
val nothing = exact("_root_.scala.Nothing#")
26+
val setPlus2 = exact("_root_.scala.collection.SetLike#`+`(Ljava/lang/Object;Ljava/lang/Object;Lscala/collection/Seq;)Lscala/collection/Set;.")
27+
val mapPlus2 = exact("_root_.scala.collection.immutable.MapLike#`+`(Lscala/Tuple2;Lscala/Tuple2;Lscala/collection/Seq;)Lscala/collection/immutable/Map;.")
28+
val mutSetPlus = exact("_root_.scala.collection.mutable.SetLike#`+`(Ljava/lang/Object;)Lscala/collection/mutable/Set;.")
29+
val mutMapPlus = exact("_root_.scala.collection.mutable.MapLike#`+`(Lscala/Tuple2;)Lscala/collection/mutable/Map;.")
30+
val mutMapUpdate = exact("_root_.scala.collection.mutable.MapLike#updated(Ljava/lang/Object;Ljava/lang/Object;)Lscala/collection/mutable/Map;.")
31+
val foldLeftSymbol = foldSymbol(isLeft = true)
32+
val foldRightSymbol = foldSymbol(isLeft = false)
2933

3034
// == Rules ==
3135

@@ -116,8 +120,30 @@ trait Stable212Base { self: SemanticRule =>
116120
}.asPatch
117121
}
118122

123+
def replaceCanBuildFrom(ctx: RuleCtx): Patch = {
124+
val useSites =
125+
ctx.tree.collect {
126+
case Defn.Def(_, _, _, paramss, _, body) =>
127+
CanBuildFromNothing(paramss, body, ctx, collectionCanBuildFrom, nothing, toTpe) +
128+
CanBuildFrom(paramss, body, ctx, collectionCanBuildFrom, nothing)
129+
}.asPatch
130+
131+
val imports =
132+
ctx.tree.collect {
133+
case i: Importee if collectionCanBuildFromImport.matches(i) =>
134+
ctx.removeImportee(i)
135+
}.asPatch
136+
137+
val compatImport =
138+
ctx.addGlobalImport(importer"scala.collection.compat._")
139+
140+
if (useSites.nonEmpty) useSites + imports + compatImport
141+
else Patch.empty
142+
}
143+
119144
override def fix(ctx: RuleCtx): Patch = {
120145
replaceSymbols0(ctx) +
146+
replaceCanBuildFrom(ctx) +
121147
replaceCopyToBuffer(ctx) +
122148
replaceSymbolicFold(ctx) +
123149
replaceSetMapPlus2(ctx) +

0 commit comments

Comments
 (0)