Skip to content

Commit 29c3a36

Browse files
authored
Merge pull request #136 from MasseGuillaume/cbf-take3
CanBuildFrom: Add support for Class and simple form for To (fix #135)
2 parents 787282b + 01b1edb commit 29c3a36

File tree

4 files changed

+101
-64
lines changed

4 files changed

+101
-64
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,11 @@ object CanBuildFromSrc {
3333
cbf2("")
3434
()
3535
}
36+
37+
def f2[T, That](implicit cbf: CanBuildFrom[Nothing, T, That]): Foo[T, That] =
38+
new Foo
39+
40+
class Foo[T, That](implicit cbf: CanBuildFrom[Nothing, T, That]) {
41+
val b = cbf()
42+
}
3643
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,11 @@ object CanBuildFromSrc {
3333
cbf2.newBuilder("")
3434
()
3535
}
36+
37+
def f2[T, That](implicit cbf: Factory[T, That]): Foo[T, That] =
38+
new Foo
39+
40+
class Foo[T, That](implicit cbf: Factory[T, That]) {
41+
val b = cbf.newBuilder
42+
}
3643
}

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

Lines changed: 70 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,21 @@ import scala.collection.mutable
88

99
object CanBuildFrom {
1010
def apply(paramss: List[List[Term.Param]],
11-
body: Term,
11+
stats: List[Tree],
1212
ctx: RuleCtx,
1313
collectionCanBuildFrom: SymbolMatcher,
1414
nothing: SymbolMatcher)(implicit index: SemanticdbIndex): Patch = {
1515
// CanBuildFrom has def apply() but not CanBuild
1616
def emptyApply(param: Name): Boolean = {
1717
import scala.meta.contrib._
1818
val matchCbf = SymbolMatcher.exact(ctx.index.symbol(param).get)
19-
body.exists {
20-
case Term.Apply(Term.Select(matchCbf(_), _), Nil) => true
21-
case Term.Apply(matchCbf(_), Nil) => true
22-
case _ => false
23-
}
19+
stats.exists(
20+
_.exists {
21+
case Term.Apply(Term.Select(matchCbf(_), _), Nil) => true
22+
case Term.Apply(matchCbf(_), Nil) => true
23+
case _ => false
24+
}
25+
)
2426
}
2527

2628
paramss.flatten
@@ -38,7 +40,7 @@ object CanBuildFrom {
3840
) if !nothing.matches(p1) && !emptyApply(param) =>
3941
new CanBuildFrom(param, cbf)
4042
}
41-
.map(_.toBuildFrom(body, ctx))
43+
.map(_.toBuildFrom(stats, ctx))
4244
.asPatch
4345
}
4446
}
@@ -48,7 +50,7 @@ object CanBuildFrom {
4850
// param: cbf
4951
// cbf : collection.generic.CanBuildFrom
5052
case class CanBuildFrom(param: Name, cbf: Type) {
51-
def toBuildFrom(body: Term, ctx: RuleCtx)(implicit index: SemanticdbIndex): Patch = {
53+
def toBuildFrom(stats: List[Tree], ctx: RuleCtx)(implicit index: SemanticdbIndex): Patch = {
5254

5355
val matchCbf = SymbolMatcher.exact(ctx.index.symbol(param).get)
5456

@@ -60,15 +62,17 @@ case class CanBuildFrom(param: Name, cbf: Type) {
6062
)
6163

6264
val cbfCalls =
63-
body.collect {
64-
// cbf.apply(x)
65-
case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), List(x)) =>
66-
replaceNewBuilder(ap, cbf2, x)
67-
68-
// cbf(x)
69-
case ap @ Term.Apply(cbf2 @ matchCbf(_), List(x)) =>
70-
replaceNewBuilder(ap, cbf2, x)
71-
}.asPatch
65+
stats
66+
.map(_.collect {
67+
// cbf.apply(x)
68+
case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), List(x)) =>
69+
replaceNewBuilder(ap, cbf2, x)
70+
71+
// cbf(x)
72+
case ap @ Term.Apply(cbf2 @ matchCbf(_), List(x)) =>
73+
replaceNewBuilder(ap, cbf2, x)
74+
}.asPatch)
75+
.asPatch
7276

7377
val parameterType =
7478
ctx.replaceTree(cbf, "BuildFrom")
@@ -79,12 +83,13 @@ case class CanBuildFrom(param: Name, cbf: Type) {
7983

8084
object CanBuildFromNothing {
8185
def apply(paramss: List[List[Term.Param]],
82-
body: Term,
86+
stats: List[Tree],
8387
ctx: RuleCtx,
8488
collectionCanBuildFrom: SymbolMatcher,
8589
nothing: SymbolMatcher,
8690
toTpe: SymbolMatcher,
8791
handledTo: mutable.Set[Tree])(implicit index: SemanticdbIndex): Patch = {
92+
8893
paramss.flatten
8994
.collect {
9095
case Term.Param(
@@ -96,16 +101,13 @@ object CanBuildFromNothing {
96101
List(
97102
nothing(_),
98103
t,
99-
cct @ Type.Apply(
100-
cc,
101-
_
102-
)
104+
toT
103105
)
104106
)
105107
),
106108
_
107109
) =>
108-
new CanBuildFromNothing(param, tpe, t, cct, cc, body, ctx, toTpe, handledTo)
110+
new CanBuildFromNothing(param, tpe, t, toT, stats, ctx, toTpe, handledTo)
109111
}
110112
.map(_.toFactory)
111113
.asPatch
@@ -119,14 +121,12 @@ object CanBuildFromNothing {
119121
// tpe : collection.generic.CanBuildFrom[Nothing, Int, CC[Int]]
120122
// cbf : CanBuildFrom
121123
// v : Int
122-
// cct : CC[Int]
123-
// cc : CC
124+
// toT : CC[Int]
124125
case class CanBuildFromNothing(param: Name,
125126
tpe: Type.Apply,
126127
t: Type,
127-
cct: Type.Apply,
128-
cc: Type,
129-
body: Term,
128+
toT: Type,
129+
stats: List[Tree],
130130
ctx: RuleCtx,
131131
toTpe: SymbolMatcher,
132132
handledTo: mutable.Set[Tree]) {
@@ -141,53 +141,61 @@ case class CanBuildFromNothing(param: Name,
141141
val visitedCbfCalls = mutable.Set[Tree]()
142142

143143
val cbfCalls =
144-
body.collect {
145-
// cbf.apply()
146-
case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), Nil) =>
147-
visitedCbfCalls += sel
148-
replaceNewBuilder(ap, cbf2)
149-
150-
// cbf.apply
151-
case sel @ Term.Select(cbf2 @ matchCbf(_), ap) if (!visitedCbfCalls.contains(sel)) =>
152-
replaceNewBuilder(sel, cbf2)
153-
154-
// cbf()
155-
case ap @ Term.Apply(cbf2 @ matchCbf(_), Nil) =>
156-
replaceNewBuilder(ap, cbf2)
157-
}.asPatch
158-
159-
val matchCC = SymbolMatcher.exact(ctx.index.symbol(cc).get)
144+
stats
145+
.map(_.collect {
146+
// cbf.apply()
147+
case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), Nil) =>
148+
visitedCbfCalls += sel
149+
replaceNewBuilder(ap, cbf2)
150+
151+
// cbf.apply
152+
case sel @ Term.Select(cbf2 @ matchCbf(_), ap) if (!visitedCbfCalls.contains(sel)) =>
153+
replaceNewBuilder(sel, cbf2)
154+
155+
// cbf()
156+
case ap @ Term.Apply(cbf2 @ matchCbf(_), Nil) =>
157+
replaceNewBuilder(ap, cbf2)
158+
}.asPatch)
159+
.asPatch
160160

161161
// e.to[CC] => cbf.fromSpecific(e)
162-
val toCalls =
163-
body.collect {
164-
case ap @ Term.ApplyType(Term.Select(e, to @ toTpe(_)), List(cc2 @ matchCC(_))) =>
165-
handledTo += to
162+
val toCalls = toT match {
163+
case cct @ Type.Apply(cc, _) => {
164+
val matchCC = SymbolMatcher.exact(ctx.index.symbol(cc).get)
165+
166+
stats
167+
.map(_.collect {
168+
case ap @ Term.ApplyType(Term.Select(e, to @ toTpe(_)), List(cc2 @ matchCC(_))) =>
169+
handledTo += to
166170

167-
// e.to[CC](*cbf*) extract implicit parameter
168-
val synth = ctx.index.synthetics.find(_.position.end == ap.pos.end).get
169-
val Term.Apply(_, List(implicitCbf)) = synth.text.parse[Term].get
171+
// e.to[CC](*cbf*) extract implicit parameter
172+
val synth = ctx.index.synthetics.find(_.position.end == ap.pos.end).get
173+
val Term.Apply(_, List(implicitCbf)) = synth.text.parse[Term].get
170174

171-
// This is a bit unsafe
172-
// https://github.com/scalameta/scalameta/issues/1636
173-
if (implicitCbf.syntax == param.syntax) {
175+
// This is a bit unsafe
176+
// https://github.com/scalameta/scalameta/issues/1636
177+
if (implicitCbf.syntax == param.syntax) {
174178

175-
// .to[CC]
176-
val apToRemove = ap.tokens.slice(e.tokens.end - ap.tokens.start, ap.tokens.size)
179+
// .to[CC]
180+
val apToRemove = ap.tokens.slice(e.tokens.end - ap.tokens.start, ap.tokens.size)
177181

178-
ctx.removeTokens(apToRemove) +
179-
ctx.addLeft(e, implicitCbf.syntax + ".fromSpecific(") +
180-
ctx.addRight(e, ")")
181-
} else Patch.empty
182+
ctx.removeTokens(apToRemove) +
183+
ctx.addLeft(e, implicitCbf.syntax + ".fromSpecific(") +
184+
ctx.addRight(e, ")")
185+
} else Patch.empty
182186

183-
}.asPatch
187+
}.asPatch)
188+
.asPatch
189+
}
190+
case _ => Patch.empty
191+
}
184192

185193
// implicit cbf: collection.generic.CanBuildFrom[Nothing, Int, CC[Int]] =>
186194
// implicit cbf: Factory[Int, CC[Int]]
187195
val parameterType =
188196
ctx.replaceTree(
189197
tpe,
190-
Type.Apply(Type.Name("Factory"), List(t, cct)).syntax
198+
Type.Apply(Type.Name("Factory"), List(t, toT)).syntax
191199
)
192200

193201
parameterType + cbfCalls + toCalls

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

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,23 @@ trait Stable212Base extends CrossCompatibility { self: SemanticRule =>
234234
val useSites =
235235
ctx.tree.collect {
236236
case Defn.Def(_, _, _, paramss, _, body) =>
237-
CanBuildFromNothing(paramss, body, ctx, collectionCanBuildFrom, nothing, toTpe, handledTo) +
238-
CanBuildFrom(paramss, body, ctx, collectionCanBuildFrom, nothing)
237+
CanBuildFromNothing(paramss,
238+
List(body),
239+
ctx,
240+
collectionCanBuildFrom,
241+
nothing,
242+
toTpe,
243+
handledTo) +
244+
CanBuildFrom(paramss, List(body), ctx, collectionCanBuildFrom, nothing)
245+
case Defn.Class(_, _, _, Ctor.Primary(_, _, paramss), Template(_, _, _, stats)) =>
246+
CanBuildFromNothing(paramss,
247+
stats,
248+
ctx,
249+
collectionCanBuildFrom,
250+
nothing,
251+
toTpe,
252+
handledTo) +
253+
CanBuildFrom(paramss, stats, ctx, collectionCanBuildFrom, nothing)
239254
}.asPatch
240255

241256
val imports =

0 commit comments

Comments
 (0)