Navigation Menu

Skip to content

Commit

Permalink
Warn on selection of vals from DelayedInit subclasses.
Browse files Browse the repository at this point in the history
Which are likely to yield null, if the program didn't start.

This is a common source of confusion for people new to
the language, as was seen during the Coursera course.

The test case shows that the usage pattern within Specs2
won't generate these warnings.
  • Loading branch information
retronym committed Apr 23, 2013
1 parent 5147bb2 commit d3aa9a7
Show file tree
Hide file tree
Showing 4 changed files with 64 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
Expand Up @@ -1396,6 +1396,16 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
} }
} }


private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = {
def isLikelyUninitialized = (
(sym.owner isSubClass DelayedInitClass)
&& !qual.tpe.isInstanceOf[ThisType]
&& sym.accessedOrSelf.isVal
)
if (settings.lint.value && isLikelyUninitialized)
unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value")
}

private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = ( private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = (
(otherSym != NoSymbol) (otherSym != NoSymbol)
&& !otherSym.isProtected && !otherSym.isProtected
Expand Down Expand Up @@ -1595,6 +1605,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
if(settings.Xmigration.value != NoScalaVersion) if(settings.Xmigration.value != NoScalaVersion)
checkMigration(sym, tree.pos) checkMigration(sym, tree.pos)
checkCompileTimeOnly(sym, tree.pos) checkCompileTimeOnly(sym, tree.pos)
checkDelayedInitSelect(qual, sym, tree.pos)


if (sym eq NoSymbol) { if (sym eq NoSymbol) {
unit.warning(tree.pos, "Select node has NoSymbol! " + tree + " / " + tree.tpe) unit.warning(tree.pos, "Select node has NoSymbol! " + tree + " / " + tree.tpe)
Expand Down
10 changes: 10 additions & 0 deletions test/files/neg/delayed-init-ref.check
@@ -0,0 +1,10 @@
delayed-init-ref.scala:17: error: Selecting value vall from object O, which extends scala.DelayedInit, is likely to yield an uninitialized value
println(O.vall) // warn
^
delayed-init-ref.scala:19: error: Selecting value vall from object O, which extends scala.DelayedInit, is likely to yield an uninitialized value
println(vall) // warn
^
delayed-init-ref.scala:40: error: Selecting value foo from trait UserContext, which extends scala.DelayedInit, is likely to yield an uninitialized value
println({locally(()); this}.foo) // warn (spurious, but we can't discriminate)
^
three errors found
1 change: 1 addition & 0 deletions test/files/neg/delayed-init-ref.flags
@@ -0,0 +1 @@
-Xlint -Xfatal-warnings
42 changes: 42 additions & 0 deletions test/files/neg/delayed-init-ref.scala
@@ -0,0 +1,42 @@
trait T {
val traitVal = ""
}

object O extends App with T {
val vall = ""
lazy val lazyy = ""
def deff = ""

println(vall) // no warn
new {
println(vall) // no warn
}
}

object Client {
println(O.vall) // warn
import O.vall
println(vall) // warn

println(O.lazyy) // no warn
println(O.deff) // no warn
println(O.traitVal) // no warn
}

// Delayed init usage pattern from Specs2
// See: https://groups.google.com/d/msg/scala-sips/wP6dL8nIAQs/ogjoPE-MSVAJ
trait Before extends DelayedInit {
def before()
override def delayedInit(x: => Unit): Unit = { before; x }
}
object Spec {
trait UserContext extends Before {
def before() = ()
val foo = "foo"
}
new UserContext {
println(foo) // no warn
println(this.foo) // no warn
println({locally(()); this}.foo) // warn (spurious, but we can't discriminate)
}
}

0 comments on commit d3aa9a7

Please sign in to comment.