Skip to content

Commit

Permalink
add DiscardEff, DiscardMonixTask, DiscardScalaFuture
Browse files Browse the repository at this point in the history
  • Loading branch information
xuwei-k committed Jan 11, 2024
1 parent 0018725 commit e61f07e
Show file tree
Hide file tree
Showing 7 changed files with 327 additions and 0 deletions.
2 changes: 2 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ lazy val inputOutputCommon = Def.settings(
}
},
libraryDependencies += "com.typesafe.slick" %% "slick" % "3.5.0-M5",
libraryDependencies += "io.monix" %% "monix-eval" % "3.4.1",
libraryDependencies += "org.mockito" % "mockito-subclass" % "5.8.0",
libraryDependencies += "org.atnos" %% "eff-core" % "7.0.1"
)

Expand Down
48 changes: 48 additions & 0 deletions input/src/main/scala-2/fix/DiscardEffTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
rule = DiscardEff
*/
package fix

import org.atnos.eff.Eff
import org.mockito.Mockito
import org.mockito.Mockito.verify

trait DiscardEffTest {
def f0[R]: Eff[R, Int]

def f1[R](n: Int): Eff[R, Int]

def mock: DiscardEffTest

def f2[R]: Eff[R, Int] = {
f0[R] // assert: DiscardEff

f1[R](2) // assert: DiscardEff

f1[R](3).map(_ + 4) // assert: DiscardEff

val x1 = f1[R](5)
def x2: Eff[R, Int] = f1[R](5)

Mockito.verify(mock).f1(3)
Mockito.verify(mock).f1[R](3)
Mockito.verify(mock, Mockito.times(2)).f1(9)
Mockito.verify(mock, Mockito.times(2)).f1[R](9)
Mockito.verify(mock, Mockito.never()).f1(-1)
Mockito.verify(mock, Mockito.never()).f1[R](-1)

verify(mock).f0[R]
verify(mock).f1(4)
verify(mock).f1[R](4)
verify(mock, Mockito.times(2)).f1(5)
verify(mock, Mockito.times(2)).f1[R](5)
verify(mock, Mockito.never()).f1(8)
verify(mock, Mockito.never()).f1[R](8)

x1.flatMap(y => x1.map(_ + y)) // assert: DiscardEff

x1 // assert: DiscardEff

x1
}
}
48 changes: 48 additions & 0 deletions input/src/main/scala-2/fix/DiscardMonixTaskTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
rule = DiscardMonixTask
*/
package fix

import monix.eval.Task
import org.mockito.Mockito
import org.mockito.Mockito.verify

trait DiscardMonixTaskTest {
def f0[R]: Task[Int]

def f1[R](n: Int): Task[Int]

def mock: DiscardMonixTaskTest

def f2[R]: Task[Int] = {
f0[R] // assert: DiscardMonixTask

f1[R](2) // assert: DiscardMonixTask

f1[R](3).map(_ + 4) // assert: DiscardMonixTask

val x1 = f1[R](5)
def x2: Task[Int] = f1[R](5)

Mockito.verify(mock).f1(3)
Mockito.verify(mock).f1[R](3)
Mockito.verify(mock, Mockito.times(2)).f1(9)
Mockito.verify(mock, Mockito.times(2)).f1[R](9)
Mockito.verify(mock, Mockito.never()).f1(-1)
Mockito.verify(mock, Mockito.never()).f1[R](-1)

verify(mock).f0[R]
verify(mock).f1(4)
verify(mock).f1[R](4)
verify(mock, Mockito.times(2)).f1(5)
verify(mock, Mockito.times(2)).f1[R](5)
verify(mock, Mockito.never()).f1(8)
verify(mock, Mockito.never()).f1[R](8)

x1.flatMap(y => x1.map(_ + y)) // assert: DiscardMonixTask

x1 // assert: DiscardMonixTask

x1
}
}
49 changes: 49 additions & 0 deletions input/src/main/scala-2/fix/DiscardScalaFutureTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
rule = DiscardScalaFuture
*/
package fix

import scala.concurrent.Future
import scala.concurrent.ExecutionContext
import org.mockito.Mockito
import org.mockito.Mockito.verify

trait DiscardScalaFutureTest {
def f0[R]: Future[Int]

def f1[R](n: Int): Future[Int]

def mock: DiscardScalaFutureTest

def f2[R](implicit ec: ExecutionContext): Future[Int] = {
f0[R] // assert: DiscardScalaFuture

f1[R](2) // assert: DiscardScalaFuture

f1[R](3).map(_ + 4) // assert: DiscardScalaFuture

val x1 = f1[R](5)
def x2: Future[Int] = f1[R](5)

Mockito.verify(mock).f1(3)
Mockito.verify(mock).f1[R](3)
Mockito.verify(mock, Mockito.times(2)).f1(9)
Mockito.verify(mock, Mockito.times(2)).f1[R](9)
Mockito.verify(mock, Mockito.never()).f1(-1)
Mockito.verify(mock, Mockito.never()).f1[R](-1)

verify(mock).f0[R]
verify(mock).f1(4)
verify(mock).f1[R](4)
verify(mock, Mockito.times(2)).f1(5)
verify(mock, Mockito.times(2)).f1[R](5)
verify(mock, Mockito.never()).f1(8)
verify(mock, Mockito.never()).f1[R](8)

x1.flatMap(y => x1.map(_ + y)) // assert: DiscardScalaFuture

x1 // assert: DiscardScalaFuture

x1
}
}
60 changes: 60 additions & 0 deletions rules/src/main/scala/fix/DiscardEff.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package fix

import scala.meta.Defn
import scala.meta.Term
import scala.meta.contrib.XtensionTreeOps
import scalafix.Patch
import scalafix.lint.Diagnostic
import scalafix.lint.LintSeverity
import scalafix.v1.MethodSignature
import scalafix.v1.SemanticDocument
import scalafix.v1.SemanticRule
import scalafix.v1.TypeRef
import scalafix.v1.ValueSignature
import scalafix.v1.XtensionTreeScalafix

class DiscardEff extends SemanticRule("DiscardEff") {
override def fix(implicit doc: SemanticDocument): Patch = {
doc.tree.collect { case Term.Block(values :+ _) => // ignore last
values.filter {
case _: Defn.Val =>
false
case _: Defn.Def =>
false
case x =>
x.collectFirst {
case Term.Apply.After_4_6_0(Term.Select(Term.Name("Mockito"), _), _) =>
()
case Term.Apply.After_4_6_0(Term.Name("verify"), _) =>
()
}.isEmpty
}.flatMap(x => x.symbol.info.map(x -> _))
.map { case (x, info) =>
PartialFunction
.condOpt(info.signature) {
case m: MethodSignature =>
m.returnType
case v: ValueSignature =>
v.tpe
}
.map { tpe =>
PartialFunction
.condOpt(tpe) {
case ref: TypeRef if ref.symbol.value == "org/atnos/eff/Eff#" =>
Patch.lint(
Diagnostic(
id = "",
message = "discarded Eff",
position = x.pos,
severity = LintSeverity.Warning
)
)
}
.asPatch
}
.asPatch
}
.asPatch
}.asPatch
}
}
60 changes: 60 additions & 0 deletions rules/src/main/scala/fix/DiscardMonixTask.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package fix

import scala.meta.Defn
import scala.meta.Term
import scala.meta.contrib.XtensionTreeOps
import scalafix.Patch
import scalafix.lint.Diagnostic
import scalafix.lint.LintSeverity
import scalafix.v1.MethodSignature
import scalafix.v1.SemanticDocument
import scalafix.v1.SemanticRule
import scalafix.v1.TypeRef
import scalafix.v1.ValueSignature
import scalafix.v1.XtensionTreeScalafix

class DiscardMonixTask extends SemanticRule("DiscardMonixTask") {
override def fix(implicit doc: SemanticDocument): Patch = {
doc.tree.collect { case Term.Block(values :+ _) => // ignore last
values.filter {
case _: Defn.Val =>
false
case _: Defn.Def =>
false
case x =>
x.collectFirst {
case Term.Apply.After_4_6_0(Term.Select(Term.Name("Mockito"), _), _) =>
()
case Term.Apply.After_4_6_0(Term.Name("verify"), _) =>
()
}.isEmpty
}.flatMap(x => x.symbol.info.map(x -> _))
.map { case (x, info) =>
PartialFunction
.condOpt(info.signature) {
case m: MethodSignature =>
m.returnType
case v: ValueSignature =>
v.tpe
}
.map { tpe =>
PartialFunction
.condOpt(tpe) {
case ref: TypeRef if ref.symbol.value == "monix/eval/Task#" =>
Patch.lint(
Diagnostic(
id = "",
message = "discarded monix.eval.Task",
position = x.pos,
severity = LintSeverity.Warning
)
)
}
.asPatch
}
.asPatch
}
.asPatch
}.asPatch
}
}
60 changes: 60 additions & 0 deletions rules/src/main/scala/fix/DiscardScalaFuture.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package fix

import scala.meta.Defn
import scala.meta.Term
import scala.meta.contrib.XtensionTreeOps
import scalafix.Patch
import scalafix.lint.Diagnostic
import scalafix.lint.LintSeverity
import scalafix.v1.MethodSignature
import scalafix.v1.SemanticDocument
import scalafix.v1.SemanticRule
import scalafix.v1.TypeRef
import scalafix.v1.ValueSignature
import scalafix.v1.XtensionTreeScalafix

class DiscardScalaFuture extends SemanticRule("DiscardScalaFuture") {
override def fix(implicit doc: SemanticDocument): Patch = {
doc.tree.collect { case Term.Block(values :+ _) => // ignore last
values.filter {
case _: Defn.Val =>
false
case _: Defn.Def =>
false
case x =>
x.collectFirst {
case Term.Apply.After_4_6_0(Term.Select(Term.Name("Mockito"), _), _) =>
()
case Term.Apply.After_4_6_0(Term.Name("verify"), _) =>
()
}.isEmpty
}.flatMap(x => x.symbol.info.map(x -> _))
.map { case (x, info) =>
PartialFunction
.condOpt(info.signature) {
case m: MethodSignature =>
m.returnType
case v: ValueSignature =>
v.tpe
}
.map { tpe =>
PartialFunction
.condOpt(tpe) {
case ref: TypeRef if ref.symbol.value == "scala/concurrent/Future#" =>
Patch.lint(
Diagnostic(
id = "",
message = "discarded Future",
position = x.pos,
severity = LintSeverity.Error
)
)
}
.asPatch
}
.asPatch
}
.asPatch
}.asPatch
}
}

0 comments on commit e61f07e

Please sign in to comment.