summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-12-15 08:11:41 +0000
committerPaul Phillips <paulp@improving.org>2010-12-15 08:11:41 +0000
commit9f08c98a6e801d4798cb5c3794cab23deb6d9eec (patch)
tree239d2bde0c2c8b6bf9331387b8549e4be060a6a0
parent69aa78bd1bc593f878e44b2abde61dbb56391204 (diff)
downloadscala-9f08c98a6e801d4798cb5c3794cab23deb6d9eec.tar.gz
scala-9f08c98a6e801d4798cb5c3794cab23deb6d9eec.tar.bz2
scala-9f08c98a6e801d4798cb5c3794cab23deb6d9eec.zip
Stops barking up the wrong tree with -Ywarn-dea...
Stops barking up the wrong tree with -Ywarn-dead-code. The origin of its issues was twofold: 1) synchronized acts by-name without being by-name (ticket #4086) 2) warnings are swallowed if context.reportGeneralErrors is false Those two plus a dash of bitrot. In any case it's at its all time happiest now. It found all the dead code related fixes in this commit. Way to go, -Ywarn-dead-code! Review by odersky.
-rw-r--r--src/compiler/scala/tools/ant/ScalaTool.scala1
-rw-r--r--src/compiler/scala/tools/ant/Scalac.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala5
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala1
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala32
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala16
-rw-r--r--src/library/scala/io/BytePickle.scala4
-rw-r--r--src/library/scala/mobile/Code.scala1
-rw-r--r--src/library/scala/xml/dtd/Scanner.scala2
-rw-r--r--test/files/neg/check-dead.check16
-rw-r--r--test/files/neg/check-dead.scala55
12 files changed, 90 insertions, 50 deletions
diff --git a/src/compiler/scala/tools/ant/ScalaTool.scala b/src/compiler/scala/tools/ant/ScalaTool.scala
index e9bc4435e1..5ac0c90a76 100644
--- a/src/compiler/scala/tools/ant/ScalaTool.scala
+++ b/src/compiler/scala/tools/ant/ScalaTool.scala
@@ -100,7 +100,6 @@ class ScalaTool extends ScalaMatchingTask {
(if (input != "") List(st) else Nil)
else {
buildError("Platform " + st + " does not exist.")
- Nil
}
}
}
diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala
index 6c56ace9fe..fcf293fb2a 100644
--- a/src/compiler/scala/tools/ant/Scalac.scala
+++ b/src/compiler/scala/tools/ant/Scalac.scala
@@ -329,7 +329,6 @@ class Scalac extends ScalaMatchingTask with ScalacShared {
(if (input != "") List(st) else Nil)
else {
buildError("Phase " + st + " in log does not exist.")
- Nil
}
}
}
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index ee0a0c4d81..5f9696c71d 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -382,8 +382,9 @@ trait Definitions extends reflect.generic.StandardDefinitions {
def isSeqType(tp: Type) = cond(tp.normalize) { case TypeRef(_, SeqClass, List(tparam)) => true }
- def seqType(arg: Type) = typeRef(SeqClass.typeConstructor.prefix, SeqClass, List(arg))
- def arrayType(arg: Type) = typeRef(ArrayClass.typeConstructor.prefix, ArrayClass, List(arg))
+ def seqType(arg: Type) = typeRef(SeqClass.typeConstructor.prefix, SeqClass, List(arg))
+ def arrayType(arg: Type) = typeRef(ArrayClass.typeConstructor.prefix, ArrayClass, List(arg))
+ def byNameType(arg: Type) = appliedType(ByNameParamClass.typeConstructor, List(arg))
def ClassType(arg: Type) =
if (phase.erasedTypes || forMSIL) ClassClass.tpe
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 8de11943cf..c695f70871 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -351,7 +351,6 @@ self: Analyzer =>
case Some(pending) =>
// println("Pending implicit "+pending+" dominates "+pt+"/"+undetParams) //@MDEBUG
throw DivergentImplicit
- SearchFailure
case None =>
try {
context.openImplicits = (pt, tree.symbol) :: context.openImplicits
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index 3ffe25c61c..26fb5148bf 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1479,12 +1479,6 @@ trait Infer {
protected def includeCondition(sym: Symbol): Boolean = true
}
- def checkDead(tree: Tree): Tree = {
- if (settings.Ywarndeadcode.value && tree.tpe != null && tree.tpe.typeSymbol == NothingClass)
- context.warning (tree.pos, "dead code following this construct")
- tree
- }
-
/* -- Overload Resolution ---------------------------------------------- */
/*
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index 22a60a8abf..3de2f38d5d 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -332,6 +332,38 @@ trait TypeDiagnostics {
private def contextError(pos: Position, msg: String) = context.error(pos, msg)
private def contextError(pos: Position, err: Throwable) = context.error(pos, err)
+ object checkDead {
+ private var expr: Symbol = NoSymbol
+ private def exprOK = expr != Object_synchronized
+ private def treeOK(tree: Tree) = tree.tpe != null && tree.tpe.typeSymbol == NothingClass
+
+ def updateExpr(fn: Tree) = {
+ if (fn.symbol != null && fn.symbol.isMethod && !fn.symbol.isConstructor)
+ checkDead.expr = fn.symbol
+ }
+ def apply(tree: Tree): Tree = {
+ // Error suppression will squash some of these warnings unless we circumvent it.
+ // It is presumed if you are using a -Y option you would really like to hear
+ // the warnings you've requested.
+ if (settings.Ywarndeadcode.value && context.unit != null && treeOK(tree) && exprOK) {
+ val saved = context.reportGeneralErrors
+ try {
+ context.reportGeneralErrors = true
+ context.warning(tree.pos, "dead code following this construct")
+ }
+ finally context.reportGeneralErrors = saved
+ }
+ tree
+ }
+
+ // The checkDead call from typedArg is more selective.
+ def inMode(mode: Int, tree: Tree): Tree = {
+ val modeOK = (mode & (EXPRmode | BYVALmode | POLYmode)) == (EXPRmode | BYVALmode)
+ if (modeOK) apply(tree)
+ else tree
+ }
+ }
+
def symWasOverloaded(sym: Symbol) = sym.owner.isClass && sym.owner.info.member(sym.name).isOverloaded
def cyclicAdjective(sym: Symbol) = if (symWasOverloaded(sym)) "overloaded" else "recursive"
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index 0f87d6ba33..c20732b2bc 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -1791,7 +1791,8 @@ trait Typers { self: Analyzer =>
if (meth.isPrimaryConstructor && meth.isClassConstructor &&
phase.id <= currentRun.typerPhase.id && !reporter.hasErrors)
computeParamAliases(meth.owner, vparamss1, rhs1)
- if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass) rhs1 = checkDead(rhs1)
+ if (tpt1.tpe.typeSymbol != NothingClass && !context.returnsSeen && rhs1.tpe.typeSymbol != NothingClass)
+ rhs1 = checkDead(rhs1)
if (phase.id <= currentRun.typerPhase.id && meth.owner.isClass &&
meth.paramss.exists(ps => ps.exists(_.hasDefaultFlag) && isRepeatedParamType(ps.last.tpe)))
@@ -2206,9 +2207,9 @@ trait Typers { self: Analyzer =>
}
def typedArg(arg: Tree, mode: Int, newmode: Int, pt: Type): Tree = {
- val t = constrTyperIf((mode & SCCmode) != 0).typed(arg, mode & stickyModes | newmode, pt)
- if ((newmode & BYVALmode) != 0) t
- else checkDead(t)
+ val typedMode = mode & stickyModes | newmode
+ val t = constrTyperIf((mode & SCCmode) != 0).typed(arg, typedMode, pt)
+ checkDead.inMode(typedMode, t)
}
def typedArgs(args: List[Tree], mode: Int) =
@@ -2301,6 +2302,7 @@ trait Typers { self: Analyzer =>
if (sym != NoSymbol)
fun = adapt(fun setSymbol sym setType pre.memberType(sym), funMode(mode), WildcardType)
}
+
fun.tpe match {
case OverloadedType(pre, alts) =>
val undetparams = context.extractUndetparams()
@@ -2427,6 +2429,12 @@ trait Typers { self: Analyzer =>
} else {
val tparams = context.extractUndetparams()
if (tparams.isEmpty) { // all type params are defined
+ // In order for checkDead not to be misled by the unfortunate special
+ // case of AnyRef#synchronized (which is implemented with signature T => T
+ // but behaves as if it were (=> T) => T) we need to know what is the actual
+ // target of a call. Since this information is no longer available from
+ // typedArg, it is recorded here.
+ checkDead.updateExpr(fun)
val args1 = typedArgs(args, argMode(fun, mode), paramTypes, formals)
// instantiate dependent method types, must preserve singleton types where possible (stableTypeFor) -- example use case:
// val foo = "foo"; def precise(x: String)(y: x.type): x.type = {...}; val bar : foo.type = precise(foo)(foo)
diff --git a/src/library/scala/io/BytePickle.scala b/src/library/scala/io/BytePickle.scala
index 3a51f2e0b8..97e3ff589d 100644
--- a/src/library/scala/io/BytePickle.scala
+++ b/src/library/scala/io/BytePickle.scala
@@ -144,7 +144,7 @@ object BytePickle {
case Ref() =>
val res2 = unat.appU(res._2) // read location
upe.get(res2._1) match { // lookup value in unpickler env
- case None => throw new IllegalArgumentException("invalid unpickler environment"); return null
+ case None => throw new IllegalArgumentException("invalid unpickler environment")
case Some(v) => return (v.asInstanceOf[a], new UnPicklerState(res2._2, upe))
}
}
@@ -153,7 +153,7 @@ object BytePickle {
def ulift[t](x: t): PU[t] = new PU[t] {
def appP(a: t, state: Array[Byte]): Array[Byte] =
- if (x != a) { throw new IllegalArgumentException("value to be pickled (" + a + ") != " + x); state }
+ if (x != a) throw new IllegalArgumentException("value to be pickled (" + a + ") != " + x)
else state;
def appU(state: Array[Byte]) = (x, state)
}
diff --git a/src/library/scala/mobile/Code.scala b/src/library/scala/mobile/Code.scala
index 677248aabb..e1d04eafa5 100644
--- a/src/library/scala/mobile/Code.scala
+++ b/src/library/scala/mobile/Code.scala
@@ -197,7 +197,6 @@ class Code(clazz: java.lang.Class[_]) {
cs(0).newInstance("").asInstanceOf[AnyRef]
} else {
system.error("class " + clazz.getName() + " has no public constructor")
- null
}
}
}
diff --git a/src/library/scala/xml/dtd/Scanner.scala b/src/library/scala/xml/dtd/Scanner.scala
index 0b2daa3284..1ac3c0f24d 100644
--- a/src/library/scala/xml/dtd/Scanner.scala
+++ b/src/library/scala/xml/dtd/Scanner.scala
@@ -65,7 +65,7 @@ class Scanner extends Tokens with parsing.TokenTests {
case ENDCH => END
case _ =>
if (isNameStart(c)) name; // NAME
- else { system.error("unexpected character:"+c); END }
+ else system.error("unexpected character:" + c)
}
final def name = {
diff --git a/test/files/neg/check-dead.check b/test/files/neg/check-dead.check
index be4de3060b..29601c1d4a 100644
--- a/test/files/neg/check-dead.check
+++ b/test/files/neg/check-dead.check
@@ -1,7 +1,13 @@
-check-dead.scala:27: error: dead code following this construct
- throw new Exception
+check-dead.scala:7: error: dead code following this construct
+ def z1 = y1(throw new Exception) // should warn
+ ^
+check-dead.scala:10: error: dead code following this construct
+ def z2 = y2(throw new Exception) // should warn
+ ^
+check-dead.scala:29: error: dead code following this construct
+ throw new Exception // should warn
^
-check-dead.scala:31: error: dead code following this construct
- throw new Exception
+check-dead.scala:33: error: dead code following this construct
+ throw new Exception // should warn
^
-two errors found
+four errors found
diff --git a/test/files/neg/check-dead.scala b/test/files/neg/check-dead.scala
index 851e81d886..2d5bccb21d 100644
--- a/test/files/neg/check-dead.scala
+++ b/test/files/neg/check-dead.scala
@@ -1,34 +1,37 @@
-package dummy
-
-object Error {
- def soSorry(msg: String = "sorry"): Nothing =
- throw new Exception("we have a problem: "+msg)
+object Other {
+ def oops(msg: String = "xxx"): Nothing = throw new Exception(msg) // should not warn
}
class NoDeads {
- def x = synchronized { throw new Exception }
- def y[T](arg: T) = println("foo")
- def z = this.y(throw new Exception)
-
- def dummy1: Int = synchronized {
- val i = 10 + 2
- return i
- }
- def dummy1b: Int = synchronized {
- val i = 10 + 2
- i
- }
-
- def dummy2: String = Error.soSorry("we're dummies")
-}
+ def y1(arg: Any) = println("foo")
+ def z1 = y1(throw new Exception) // should warn
+
+ def y2[T](arg: T) = println("foo")
+ def z2 = y2(throw new Exception) // should warn
-class Deads {
- def x1 = synchronized {
- throw new Exception
+ def y3[T](arg: => T) = println("foo")
+ def z3 = y3(throw new Exception) // should not warn: by name arg
+
+ def nowarn1 = synchronized { throw new Exception } // should not warn: synchronized should be by name
+
+ def nowarn2: Int = synchronized { // should not warn
+ val i = 10 + 2
+ return i
+ }
+ def nowarn3: Int = synchronized { // should not warn
+ val i = 10 + 2
+ i
+ }
+
+ def nowarn4: String = Other.oops("don't warn about me") // should not warn
+
+ def yeswarn1 = synchronized {
+ throw new Exception // should warn
5 * 5
}
- def x2: Int = synchronized {
- throw new Exception
+ def yeswarn2: Int = synchronized {
+ throw new Exception // should warn
return 5
}
-} \ No newline at end of file
+}
+