diff options
author | Paul Phillips <paulp@improving.org> | 2013-03-12 18:46:52 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2013-03-12 18:46:52 -0700 |
commit | 2f38bb8e5c69af0db63e05da78301dda916fad6f (patch) | |
tree | 6d2b2d4243b9bb2bb81c2e18eacd436d3026ec04 | |
parent | 4cb4852f9b30ad41959118b98b1a0b0486ac8123 (diff) | |
parent | 089cad8f436e1bc0935218937590897f5b9cbae4 (diff) | |
download | scala-2f38bb8e5c69af0db63e05da78301dda916fad6f.tar.gz scala-2f38bb8e5c69af0db63e05da78301dda916fad6f.tar.bz2 scala-2f38bb8e5c69af0db63e05da78301dda916fad6f.zip |
Merge pull request #2240 from paulp/pr/warn-forward-reference
Warn about locally identifiable init order issues.
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/Constructors.scala | 35 | ||||
-rw-r--r-- | test/files/neg/constructor-init-order.check | 9 | ||||
-rw-r--r-- | test/files/neg/constructor-init-order.flags | 1 | ||||
-rw-r--r-- | test/files/neg/constructor-init-order.scala | 23 |
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" +} |