Skip to content

Commit

Permalink
New ImplicitClassPrivateParams rule scalacenter#542
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonardMeyer committed Jan 7, 2018
1 parent 5b64319 commit ea2ee6b
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 0 deletions.
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
}
}
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)
}
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 website/src/main/tut/docs/rules/ImplicitClassPrivateParams.md
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
```

0 comments on commit ea2ee6b

Please sign in to comment.