From d0c4be6861109683d80513eda74e5c6ca88f1441 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Tue, 30 Oct 2012 14:29:14 -0700 Subject: 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. --- .../scala/tools/nsc/typechecker/Analyzer.scala | 9 ++-- .../tools/nsc/typechecker/TypeDiagnostics.scala | 57 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 3 deletions(-) (limited to 'src') 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 -- cgit v1.2.3