summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2012-10-30 14:29:14 -0700
committerAdriaan Moors <adriaan.moors@epfl.ch>2012-11-01 18:50:43 -0700
commitd0c4be6861109683d80513eda74e5c6ca88f1441 (patch)
treef0604d9f71cd0807e6a5a6351535a78ac18ddefc
parenta70c8219220b637072b52fc834439d90f0cf5b38 (diff)
downloadscala-d0c4be6861109683d80513eda74e5c6ca88f1441.tar.gz
scala-d0c4be6861109683d80513eda74e5c6ca88f1441.tar.bz2
scala-d0c4be6861109683d80513eda74e5c6ca88f1441.zip
Warn about unused private members.
Warnings enabled via -Xlint. It's one of the most requested features. And it is hard to argue we don't need it: see the 99 methods removed in the next commit. This should close SI-440.
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Analyzer.scala9
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala57
-rw-r--r--test/files/neg/warn-unused-privates.check30
-rw-r--r--test/files/neg/warn-unused-privates.flags1
-rw-r--r--test/files/neg/warn-unused-privates.scala58
-rw-r--r--test/files/pos/t5809.scala3
6 files changed, 154 insertions, 4 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
index 3526d932d3..9a6b5c45c4 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala
@@ -95,14 +95,17 @@ trait Analyzer extends AnyRef
}
def apply(unit: CompilationUnit) {
try {
- unit.body = newTyper(rootContext(unit)).typed(unit.body)
+ val typer = newTyper(rootContext(unit))
+ unit.body = typer.typed(unit.body)
if (global.settings.Yrangepos.value && !global.reporter.hasErrors) global.validatePositions(unit.body)
for (workItem <- unit.toCheck) workItem()
- } finally {
+ if (settings.lint.value)
+ typer checkUnused unit
+ }
+ finally {
unit.toCheck.clear()
}
}
}
}
}
-
diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
index e5c0f5767c..283d0fa440 100644
--- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala
@@ -426,6 +426,63 @@ trait TypeDiagnostics {
def permanentlyHiddenWarning(pos: Position, hidden: Name, defn: Symbol) =
contextWarning(pos, "imported `%s' is permanently hidden by definition of %s".format(hidden, defn.fullLocationString))
+ object checkUnused {
+ val ignoreNames = Set[TermName]("readResolve", "readObject", "writeObject", "writeReplace")
+
+ class UnusedPrivates extends Traverser {
+ val defnTrees = ListBuffer[MemberDef]()
+ val targets = mutable.Set[Symbol]()
+ def qualifies(sym: Symbol) = (
+ (sym ne null)
+ && (sym.isMethod || sym.isPrivateLocal && !nme.isLocalName(sym.name))
+ && !sym.isParameter
+ && !sym.isParamAccessor // could improve this, but it's a pain
+ )
+
+ override def traverse(t: Tree): Unit = {
+ t match {
+ case t: ValOrDefDef if qualifies(t.symbol) => defnTrees += t
+ case t: RefTree if t.symbol ne null => targets += t.symbol
+ case _ =>
+ }
+ super.traverse(t)
+ }
+ def isUnused(m: Symbol): Boolean = (
+ m.isPrivate
+ && !targets(m)
+ && !ignoreNames(m.name) // serialization methods
+ && !isConstantType(m.info.resultType) // subject to constant inlining
+ )
+ def unused = defnTrees.toList filter (t => isUnused(t.symbol))
+ }
+
+ def apply(unit: CompilationUnit) = {
+ val p = new UnusedPrivates
+ p traverse unit.body
+ p.unused foreach { defn: DefTree =>
+ val sym = defn.symbol
+ val isDefaultGetter = sym.name containsName nme.DEFAULT_GETTER_STRING
+ val pos = (
+ if (defn.pos.isDefined) defn.pos
+ else if (sym.pos.isDefined) sym.pos
+ else sym match {
+ case sym: TermSymbol => sym.referenced.pos
+ case _ => NoPosition
+ }
+ )
+ val what = (
+ if (isDefaultGetter) "default argument"
+ else if (sym.isConstructor) "constructor"
+ else if (sym.isSetter) "setter"
+ else if (sym.isGetter) "getter"
+ else if (sym.isMethod) "method"
+ else "member"
+ )
+ unit.warning(pos, s"private $what in ${sym.owner} is never used")
+ }
+ }
+ }
+
object checkDead {
private var expr: Symbol = NoSymbol
diff --git a/test/files/neg/warn-unused-privates.check b/test/files/neg/warn-unused-privates.check
new file mode 100644
index 0000000000..c37e01106c
--- /dev/null
+++ b/test/files/neg/warn-unused-privates.check
@@ -0,0 +1,30 @@
+warn-unused-privates.scala:2: warning: private constructor in class Bippy is never used
+ private def this(c: Int) = this(c, c) // warn
+ ^
+warn-unused-privates.scala:4: warning: private method in class Bippy is never used
+ private def boop(x: Int) = x+a+b // warn
+ ^
+warn-unused-privates.scala:6: warning: private getter in class Bippy is never used
+ final private val MILLIS2: Int = 1000 // warn
+ ^
+warn-unused-privates.scala:13: warning: private getter in object Bippy is never used
+ private val HEY_INSTANCE: Int = 1000 // warn
+ ^
+warn-unused-privates.scala:41: warning: private getter in trait Accessors is never used
+ private var v1: Int = 0 // warn
+ ^
+warn-unused-privates.scala:42: warning: private setter in trait Accessors is never used
+ private var v2: Int = 0 // warn, never set
+ ^
+warn-unused-privates.scala:43: warning: private getter in trait Accessors is never used
+ private var v3: Int = 0 // warn, never got
+ ^
+warn-unused-privates.scala:55: warning: private default argument in trait DefaultArgs is never used
+ private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3
+ ^
+warn-unused-privates.scala:55: warning: private default argument in trait DefaultArgs is never used
+ private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3
+ ^
+error: No warnings can be incurred under -Xfatal-warnings.
+9 warnings found
+one error found
diff --git a/test/files/neg/warn-unused-privates.flags b/test/files/neg/warn-unused-privates.flags
new file mode 100644
index 0000000000..7949c2afa2
--- /dev/null
+++ b/test/files/neg/warn-unused-privates.flags
@@ -0,0 +1 @@
+-Xlint -Xfatal-warnings
diff --git a/test/files/neg/warn-unused-privates.scala b/test/files/neg/warn-unused-privates.scala
new file mode 100644
index 0000000000..1ac272357f
--- /dev/null
+++ b/test/files/neg/warn-unused-privates.scala
@@ -0,0 +1,58 @@
+class Bippy(a: Int, b: Int) {
+ private def this(c: Int) = this(c, c) // warn
+ private def bippy(x: Int): Int = bippy(x) // TODO: could warn
+ private def boop(x: Int) = x+a+b // warn
+ final private val MILLIS1 = 2000 // no warn, might have been inlined
+ final private val MILLIS2: Int = 1000 // warn
+ final private val HI_COMPANION: Int = 500 // no warn, accessed from companion
+ def hi() = Bippy.HI_INSTANCE
+}
+object Bippy {
+ def hi(x: Bippy) = x.HI_COMPANION
+ private val HI_INSTANCE: Int = 500 // no warn, accessed from instance
+ private val HEY_INSTANCE: Int = 1000 // warn
+}
+
+class A(val msg: String)
+class B1(msg: String) extends A(msg)
+class B2(msg0: String) extends A(msg0)
+class B3(msg0: String) extends A("msg")
+
+/*** Early defs full of noise due to SI-6595. ***/
+/***
+class Boppy extends {
+ private val hmm: String = "abc" // no warn, used in early defs
+ private val hom: String = "def" // no warn, used in body
+ private final val him = "ghi" // no warn, might have been (was) inlined
+ final val him2 = "ghi" // no warn, same
+ final val himinline = him
+ private val hum: String = "jkl" // warn
+ final val ding = hmm.length
+} with Mutable {
+ val dinger = hom
+ private val hummer = "def" // warn
+
+ private final val bum = "ghi" // no warn, might have been (was) inlined
+ final val bum2 = "ghi" // no warn, same
+}
+***/
+
+trait Accessors {
+ private var v1: Int = 0 // warn
+ private var v2: Int = 0 // warn, never set
+ private var v3: Int = 0 // warn, never got
+ private var v4: Int = 0 // no warn
+
+ def bippy(): Int = {
+ v3 = 5
+ v4 = 6
+ v2 + v4
+ }
+}
+
+trait DefaultArgs {
+ // warn about default getters for x2 and x3
+ private def bippy(x1: Int, x2: Int = 10, x3: Int = 15): Int = x1 + x2 + x3
+
+ def boppy() = bippy(5, 100, 200)
+}
diff --git a/test/files/pos/t5809.scala b/test/files/pos/t5809.scala
index 133e13c4ed..4bcd743faa 100644
--- a/test/files/pos/t5809.scala
+++ b/test/files/pos/t5809.scala
@@ -1,5 +1,6 @@
package object foo {
implicit class PimpedInt(foo: Int) {
def bar = ???
+ def bippy = foo
}
-} \ No newline at end of file
+}