Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ThrownExceptionNotInFunction rule to the Analyzer Plugin #585

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ final class AnalyzerPlugin(val global: Global) extends Plugin { plugin =>
new Any2StringAdd(global),
new ThrowableObjects(global),
new DiscardedMonixTask(global),
new ThrownExceptionNotInMonixScope(global),
new BadSingletonComponent(global),
new ConstantDeclarations(global),
new BasePackage(global),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.avsystem.commons
package analyzer

import scala.tools.nsc.Global

final class ThrownExceptionNotInMonixScope(g: Global) extends AnalyzerRule(g, "thrownExceptionNotInMonixScope") {

import global.*

lazy val monixTaskTpe: Type = classType("monix.eval.Task") match {
case NoType => NoType
case tpe => TypeRef(NoPrefix, tpe.typeSymbol, List(definitions.AnyTpe))
}
private def checkDiscardedNothing(tree: Tree, discarded: Boolean): Unit = tree match {
case tree if !discarded && tree.tpe != null && tree.tpe =:= definitions.NothingTpe =>
checkDiscardedNothing(tree, discarded = true)

case Block(stats: List[Tree], expr: Tree) =>
stats.foreach(checkDiscardedNothing(_, discarded = true))
checkDiscardedNothing(expr, discarded)

case Template(parents: List[Tree], self: ValDef, body: List[Tree]) =>
parents.foreach(checkDiscardedNothing(_, discarded = false))
checkDiscardedNothing(self, discarded = false)
body.foreach(checkDiscardedNothing(_, discarded = true))

case If(_: Tree, thenp: Tree, elsep: Tree) =>
checkDiscardedNothing(thenp, discarded)
checkDiscardedNothing(elsep, discarded)

case LabelDef(_: TermName, _: List[Ident], rhs: Tree) =>
checkDiscardedNothing(rhs, discarded = true)

case Try(block: Tree, catches: List[CaseDef], finalizer: Tree) =>
checkDiscardedNothing(block, discarded)
catches.foreach(checkDiscardedNothing(_, discarded))
checkDiscardedNothing(finalizer, discarded = true)

case CaseDef(_: Tree, _: Tree, body: Tree) =>
checkDiscardedNothing(body, discarded)

case Match(_: Tree, cases: List[CaseDef]) =>
cases.foreach(checkDiscardedNothing(_, discarded))

case Annotated(_: Tree, arg: Tree) =>
checkDiscardedNothing(arg, discarded)

case Typed(expr: Tree, _: Tree) =>
checkDiscardedNothing(expr, discarded)

case Apply(TypeApply(Select(prefix: Tree, TermName("map")), List(_)), List(Throw(_))) if prefix.tpe <:< monixTaskTpe =>
halotukozak marked this conversation as resolved.
Show resolved Hide resolved
report(tree.pos, "exception thrown not in Monix Task scope ")

case tree =>
tree.children.foreach(checkDiscardedNothing(_, discarded = false))
}

def analyze(unit: CompilationUnit): Unit = checkDiscardedNothing(unit.body, discarded = false)
}
halotukozak marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.avsystem.commons
package analyzer

import org.scalatest.funsuite.AnyFunSuite

final class ThrownExceptionNotInMonixScopeTest extends AnyFunSuite with AnalyzerTest {
settings.pluginOptions.value ++= List("AVSystemAnalyzer:-discardedMonixTask")

test("simple") {
assertErrors(10,
//language=Scala
"""
|import monix.eval.Task
|
|object whatever {
| def task: Task[String] = ???
| def ex: Exception = ???
|
| // errors from these
| task.map(throw ex)
|
| {
| println(""); task.map(throw ex)
| }
|
| if (true) task.map(throw ex) else task.map(throw ex)
|
| try task.map(throw ex) catch {
| case _: Exception => task.map(throw ex)
| } finally task.map(throw ex)
|
| Seq(1, 2, 3).foreach(_ => task.map(throw ex))
|
| while (true) task.map(throw ex)
|
| do task.map(throw ex) while (true)
|
| // no errors from these
| task.map(_ => throw ex)
|
| {
| println(""); task.map(_ => throw ex)
| }
|
| if (true) task.map(_ => throw ex) else task.map(_ => throw ex)
|
| try task.map(_ => throw ex) catch {
| case _: Exception => task.map(_ => throw ex)
| } finally task.map(_ => throw ex)
|
| Seq(1, 2, 3).foreach(_ => task.map(_ => throw ex))
|
| while (true) task.map(_ => throw ex)
|
| do task.map(_ => throw ex) while (true)
|}
|
|""".stripMargin
)
}
}
Loading