Skip to content

Commit d8f04ff

Browse files
committed
add ThrownExceptionNotInMonixScope rule to the Analyzer Plugin
1 parent dc34354 commit d8f04ff

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed

analyzer/src/main/scala/com/avsystem/commons/analyzer/AnalyzerPlugin.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ final class AnalyzerPlugin(val global: Global) extends Plugin { plugin =>
5656
new Any2StringAdd(global),
5757
new ThrowableObjects(global),
5858
new DiscardedMonixTask(global),
59+
new ThrownExceptionNotInMonixScope(global),
5960
new BadSingletonComponent(global),
6061
new ConstantDeclarations(global),
6162
new BasePackage(global),
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.avsystem.commons
2+
package analyzer
3+
4+
import scala.tools.nsc.Global
5+
6+
final class ThrownExceptionNotInMonixScope(g: Global) extends AnalyzerRule(g, "thrownExceptionNotInMonixScope") {
7+
8+
import global.*
9+
10+
lazy val monixTaskTpe: Type = classType("monix.eval.Task") match {
11+
case NoType => NoType
12+
case tpe => TypeRef(NoPrefix, tpe.typeSymbol, List(definitions.AnyTpe))
13+
}
14+
private def checkDiscardedNothing(tree: Tree, discarded: Boolean): Unit = tree match {
15+
case tree if !discarded && tree.tpe != null && tree.tpe =:= definitions.NothingTpe =>
16+
checkDiscardedNothing(tree, discarded = true)
17+
18+
case Block(stats: List[Tree], expr: Tree) =>
19+
stats.foreach(checkDiscardedNothing(_, discarded = true))
20+
checkDiscardedNothing(expr, discarded)
21+
22+
case Template(parents: List[Tree], self: ValDef, body: List[Tree]) =>
23+
parents.foreach(checkDiscardedNothing(_, discarded = false))
24+
checkDiscardedNothing(self, discarded = false)
25+
body.foreach(checkDiscardedNothing(_, discarded = true))
26+
27+
case If(_: Tree, thenp: Tree, elsep: Tree) =>
28+
checkDiscardedNothing(thenp, discarded)
29+
checkDiscardedNothing(elsep, discarded)
30+
31+
case LabelDef(_: TermName, _: List[Ident], rhs: Tree) =>
32+
checkDiscardedNothing(rhs, discarded = true)
33+
34+
case Try(block: Tree, catches: List[CaseDef], finalizer: Tree) =>
35+
checkDiscardedNothing(block, discarded)
36+
catches.foreach(checkDiscardedNothing(_, discarded))
37+
checkDiscardedNothing(finalizer, discarded = true)
38+
39+
case CaseDef(_: Tree, _: Tree, body: Tree) =>
40+
checkDiscardedNothing(body, discarded)
41+
42+
case Match(_: Tree, cases: List[CaseDef]) =>
43+
cases.foreach(checkDiscardedNothing(_, discarded))
44+
45+
case Annotated(_: Tree, arg: Tree) =>
46+
checkDiscardedNothing(arg, discarded)
47+
48+
case Typed(expr: Tree, _: Tree) =>
49+
checkDiscardedNothing(expr, discarded)
50+
51+
case Apply(TypeApply(Select(prefix: Tree, TermName("map")), List(_)), List(Throw(_))) if prefix.tpe <:< monixTaskTpe =>
52+
report(tree.pos, "exception thrown not in Monix Task scope ")
53+
54+
case tree =>
55+
tree.children.foreach(checkDiscardedNothing(_, discarded = false))
56+
}
57+
58+
def analyze(unit: CompilationUnit): Unit = checkDiscardedNothing(unit.body, discarded = false)
59+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package com.avsystem.commons
2+
package analyzer
3+
4+
import org.scalatest.funsuite.AnyFunSuite
5+
6+
final class ThrownExceptionNotInMonixScopeTest extends AnyFunSuite with AnalyzerTest {
7+
settings.pluginOptions.value ++= List("AVSystemAnalyzer:-discardedMonixTask")
8+
9+
test("simple") {
10+
assertErrors(10,
11+
//language=Scala
12+
"""
13+
|import monix.eval.Task
14+
|
15+
|object whatever {
16+
| def task: Task[String] = ???
17+
| def ex: Exception = ???
18+
|
19+
| // errors from these
20+
| task.map(throw ex)
21+
|
22+
| {
23+
| println(""); task.map(throw ex)
24+
| }
25+
|
26+
| if (true) task.map(throw ex) else task.map(throw ex)
27+
|
28+
| try task.map(throw ex) catch {
29+
| case _: Exception => task.map(throw ex)
30+
| } finally task.map(throw ex)
31+
|
32+
| Seq(1, 2, 3).foreach(_ => task.map(throw ex))
33+
|
34+
| while (true) task.map(throw ex)
35+
|
36+
| do task.map(throw ex) while (true)
37+
|
38+
| // no errors from these
39+
| task.map(_ => throw ex)
40+
|
41+
| {
42+
| println(""); task.map(_ => throw ex)
43+
| }
44+
|
45+
| if (true) task.map(_ => throw ex) else task.map(_ => throw ex)
46+
|
47+
| try task.map(_ => throw ex) catch {
48+
| case _: Exception => task.map(_ => throw ex)
49+
| } finally task.map(_ => throw ex)
50+
|
51+
| Seq(1, 2, 3).foreach(_ => task.map(_ => throw ex))
52+
|
53+
| while (true) task.map(_ => throw ex)
54+
|
55+
| do task.map(_ => throw ex) while (true)
56+
|}
57+
|
58+
|""".stripMargin
59+
)
60+
}
61+
}

0 commit comments

Comments
 (0)