forked from scalacenter/scalafix
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New ImplicitClassPrivateParams rule scalacenter#542
- Loading branch information
1 parent
5b64319
commit ea2ee6b
Showing
4 changed files
with
183 additions
and
0 deletions.
There are no files selected for viewing
51 changes: 51 additions & 0 deletions
51
scalafix-core/shared/src/main/scala/scalafix/internal/rule/ImplicitClassPrivateParams.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package scalafix.internal.rule | ||
|
||
import scala.meta.Term.Param | ||
import scala.meta.{Defn, Mod} | ||
import scalafix.rule.RuleCtx | ||
import scalafix.{Patch, Rule} | ||
|
||
case object ImplicitClassPrivateParams extends Rule("ImplicitClassPrivateParams") { | ||
|
||
override def description: String = | ||
"Prevent public access of val or var implicit class parameters by adding private modifier" | ||
|
||
override def fix(ctx: RuleCtx): Patch = { | ||
ctx.tree.collect { | ||
case Defn.Class(mods, _, _, primCtor, _) if hasImplicitMod(mods) && atLeastOneNonPrivateOrProtectedParam(primCtor.paramss) => | ||
|
||
val patches = for { | ||
paramList <- primCtor.paramss | ||
param <- paramList if isNonPrivateOrProtectedParam(param) | ||
anchorMod <- retrieveAnchorMod(param.mods) | ||
} yield ctx.addLeft(anchorMod, "private") | ||
patches.asPatch | ||
|
||
}.asPatch | ||
} | ||
|
||
private def retrieveAnchorMod(mods: List[Mod]): Option[Mod] = { | ||
mods match { | ||
case (_ :Mod.ValParam) | (_ :Mod.VarParam) :: _ => Some(mods.head) | ||
case (_ :Mod.Final) :: (_ :Mod.VarParam) | (_ :Mod.ValParam) :: _ => Some(mods(1)) | ||
case (_ :Mod.Annot) :: (_ :Mod.VarParam) | (_ :Mod.ValParam) :: _ => Some(mods(1)) | ||
case (_ :Mod.Annot) :: (_ :Mod.Final) :: (_ :Mod.ValParam) | (_ :Mod.VarParam) :: _ => Some(mods(1)) | ||
case _ => None | ||
} | ||
} | ||
|
||
private def isNonPrivateOrProtectedParam(param: Param): Boolean = | ||
!param.mods.exists { | ||
case (_: Mod.Private) | (_ : Mod.Protected) => true | ||
case _ => false | ||
} | ||
|
||
private def atLeastOneNonPrivateOrProtectedParam(params: List[List[Param]]): Boolean = | ||
params.flatten.exists(isNonPrivateOrProtectedParam) | ||
|
||
private def hasImplicitMod(mods: List[Mod]): Boolean = | ||
mods.exists { | ||
case _: Mod.Implicit => true | ||
case _ => false | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
scalafix-tests/input/src/main/scala/test/ImplicitClassPrivateParams.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
rule = PrivateImplicitClassParams | ||
*/ | ||
package test | ||
|
||
object ImplicitClassPrivateParams { | ||
implicit class ValueClassXtensionVal(val str: Int) extends AnyVal { | ||
def doubled: Int = str + str | ||
} | ||
|
||
implicit class ValueClassXtensionAnnotatedVal(@transient val str: String) extends AnyVal { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionVal(val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedVal(@transient val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedFinalVal(@transient final val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionVar(var str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedVar(@transient var str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionFinalAnnotatedFinalVar(@transient final var str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedProtectedVal(@transient protected val str: Int) { | ||
def doubled: Int = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedProtectedVar(@transient protected var str: Int) { | ||
def doubled: Int = str + str | ||
} | ||
|
||
implicit class Xtension(str: Int) { | ||
def doubled: Int = str + str | ||
} | ||
|
||
class BaseClass(val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionOverride(override val str: String) extends BaseClass(str) | ||
} |
53 changes: 53 additions & 0 deletions
53
scalafix-tests/output/src/main/scala/test/ImplicitClassPrivateParams.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package test | ||
|
||
object ImplicitClassPrivateParams { | ||
implicit class ValueClassXtensionVal(private val str: Int) extends AnyVal { | ||
def doubled: Int = str + str | ||
} | ||
|
||
implicit class ValueClassXtensionAnnotatedVal(@transient private val str: String) extends AnyVal { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionVal(private val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedVal(@transient private val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedFinalVal(@transient private final val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionVar(private var str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedVar(@transient private var str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionFinalAnnotatedFinalVar(@transient private final var str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedProtectedVal(@transient protected val str: Int) { | ||
def doubled: Int = str + str | ||
} | ||
|
||
implicit class XtensionAnnotatedProtectedVar(@transient protected var str: Int) { | ||
def doubled: Int = str + str | ||
} | ||
|
||
implicit class Xtension(str: Int) { | ||
def doubled: Int = str + str | ||
} | ||
|
||
class BaseClass(val str: String) { | ||
def doubled: String = str + str | ||
} | ||
|
||
implicit class XtensionOverride(override val str: String) extends BaseClass(str) | ||
} |
23 changes: 23 additions & 0 deletions
23
website/src/main/tut/docs/rules/ImplicitClassPrivateParams.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
--- | ||
layout: docs | ||
title: ImplicitClassPrivateParams | ||
--- | ||
|
||
# ImplicitClassPrivateParams | ||
|
||
`val` and `var` fields of an implicit class are accessible as an extension. | ||
This rule adds the `private` access modifier in such cases in order to prevent direct access. | ||
|
||
```scala | ||
// before | ||
implicit class XtensionVal(val str: String) { | ||
def doubled: String = str + str | ||
} | ||
"message".str // compiles | ||
|
||
// after | ||
implicit class XtensionValFixed(private val strFixed: String) { | ||
def doubled: String = strFixed + strFixed | ||
} | ||
"message".strFixed // does not compile | ||
``` |