Description
reproduction steps
Scala 2.13.3 or 2.12.12 with fatal warnings.
The following code emulates build.sbt
, but I'm seeing it in sbt code really sbt/sbt#5743, and I'm afraid others will run into this if we upgrade to Scala 2.12.12.
object Test extends AnyRef with App {
lazy val task1 = new AnyRef {
val value: Int = {
println("flip pancake")
0
}
}
lazy val task2 = new AnyRef {
val value: Int = {
println("flop pancake")
0
}
}
def use() =
try {
task1.value
task2.value
()
} catch {
case _: Throwable => ()
}
use()
}
problem
predef-unit.scala:20: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
task1.value
^
predef-unit.scala:21: warning: a pure expression does nothing in statement position; multiline expressions might require enclosing parentheses
task2.value
^
error: No warnings can be incurred under -Werror.
2 warnings
1 error
expectations
task1.value
may look pure-looking from the compiler's point of view, but it's actually a macro, and it's not.
The reproduction above exploits a bug that @som-snytt pointed out that it's not checking full path for laziness, but I'm not sure if fixing that alone would fix the situation with sbt.
Maybe it would be better to put this warning behind a flag as an opt-in or turn it into Scalafix rule instead?
notes
In the recent releases of Scala, "a pure expression does nothing in statement position" has become more enthusiastic/accurate in its reach. Maybe because it moved to RefCheck in scala/scala#8995?
This seems to be a common occurrence since there are multiple PRs related to this:
- Introduce Unit.void[A](x: A): Unit scala#7530
- Allow suppressing value-discard warning via type ascription to
Unit
scala#7563 - Predef.unit to workaround "a pure expression does nothing" scala#9164
People have suggested various creative solutions in reply to https://twitter.com/eed3si9n/status/1293272725415505921.
(compile.value, ())._2
locally { val _ = compile.value }
class UniversalOps[A](private val a: A) extends AnyVal {
/**
* Explicit syntax to discard the value of a side-effecting expression.
* Useful when `-Ywarn-value-discard` compiler option is enabled.
*/
@silent
def discard: Unit = ()
}