Skip to content

Commit

Permalink
Add NoUnnecessaryForComprehension rule.
Browse files Browse the repository at this point in the history
  • Loading branch information
mrdziuban committed Sep 13, 2024
1 parent 6e63c4f commit 8b04aad
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
rule = NoUnnecessaryForComprehension
NoUnnecessaryForComprehension.color = false
*/
package fix

object NoUnnecessaryForComprehension {
val x = for {/* assert: NoUnnecessaryForComprehension
^
A for comprehension with only one statement can be simplified
Cases where the yield returns the result of the statement can just be the statement itself:
Before:
for {
x <- someStatementHere
} yield x
After:
someStatementHere
Cases where the yield performs an additional computation can be rewritten with map:
Before:
for {
x <- someStatementHere
} yield doSomethingElse(x)
After:
someStatementHere.map(doSomethingElse) */
i <- Option(1)
} yield i

val y = for {
i <- Option(1)
j <- Option(2)
} yield i + j
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package fix

object NoUnnecessaryForComprehension {
val x = for {
i <- Option(1)
} yield i

val y = for {
i <- Option(1)
j <- Option(2)
} yield i + j
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
fix.NoUnnecessaryCase
fix.NoUnnecessaryForComprehension
fix.StrictSubclassAccess
fix.NoWithForExtends
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package fix

import metaconfig.{ConfDecoder, Configured}
import metaconfig.generic.{deriveDecoder, deriveSurface, Surface}
import scalafix.v1._
import scala.io.AnsiColor
import scala.meta._

case class NoUnnecessaryForComprehensionConfig(color: Boolean)
object NoUnnecessaryForComprehensionConfig {
val default = NoUnnecessaryForComprehensionConfig(true)

implicit val surface: Surface[NoUnnecessaryForComprehensionConfig] = deriveSurface
implicit val decoder: ConfDecoder[NoUnnecessaryForComprehensionConfig] = deriveDecoder(default)
}

case class UnnecessaryForComprehensionLint(position: Position, color: Boolean) extends Diagnostic {
private def withColor(c: String): String => String = s => if (color) c ++ s ++ AnsiColor.RESET else s
private val magenta = withColor(AnsiColor.MAGENTA)
private val red = withColor(AnsiColor.RED)

override def message = s"""|
|A ${magenta("for")} comprehension with only one statement can be simplified
|
|Cases where the ${magenta("yield")} returns the result of the statement can just be the statement itself:
|
|Before:
|
| ${magenta("for")} {
| ${red("x")} <- someStatementHere
| } ${magenta("yield")} x
|
|After:
|
| someStatementHere
|
|Cases where the ${magenta("yield")} performs an additional computation can be rewritten with ${magenta("map")}:
|
|Before:
|
| ${magenta("for")} {
| ${red("x")} <- someStatementHere
| } ${magenta("yield")} doSomethingElse(x)
|
|After:
|
| someStatementHere.map(doSomethingElse)
|""".stripMargin
}

class NoUnnecessaryForComprehension(config: NoUnnecessaryForComprehensionConfig)
extends SyntacticRule("NoUnnecessaryForComprehension") {
def this() = this(NoUnnecessaryForComprehensionConfig.default)

override def withConfiguration(config: Configuration): Configured[Rule] =
config.conf.getOrElse("NoUnnecessaryForComprehension")(this.config).map(new NoUnnecessaryForComprehension(_))

override def fix(implicit doc: SyntacticDocument): Patch =
doc.tree.collect {
case t @ Term.ForYield(List(_), _) => Patch.lint(UnnecessaryForComprehensionLint(t.pos, config.color))
}.asPatch
}

0 comments on commit 8b04aad

Please sign in to comment.