Skip to content

Commit 59daa38

Browse files
committed
feat: implicit conversions
1 parent 25b1eba commit 59daa38

File tree

4 files changed

+142
-1
lines changed

4 files changed

+142
-1
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
rule = ImplicitConversions
3+
*/
4+
5+
package fix
6+
7+
import scala.language.implicitConversions
8+
9+
// format: off
10+
object ImplicitConversionsTest {
11+
implicit def implicitDefWithFunctionArg1(x: Int): String = x.toString
12+
implicit def implicitDefWithFunctionArg2(x: (Int, Long)): String = x.toString
13+
14+
implicit val implicitValWithFunctionType1: Long => String = _.toString
15+
implicit val implicitValWithFunctionType2: (Int, Long) => String = (i, l ) => i.toString
16+
17+
def defWithImplicitFunctionType1(x: Int)(implicit conv: Int => String): String = x
18+
def defWithImplicitFunctionType2(x: Int)(implicit conv: (Int, Long) => String): String = x
19+
def defWithImplicitFunctionType3(x: Int)(using conv: (Int, Long) => String): String = x
20+
}
21+
// format: on
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package fix
2+
3+
import scala.language.implicitConversions
4+
5+
// format: off
6+
object ImplicitConversionsTest {
7+
implicit val implicitDefWithFunctionArg1: Conversion[Int, String] = (x: Int) => x.toString
8+
implicit val implicitDefWithFunctionArg2: Conversion[(Int, Long), String] = (x: (Int, Long)) => x.toString
9+
10+
implicit val implicitValWithFunctionType1: Conversion[Long, String] = _.toString
11+
implicit val implicitValWithFunctionType2: Conversion[(Int, Long), String] = (i, l ) => i.toString
12+
13+
def defWithImplicitFunctionType1(x: Int)(implicit conv: Conversion[Int, String]): String = x
14+
def defWithImplicitFunctionType2(x: Int)(implicit conv: Conversion[(Int, Long), String]): String = x
15+
def defWithImplicitFunctionType3(x: Int)(using conv: Conversion[(Int, Long), String]): String = x
16+
}
17+
// format: on

rules/src/main/resources/META-INF/services/scalafix.v1.Rule

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ fix.SemiAuto
22
fix.GivenAndUsing
33
fix.PackageObjectExport
44
fix.DropModThis
5-
fix.WildcardInitializer
5+
fix.WildcardInitializer
6+
fix.ImplicitConversions
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
/*
2+
* Copyright 2022 Arktekk
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package fix
18+
19+
import fix.ImplicitConversions._
20+
import scalafix.v1.{Patch, SemanticDocument, SemanticRule}
21+
22+
import scala.meta._
23+
import scala.meta.tokens.Token
24+
25+
class ImplicitConversions extends SemanticRule("ImplicitConversions") {
26+
27+
override def fix(implicit doc: SemanticDocument): Patch = {
28+
29+
doc.tree.collect {
30+
31+
case ImplicitDefWithFunctionArg(d, inType, outType) =>
32+
(for {
33+
pathDefToVal <- d.tokens.find(_.is[Token.KwDef]).map(Patch.replaceToken(_, "val"))
34+
eqSignToken <- d.tokens.find(_.is[Token.Equals])
35+
argTokens = d.tokens.dropWhile(_.isNot[Token.LeftParen]).dropRightWhile(_.isNot[Token.RightParen])
36+
} yield pathDefToVal +
37+
Patch.removeTokens(argTokens) +
38+
Patch.addRight(eqSignToken, s" ${argTokens.toString()} =>") +
39+
Patch.replaceTree(outType, conversionCode(inType, outType))).asPatch
40+
41+
case ImplicitValWithFunctionType(ts) =>
42+
Patch.replaceTree(ts, conversionCode(ts.params, ts.res))
43+
44+
case DefWithImplicitFunctionType(typeFunctions) =>
45+
typeFunctions.foldRight(Patch.empty) { case (tf, patches) =>
46+
patches + Patch.replaceTree(tf, conversionCode(tf.params, tf.res))
47+
}
48+
}.asPatch
49+
}
50+
51+
private def conversionCode(inType: Type, retType: Type): String =
52+
conversionCode(inType :: Nil, retType)
53+
54+
private def conversionCode(inType: List[Type], retType: Type): String = {
55+
val inStr = inType match {
56+
case one :: Nil => one.toString()
57+
case many => many.map(_.toString()).mkString("(", ", ", ")")
58+
}
59+
s"Conversion[$inStr, $retType]"
60+
}
61+
}
62+
63+
object ImplicitConversions {
64+
65+
object ImplicitValWithFunctionType {
66+
def unapply(tree: Tree): Option[Type.Function] =
67+
tree match {
68+
case Defn.Val(mods, _, Some(ts: Type.Function), _) if mods.exists(_.is[Mod.Implicit]) => Some(ts)
69+
case _ => None
70+
}
71+
}
72+
object ImplicitDefWithFunctionArg {
73+
def unapply(tree: Tree): Option[(Defn.Def, Type, Type)] =
74+
tree match {
75+
case d @ Defn.Def(mods, Term.Name(_), _, (firstParam :: Nil) :: Nil, Some(outType: Type.Name), _)
76+
if mods.exists(_.is[Mod.Implicit]) && firstParam.mods.forall(_.isNot[Mod.Implicit]) =>
77+
firstParam.decltpe.map(inType => (d, inType, outType))
78+
case _ => None
79+
}
80+
}
81+
82+
object DefWithImplicitFunctionType {
83+
def unapply(tree: Tree): Option[List[Type.Function]] = tree match {
84+
case Defn.Def(_, _, _, LastImplicitTypeFunctionParams(typeFunctions), _, _) => Some(typeFunctions)
85+
case _ => None
86+
}
87+
88+
object LastImplicitTypeFunctionParams {
89+
def unapply(params: List[List[Term.Param]]): Option[List[Type.Function]] = {
90+
val lastParams = params.lastOption
91+
if (lastParams.exists(_.exists(_.mods.exists(a => a.is[Mod.Implicit] || a.is[Mod.Using])))) {
92+
val tfs = lastParams.toList
93+
.flatMap(_.map(_.decltpe))
94+
.collect { case Some(tf: Type.Function) => tf }
95+
if (tfs.isEmpty) None
96+
else Some(tfs)
97+
} else None
98+
}
99+
}
100+
}
101+
102+
}

0 commit comments

Comments
 (0)