diff --git a/scalafix/input/src/main/scala/fix/CanBuildFromSrc.scala b/scalafix/input/src/main/scala/fix/CanBuildFromSrc.scala index e1c9148f..50c5eda3 100644 --- a/scalafix/input/src/main/scala/fix/CanBuildFromSrc.scala +++ b/scalafix/input/src/main/scala/fix/CanBuildFromSrc.scala @@ -33,4 +33,11 @@ object CanBuildFromSrc { cbf2("") () } + + def f2[T, That](implicit cbf: CanBuildFrom[Nothing, T, That]): Foo[T, That] = + new Foo + + class Foo[T, That](implicit cbf: CanBuildFrom[Nothing, T, That]) { + val b = cbf() + } } diff --git a/scalafix/output/src/main/scala/fix/CanBuildFromSrc.scala b/scalafix/output/src/main/scala/fix/CanBuildFromSrc.scala index ddd7d706..ffa0092d 100644 --- a/scalafix/output/src/main/scala/fix/CanBuildFromSrc.scala +++ b/scalafix/output/src/main/scala/fix/CanBuildFromSrc.scala @@ -33,4 +33,11 @@ object CanBuildFromSrc { cbf2.newBuilder("") () } + + def f2[T, That](implicit cbf: Factory[T, That]): Foo[T, That] = + new Foo + + class Foo[T, That](implicit cbf: Factory[T, That]) { + val b = cbf.newBuilder + } } diff --git a/scalafix/rules/src/main/scala/fix/CanBuildFrom.scala b/scalafix/rules/src/main/scala/fix/CanBuildFrom.scala index df886da5..de81577c 100644 --- a/scalafix/rules/src/main/scala/fix/CanBuildFrom.scala +++ b/scalafix/rules/src/main/scala/fix/CanBuildFrom.scala @@ -8,7 +8,7 @@ import scala.collection.mutable object CanBuildFrom { def apply(paramss: List[List[Term.Param]], - body: Term, + stats: List[Tree], ctx: RuleCtx, collectionCanBuildFrom: SymbolMatcher, nothing: SymbolMatcher)(implicit index: SemanticdbIndex): Patch = { @@ -16,11 +16,13 @@ object CanBuildFrom { def emptyApply(param: Name): Boolean = { import scala.meta.contrib._ val matchCbf = SymbolMatcher.exact(ctx.index.symbol(param).get) - body.exists { - case Term.Apply(Term.Select(matchCbf(_), _), Nil) => true - case Term.Apply(matchCbf(_), Nil) => true - case _ => false - } + stats.exists( + _.exists { + case Term.Apply(Term.Select(matchCbf(_), _), Nil) => true + case Term.Apply(matchCbf(_), Nil) => true + case _ => false + } + ) } paramss.flatten @@ -38,7 +40,7 @@ object CanBuildFrom { ) if !nothing.matches(p1) && !emptyApply(param) => new CanBuildFrom(param, cbf) } - .map(_.toBuildFrom(body, ctx)) + .map(_.toBuildFrom(stats, ctx)) .asPatch } } @@ -48,7 +50,7 @@ object CanBuildFrom { // param: cbf // cbf : collection.generic.CanBuildFrom case class CanBuildFrom(param: Name, cbf: Type) { - def toBuildFrom(body: Term, ctx: RuleCtx)(implicit index: SemanticdbIndex): Patch = { + def toBuildFrom(stats: List[Tree], ctx: RuleCtx)(implicit index: SemanticdbIndex): Patch = { val matchCbf = SymbolMatcher.exact(ctx.index.symbol(param).get) @@ -60,15 +62,17 @@ case class CanBuildFrom(param: Name, cbf: Type) { ) val cbfCalls = - body.collect { - // cbf.apply(x) - case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), List(x)) => - replaceNewBuilder(ap, cbf2, x) - - // cbf(x) - case ap @ Term.Apply(cbf2 @ matchCbf(_), List(x)) => - replaceNewBuilder(ap, cbf2, x) - }.asPatch + stats + .map(_.collect { + // cbf.apply(x) + case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), List(x)) => + replaceNewBuilder(ap, cbf2, x) + + // cbf(x) + case ap @ Term.Apply(cbf2 @ matchCbf(_), List(x)) => + replaceNewBuilder(ap, cbf2, x) + }.asPatch) + .asPatch val parameterType = ctx.replaceTree(cbf, "BuildFrom") @@ -79,12 +83,13 @@ case class CanBuildFrom(param: Name, cbf: Type) { object CanBuildFromNothing { def apply(paramss: List[List[Term.Param]], - body: Term, + stats: List[Tree], ctx: RuleCtx, collectionCanBuildFrom: SymbolMatcher, nothing: SymbolMatcher, toTpe: SymbolMatcher, handledTo: mutable.Set[Tree])(implicit index: SemanticdbIndex): Patch = { + paramss.flatten .collect { case Term.Param( @@ -96,16 +101,13 @@ object CanBuildFromNothing { List( nothing(_), t, - cct @ Type.Apply( - cc, - _ - ) + toT ) ) ), _ ) => - new CanBuildFromNothing(param, tpe, t, cct, cc, body, ctx, toTpe, handledTo) + new CanBuildFromNothing(param, tpe, t, toT, stats, ctx, toTpe, handledTo) } .map(_.toFactory) .asPatch @@ -119,14 +121,12 @@ object CanBuildFromNothing { // tpe : collection.generic.CanBuildFrom[Nothing, Int, CC[Int]] // cbf : CanBuildFrom // v : Int -// cct : CC[Int] -// cc : CC +// toT : CC[Int] case class CanBuildFromNothing(param: Name, tpe: Type.Apply, t: Type, - cct: Type.Apply, - cc: Type, - body: Term, + toT: Type, + stats: List[Tree], ctx: RuleCtx, toTpe: SymbolMatcher, handledTo: mutable.Set[Tree]) { @@ -141,53 +141,61 @@ case class CanBuildFromNothing(param: Name, val visitedCbfCalls = mutable.Set[Tree]() val cbfCalls = - body.collect { - // cbf.apply() - case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), Nil) => - visitedCbfCalls += sel - replaceNewBuilder(ap, cbf2) - - // cbf.apply - case sel @ Term.Select(cbf2 @ matchCbf(_), ap) if (!visitedCbfCalls.contains(sel)) => - replaceNewBuilder(sel, cbf2) - - // cbf() - case ap @ Term.Apply(cbf2 @ matchCbf(_), Nil) => - replaceNewBuilder(ap, cbf2) - }.asPatch - - val matchCC = SymbolMatcher.exact(ctx.index.symbol(cc).get) + stats + .map(_.collect { + // cbf.apply() + case ap @ Term.Apply(sel @ Term.Select(cbf2 @ matchCbf(_), apply), Nil) => + visitedCbfCalls += sel + replaceNewBuilder(ap, cbf2) + + // cbf.apply + case sel @ Term.Select(cbf2 @ matchCbf(_), ap) if (!visitedCbfCalls.contains(sel)) => + replaceNewBuilder(sel, cbf2) + + // cbf() + case ap @ Term.Apply(cbf2 @ matchCbf(_), Nil) => + replaceNewBuilder(ap, cbf2) + }.asPatch) + .asPatch // e.to[CC] => cbf.fromSpecific(e) - val toCalls = - body.collect { - case ap @ Term.ApplyType(Term.Select(e, to @ toTpe(_)), List(cc2 @ matchCC(_))) => - handledTo += to + val toCalls = toT match { + case cct @ Type.Apply(cc, _) => { + val matchCC = SymbolMatcher.exact(ctx.index.symbol(cc).get) + + stats + .map(_.collect { + case ap @ Term.ApplyType(Term.Select(e, to @ toTpe(_)), List(cc2 @ matchCC(_))) => + handledTo += to - // e.to[CC](*cbf*) extract implicit parameter - val synth = ctx.index.synthetics.find(_.position.end == ap.pos.end).get - val Term.Apply(_, List(implicitCbf)) = synth.text.parse[Term].get + // e.to[CC](*cbf*) extract implicit parameter + val synth = ctx.index.synthetics.find(_.position.end == ap.pos.end).get + val Term.Apply(_, List(implicitCbf)) = synth.text.parse[Term].get - // This is a bit unsafe - // https://github.com/scalameta/scalameta/issues/1636 - if (implicitCbf.syntax == param.syntax) { + // This is a bit unsafe + // https://github.com/scalameta/scalameta/issues/1636 + if (implicitCbf.syntax == param.syntax) { - // .to[CC] - val apToRemove = ap.tokens.slice(e.tokens.end - ap.tokens.start, ap.tokens.size) + // .to[CC] + val apToRemove = ap.tokens.slice(e.tokens.end - ap.tokens.start, ap.tokens.size) - ctx.removeTokens(apToRemove) + - ctx.addLeft(e, implicitCbf.syntax + ".fromSpecific(") + - ctx.addRight(e, ")") - } else Patch.empty + ctx.removeTokens(apToRemove) + + ctx.addLeft(e, implicitCbf.syntax + ".fromSpecific(") + + ctx.addRight(e, ")") + } else Patch.empty - }.asPatch + }.asPatch) + .asPatch + } + case _ => Patch.empty + } // implicit cbf: collection.generic.CanBuildFrom[Nothing, Int, CC[Int]] => // implicit cbf: Factory[Int, CC[Int]] val parameterType = ctx.replaceTree( tpe, - Type.Apply(Type.Name("Factory"), List(t, cct)).syntax + Type.Apply(Type.Name("Factory"), List(t, toT)).syntax ) parameterType + cbfCalls + toCalls diff --git a/scalafix/rules/src/main/scala/fix/Stable212Base.scala b/scalafix/rules/src/main/scala/fix/Stable212Base.scala index 69cefa18..9b2e522c 100644 --- a/scalafix/rules/src/main/scala/fix/Stable212Base.scala +++ b/scalafix/rules/src/main/scala/fix/Stable212Base.scala @@ -234,8 +234,23 @@ trait Stable212Base extends CrossCompatibility { self: SemanticRule => val useSites = ctx.tree.collect { case Defn.Def(_, _, _, paramss, _, body) => - CanBuildFromNothing(paramss, body, ctx, collectionCanBuildFrom, nothing, toTpe, handledTo) + - CanBuildFrom(paramss, body, ctx, collectionCanBuildFrom, nothing) + CanBuildFromNothing(paramss, + List(body), + ctx, + collectionCanBuildFrom, + nothing, + toTpe, + handledTo) + + CanBuildFrom(paramss, List(body), ctx, collectionCanBuildFrom, nothing) + case Defn.Class(_, _, _, Ctor.Primary(_, _, paramss), Template(_, _, _, stats)) => + CanBuildFromNothing(paramss, + stats, + ctx, + collectionCanBuildFrom, + nothing, + toTpe, + handledTo) + + CanBuildFrom(paramss, stats, ctx, collectionCanBuildFrom, nothing) }.asPatch val imports =