summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2013-03-12 07:39:54 -0700
committerPaul Phillips <paulp@improving.org>2013-03-12 09:31:25 -0700
commit089cad8f436e1bc0935218937590897f5b9cbae4 (patch)
tree1d43901950f88f918a9d76b17f1c64ec2d49c677
parenta41c79bb5b2f16d8f37e253737f67171e5764bb9 (diff)
downloadscala-089cad8f436e1bc0935218937590897f5b9cbae4.tar.gz
scala-089cad8f436e1bc0935218937590897f5b9cbae4.tar.bz2
scala-089cad8f436e1bc0935218937590897f5b9cbae4.zip
Warn about locally identifiable init order issues.
This warns about a subset of initialization order issues which can easily be identified by inspection, such as val x = y val y = 5 The likelihood of this formulation being intentional is miniscule.
-rw-r--r--src/compiler/scala/tools/nsc/transform/Constructors.scala35
-rw-r--r--test/files/neg/constructor-init-order.check9
-rw-r--r--test/files/neg/constructor-init-order.flags1
-rw-r--r--test/files/neg/constructor-init-order.scala23
4 files changed, 68 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/Constructors.scala b/src/compiler/scala/tools/nsc/transform/Constructors.scala
index a4a6c3ff31..886c790ec0 100644
--- a/src/compiler/scala/tools/nsc/transform/Constructors.scala
+++ b/src/compiler/scala/tools/nsc/transform/Constructors.scala
@@ -34,6 +34,41 @@ abstract class Constructors extends Transform with ast.TreeDSL {
val stats = impl.body // the transformed template body
val localTyper = typer.atOwner(impl, clazz)
+ // Inspect for obvious out-of-order initialization; concrete, eager vals or vars,
+ // declared in this class, for which a reference to the member precedes its definition.
+ def checkableForInit(sym: Symbol) = (
+ (sym ne null)
+ && (sym.isVal || sym.isVar)
+ && !(sym hasFlag LAZY | DEFERRED | SYNTHETIC)
+ )
+ val uninitializedVals = mutable.Set[Symbol](
+ stats collect { case vd: ValDef if checkableForInit(vd.symbol) => vd.symbol.accessedOrSelf }: _*
+ )
+ if (uninitializedVals.nonEmpty)
+ log("Checking constructor for init order issues among: " + uninitializedVals.map(_.name).mkString(", "))
+
+ for (stat <- stats) {
+ // Checking the qualifier symbol is necessary to prevent a selection on
+ // another instance of the same class from potentially appearing to be a forward
+ // reference on the member in the current class.
+ def check(tree: Tree) = {
+ for (t <- tree) t match {
+ case t: RefTree if uninitializedVals(t.symbol.accessedOrSelf) && t.qualifier.symbol == clazz =>
+ unit.warning(t.pos, s"Reference to uninitialized ${t.symbol.accessedOrSelf}")
+ case _ =>
+ }
+ }
+ stat match {
+ case vd: ValDef =>
+ // doing this first allows self-referential vals, which to be a conservative
+ // warner we will do because it's possible though difficult for it to be useful.
+ uninitializedVals -= vd.symbol.accessedOrSelf
+ if (!vd.symbol.isLazy)
+ check(vd.rhs)
+ case _: MemberDef => // skip other member defs
+ case t => check(t) // constructor body statement
+ }
+ }
val specializedFlag: Symbol = clazz.info.decl(nme.SPECIALIZED_INSTANCE)
val shouldGuard = (specializedFlag != NoSymbol) && !clazz.hasFlag(SPECIALIZED)
diff --git a/test/files/neg/constructor-init-order.check b/test/files/neg/constructor-init-order.check
new file mode 100644
index 0000000000..9ab6ac5923
--- /dev/null
+++ b/test/files/neg/constructor-init-order.check
@@ -0,0 +1,9 @@
+constructor-init-order.scala:7: warning: Reference to uninitialized value baz
+ val bar1 = baz // warn
+ ^
+constructor-init-order.scala:17: warning: Reference to uninitialized variable baz
+ var bar1 = baz // warn
+ ^
+error: No warnings can be incurred under -Xfatal-warnings.
+two warnings found
+one error found
diff --git a/test/files/neg/constructor-init-order.flags b/test/files/neg/constructor-init-order.flags
new file mode 100644
index 0000000000..85d8eb2ba2
--- /dev/null
+++ b/test/files/neg/constructor-init-order.flags
@@ -0,0 +1 @@
+-Xfatal-warnings
diff --git a/test/files/neg/constructor-init-order.scala b/test/files/neg/constructor-init-order.scala
new file mode 100644
index 0000000000..fe8fec87ad
--- /dev/null
+++ b/test/files/neg/constructor-init-order.scala
@@ -0,0 +1,23 @@
+trait Foo0 {
+ val quux1: String
+ val quux2 = quux1 // warning here is "future work"
+}
+
+class Foo1 extends Foo0 {
+ val bar1 = baz // warn
+ val bar2 = lazybaz // no warn
+ val bar3 = defbaz // no warn
+ val baz = "oops"
+ lazy val lazybaz = "ok"
+ def defbaz = "ok"
+ val quux1 = "oops"
+}
+
+class Foo2 {
+ var bar1 = baz // warn
+ var bar2 = lazybaz // no warn
+ var bar3 = defbaz // no warn
+ var baz = "oops"
+ lazy val lazybaz = "ok"
+ def defbaz = "ok"
+}