diff options
author | Seth Tisue <seth@tisue.net> | 2017-04-10 14:34:51 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-04-10 14:34:51 -0500 |
commit | 15e2759f49a3ef0f71290f4bbd9839cbf2346b0e (patch) | |
tree | a809f665e61208cb7e8ae098c676e80f341a68ed | |
parent | 715c88e9b74d1b4d1d0c4da9d0cc8f1b740e2dd3 (diff) | |
parent | bad61ce0ff9f460c2f8873c134a7f6bee0a53824 (diff) | |
download | scala-15e2759f49a3ef0f71290f4bbd9839cbf2346b0e.tar.gz scala-15e2759f49a3ef0f71290f4bbd9839cbf2346b0e.tar.bz2 scala-15e2759f49a3ef0f71290f4bbd9839cbf2346b0e.zip |
Merge pull request #5402 from som-snytt/issue/8040-unused
SI-8040 Improve unused warnings
48 files changed, 737 insertions, 129 deletions
diff --git a/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala b/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala index ce26232e5f..089f07de06 100644 --- a/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala +++ b/src/compiler/scala/reflect/reify/codegen/GenAnnotationInfos.scala @@ -10,7 +10,7 @@ trait GenAnnotationInfos { // however, when reifying free and tough types, we're forced to reify annotation infos as is // why is that bad? take a look inside def reifyAnnotationInfo(ann: AnnotationInfo): Tree = { - val reifiedArgs = ann.args map { arg => + ann.args.foreach { arg => val saved1 = reifyTreeSymbols val saved2 = reifyTreeTypes diff --git a/src/compiler/scala/tools/nsc/ast/TreeGen.scala b/src/compiler/scala/tools/nsc/ast/TreeGen.scala index dd827a8f52..b073cb828c 100644 --- a/src/compiler/scala/tools/nsc/ast/TreeGen.scala +++ b/src/compiler/scala/tools/nsc/ast/TreeGen.scala @@ -371,4 +371,6 @@ abstract class TreeGen extends scala.reflect.internal.TreeGen with TreeDSL { Typed(New(anonClass.tpe), TypeTree(fun.tpe))) } } + + override def isPatVarWarnable = settings.warnUnusedPatVars } diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 82664ba9c0..707fe15f91 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -1947,19 +1947,22 @@ self => * | Pattern3 * }}} */ - def pattern2(): Tree = { - val p = pattern3() - - if (in.token != AT) p - else p match { - case Ident(nme.WILDCARD) => - in.nextToken() - pattern3() - case Ident(name) => - in.nextToken() - atPos(p.pos.start) { Bind(name, pattern3()) } - case _ => p - } + def pattern2(): Tree = (pattern3(), in.token) match { + case (Ident(nme.WILDCARD), AT) => + in.nextToken() + pattern3() + case (p @ Ident(name), AT) => + in.nextToken() + val body = pattern3() + atPos(p.pos.start, p.pos.start, body.pos.end) { + val t = Bind(name, body) + body match { + case Ident(nme.WILDCARD) => t updateAttachment AtBoundIdentifierAttachment + case _ if !settings.warnUnusedPatVars => t updateAttachment AtBoundIdentifierAttachment + case _ => t + } + } + case (p, _) => p } /** {{{ @@ -2854,9 +2857,8 @@ self => val (constrMods, vparamss) = if (mods.isTrait) (Modifiers(Flags.TRAIT), List()) else (accessModifierOpt(), paramClauses(name, classContextBounds, ofCaseClass = mods.isCase)) - var mods1 = mods - val template = templateOpt(mods1, name, constrMods withAnnotations constrAnnots, vparamss, tstart) - val result = gen.mkClassDef(mods1, name, tparams, template) + val template = templateOpt(mods, name, constrMods withAnnotations constrAnnots, vparamss, tstart) + val result = gen.mkClassDef(mods, name, tparams, template) // Context bounds generate implicit parameters (part of the template) with types // from tparams: we need to ensure these don't overlap if (!classContextBounds.isEmpty) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala index 0ae8347dc5..1c29859f46 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala @@ -15,7 +15,6 @@ import scala.collection.JavaConverters._ import AsmUtils._ import BytecodeUtils._ import collection.mutable -import scala.tools.asm.tree.analysis.{Analyzer, SourceInterpreter} import BackendReporting._ import scala.tools.nsc.backend.jvm.BTypes.InternalName diff --git a/src/compiler/scala/tools/nsc/settings/Warnings.scala b/src/compiler/scala/tools/nsc/settings/Warnings.scala index 87534656f9..329a6aadd7 100644 --- a/src/compiler/scala/tools/nsc/settings/Warnings.scala +++ b/src/compiler/scala/tools/nsc/settings/Warnings.scala @@ -15,15 +15,40 @@ trait Warnings { // Warning semantics. val fatalWarnings = BooleanSetting("-Xfatal-warnings", "Fail the compilation if there are any warnings.") - // Non-lint warnings + // Non-lint warnings. val warnDeadCode = BooleanSetting("-Ywarn-dead-code", "Warn when dead code is identified.") val warnValueDiscard = BooleanSetting("-Ywarn-value-discard", "Warn when non-Unit expression results are unused.") val warnNumericWiden = BooleanSetting("-Ywarn-numeric-widen", "Warn when numerics are widened.") - // SI-7712, SI-7707 warnUnused not quite ready for prime-time - val warnUnused = BooleanSetting("-Ywarn-unused", "Warn when local and private vals, vars, defs, and types are unused.") - // currently considered too noisy for general use - val warnUnusedImport = BooleanSetting("-Ywarn-unused-import", "Warn when imports are unused.") + + object UnusedWarnings extends MultiChoiceEnumeration { + val Imports = Choice("imports", "Warn if an import selector is not referenced.") + val PatVars = Choice("patvars", "Warn if a variable bound in a pattern is unused.") + val Privates = Choice("privates", "Warn if a private member is unused.") + val Locals = Choice("locals", "Warn if a local definition is unused.") + val Params = Choice("params", "Warn if a value parameter is unused.") + val Implicits = Choice("implicits", "Warn if an implicit parameter is unused.") + } + + // The -Ywarn-unused warning group. + val warnUnused = MultiChoiceSetting( + name = "-Ywarn-unused", + helpArg = "warning", + descr = "Enable or disable specific `unused' warnings", + domain = UnusedWarnings, + default = Some(List("_")) + ) + + def warnUnusedImport = warnUnused contains UnusedWarnings.Imports + def warnUnusedPatVars = warnUnused contains UnusedWarnings.PatVars + def warnUnusedPrivates = warnUnused contains UnusedWarnings.Privates + def warnUnusedLocals = warnUnused contains UnusedWarnings.Locals + def warnUnusedParams = warnUnused contains UnusedWarnings.Params + def warnUnusedImplicits = warnUnused contains UnusedWarnings.Implicits + + BooleanSetting("-Ywarn-unused-import", "Warn when imports are unused.") withPostSetHook { s => + warnUnused.add(s"${if (s) "" else "-"}imports") + } //withDeprecationMessage s"Enable -Ywarn-unused:imports" val warnExtraImplicit = BooleanSetting("-Ywarn-extra-implicit", "Warn when more than one implicit parameter section is defined.") @@ -60,6 +85,7 @@ trait Warnings { val UnsoundMatch = LintWarning("unsound-match", "Pattern match may not be typesafe.") val StarsAlign = LintWarning("stars-align", "Pattern sequence wildcard must align with sequence component.") val Constant = LintWarning("constant", "Evaluation of a constant arithmetic expression results in an error.") + val Unused = LintWarning("unused", "Enable -Ywarn-unused:imports,privates,locals,implicits.") def allLintWarnings = values.toSeq.asInstanceOf[Seq[LintWarning]] } @@ -82,6 +108,7 @@ trait Warnings { def warnUnsoundMatch = lint contains UnsoundMatch def warnStarsAlign = lint contains StarsAlign def warnConstant = lint contains Constant + def lintUnused = lint contains Unused // Lint warnings that are currently -Y, but deprecated in that usage @deprecated("Use warnAdaptedArgs", since="2.11.2") @@ -101,7 +128,11 @@ trait Warnings { helpArg = "warning", descr = "Enable or disable specific warnings", domain = LintWarnings, - default = Some(List("_"))) + default = Some(List("_")) + ).withPostSetHook { s => + val unused = List("imports", "privates", "locals", "implicits") + if (s contains Unused) unused.foreach(warnUnused.add) + } allLintWarnings foreach { case w if w.yAliased => diff --git a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala index 2e7ab8a887..034cf118d7 100644 --- a/src/compiler/scala/tools/nsc/transform/Delambdafy.scala +++ b/src/compiler/scala/tools/nsc/transform/Delambdafy.scala @@ -347,7 +347,7 @@ abstract class Delambdafy extends Transform with TypingTransformers with ast.Tre // recursively find methods that refer to 'this' directly or indirectly via references to other methods // for each method found add it to the referrers set private def refersToThis(symbol: Symbol): Boolean = { - var seen = mutable.Set[Symbol]() + val seen = mutable.Set[Symbol]() def loop(symbol: Symbol): Boolean = { if (seen(symbol)) false else { diff --git a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala index 323fe1c171..b8ef439e03 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Analyzer.scala @@ -104,7 +104,7 @@ trait Analyzer extends AnyRef for (workItem <- unit.toCheck) workItem() if (settings.warnUnusedImport) warnUnusedImports(unit) - if (settings.warnUnused) + if (settings.warnUnused.isSetByUser) typer checkUnused unit } finally { diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index db3bb9badb..c80bdb180b 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -64,10 +64,8 @@ trait Contexts { self: Analyzer => for (imps <- allImportInfos.remove(unit)) { for (imp <- imps.distinct.reverse) { val used = allUsedSelectors(imp) - - imp.tree.selectors filterNot (s => isMaskImport(s) || used(s)) foreach { sel => - reporter.warning(imp posOf sel, "Unused import") - } + for (sel <- imp.tree.selectors if !isMaskImport(sel) && !used(sel)) + reporter.warning(imp.posOf(sel), "Unused import") } allUsedSelectors --= imps } @@ -825,7 +823,6 @@ trait Contexts { self: Analyzer => private def collectImplicitImports(imp: ImportInfo): List[ImplicitInfo] = { val qual = imp.qual - val qualSym = qual.tpe.typeSymbol val pre = qual.tpe def collect(sels: List[ImportSelector]): List[ImplicitInfo] = sels match { case List() => @@ -1412,7 +1409,8 @@ trait Contexts { self: Analyzer => class ImportInfo(val tree: Import, val depth: Int) { def pos = tree.pos - def posOf(sel: ImportSelector) = tree.pos withPoint sel.namePos + def posOf(sel: ImportSelector) = + if (sel.namePos >= 0) tree.pos withPoint sel.namePos else tree.pos /** The prefix expression */ def qual: Tree = tree.symbol.info match { diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala index 7b261810d4..86a1d3f2e4 100644 --- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala +++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala @@ -323,8 +323,6 @@ abstract class RefChecks extends Transform { import pair._ val member = low val other = high - def memberTp = lowType - def otherTp = highType // debuglog(s"Checking validity of ${member.fullLocationString} overriding ${other.fullLocationString}") diff --git a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala index 35e6e0099e..cd1dd18768 100644 --- a/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala +++ b/src/compiler/scala/tools/nsc/typechecker/TypeDiagnostics.scala @@ -33,7 +33,7 @@ import scala.annotation.tailrec * @version 1.0 */ trait TypeDiagnostics { - self: Analyzer => + self: Analyzer with StdAttachments => import global._ import definitions._ @@ -79,6 +79,8 @@ trait TypeDiagnostics { prefix + name.decode } + private def atBounded(t: Tree) = t.hasAttachment[AtBoundIdentifierAttachment.type] + /** Does the positioned line assigned to t1 precede that of t2? */ def posPrecedes(p1: Position, p2: Position) = p1.isDefined && p2.isDefined && p1.line < p2.line @@ -353,7 +355,10 @@ trait TypeDiagnostics { // functions to manipulate the name def preQualify() = modifyName(trueOwner.fullName + "." + _) - def postQualify() = if (!(postQualifiedWith contains trueOwner)) { postQualifiedWith ::= trueOwner; modifyName(_ + "(in " + trueOwner + ")") } + def postQualify() = if (!(postQualifiedWith contains trueOwner)) { + postQualifiedWith ::= trueOwner + modifyName(s => s"$s(in $trueOwner)") + } def typeQualify() = if (sym.isTypeParameterOrSkolem) postQualify() def nameQualify() = if (trueOwner.isPackageClass) preQualify() else postQualify() @@ -464,13 +469,18 @@ trait TypeDiagnostics { context.warning(pos, "imported `%s' is permanently hidden by definition of %s".format(hidden, defn.fullLocationString)) object checkUnused { - val ignoreNames: Set[TermName] = Set(TermName("readResolve"), TermName("readObject"), TermName("writeObject"), TermName("writeReplace")) + val ignoreNames: Set[TermName] = Set( + "readResolve", "readObject", "writeObject", "writeReplace" + ).map(TermName(_)) class UnusedPrivates extends Traverser { val defnTrees = ListBuffer[MemberDef]() val targets = mutable.Set[Symbol]() val setVars = mutable.Set[Symbol]() val treeTypes = mutable.Set[Type]() + val atBounds = mutable.Set[Symbol]() + val params = mutable.Set[Symbol]() + val patvars = mutable.Set[Symbol]() def defnSymbols = defnTrees.toList map (_.symbol) def localVars = defnSymbols filter (t => t.isLocalToBlock && t.isVar) @@ -490,20 +500,39 @@ trait TypeDiagnostics { ) override def traverse(t: Tree): Unit = { + val sym = t.symbol t match { - case t: MemberDef if qualifies(t.symbol) => defnTrees += t - case t: RefTree if t.symbol ne null => targets += t.symbol + case m: MemberDef if qualifies(t.symbol) => + defnTrees += m + t match { + case DefDef(mods@_, name@_, tparams@_, vparamss, tpt@_, rhs@_) if !sym.isAbstract && !sym.isDeprecated && !sym.isMacro => + if (sym.isPrimaryConstructor) + for (cpa <- sym.owner.constrParamAccessors if cpa.isPrivateLocal) params += cpa + else if (sym.isSynthetic && sym.isImplicit) return + else if (!sym.isConstructor) + for (vs <- vparamss) params ++= vs.map(_.symbol) + case _ => + } + case CaseDef(pat, guard@_, rhs@_) if settings.warnUnusedPatVars + => pat.foreach { + // TODO don't warn in isDefinedAt of $anonfun + case b @ Bind(n, _) if !atBounded(b) && n != nme.DEFAULT_CASE => patvars += b.symbol + case _ => + } + case _: RefTree if sym ne null => targets += sym case Assign(lhs, _) if lhs.symbol != null => setVars += lhs.symbol + case Bind(_, _) if atBounded(t) => atBounds += sym case _ => } // Only record type references which don't originate within the // definition of the class being referenced. if (t.tpe ne null) { - for (tp <- t.tpe ; if !treeTypes(tp) && !currentOwner.ownerChain.contains(tp.typeSymbol)) { + for (tp <- t.tpe if !treeTypes(tp) && !currentOwner.ownerChain.contains(tp.typeSymbol)) { tp match { case NoType | NoPrefix => case NullaryMethodType(_) => case MethodType(_, _) => + case SingleType(_, _) => case _ => log(s"$tp referenced from $currentOwner") treeTypes += tp @@ -523,55 +552,127 @@ trait TypeDiagnostics { && (m.isPrivate || m.isLocalToBlock) && !(treeTypes.exists(tp => tp exists (t => t.typeSymbolDirect == m))) ) + def isSyntheticWarnable(sym: Symbol) = ( + sym.isDefaultGetter + ) + def isUnusedTerm(m: Symbol): Boolean = ( - (m.isTerm) - && (m.isPrivate || m.isLocalToBlock) + m.isTerm + && (!m.isSynthetic || isSyntheticWarnable(m)) + && ((m.isPrivate && !(m.isConstructor && m.owner.isAbstract)) || m.isLocalToBlock) && !targets(m) && !(m.name == nme.WILDCARD) // e.g. val _ = foo - && !ignoreNames(m.name.toTermName) // serialization methods + && (m.isValueParameter || !ignoreNames(m.name.toTermName)) // serialization methods && !isConstantType(m.info.resultType) // subject to constant inlining && !treeTypes.exists(_ contains m) // e.g. val a = new Foo ; new a.Bar + //&& !(m.isVal && m.info.resultType =:= typeOf[Unit]) // Unit val is uninteresting + ) + def isUnusedParam(m: Symbol): Boolean = ( + isUnusedTerm(m) + && !m.isDeprecated + && !m.owner.isDefaultGetter + && !(m.isParamAccessor && ( + m.owner.isImplicit || + targets.exists(s => s.isParameter + && s.name == m.name && s.owner.isConstructor && s.owner.owner == m.owner) // exclude ctor params + )) ) - def unusedTypes = defnTrees.toList filter (t => isUnusedType(t.symbol)) - def unusedTerms = defnTrees.toList filter (v => isUnusedTerm(v.symbol)) + def sympos(s: Symbol): Int = + if (s.pos.isDefined) s.pos.point else if (s.isTerm) s.asTerm.referenced.pos.point else -1 + def treepos(t: Tree): Int = + if (t.pos.isDefined) t.pos.point else sympos(t.symbol) + + def unusedTypes = defnTrees.toList.filter(t => isUnusedType(t.symbol)).sortBy(treepos) + def unusedTerms = { + val all = defnTrees.toList.filter(v => isUnusedTerm(v.symbol)) + + // filter out setters if already warning for getter, indicated by position. + // also documentary names in patterns. + all.filterNot(v => + v.symbol.isSetter && all.exists(g => g.symbol.isGetter && g.symbol.pos.point == v.symbol.pos.point) + || atBounds.exists(x => v.symbol.pos.point == x.pos.point) + ).sortBy(treepos) + } // local vars which are never set, except those already returned in unused - def unsetVars = localVars filter (v => !setVars(v) && !isUnusedTerm(v)) + def unsetVars = localVars.filter(v => !setVars(v) && !isUnusedTerm(v)).sortBy(sympos) + def unusedParams = params.toList.filter(isUnusedParam).sortBy(sympos) + def inDefinedAt(p: Symbol) = p.owner.isMethod && p.owner.name == nme.isDefinedAt && p.owner.owner.isAnonymousFunction + def unusedPatVars = patvars.toList.filter(p => isUnusedTerm(p) && !inDefinedAt(p)).sortBy(sympos) + } + + private def warningsEnabled: Boolean = { + val ss = settings + import ss._ + warnUnusedPatVars || warnUnusedPrivates || warnUnusedLocals || warnUnusedParams || warnUnusedImplicits } - def apply(unit: CompilationUnit) = { + def apply(unit: CompilationUnit): Unit = if (warningsEnabled) { val p = new UnusedPrivates - p traverse unit.body - val unused = p.unusedTerms - unused foreach { defn: DefTree => - val sym = defn.symbol - 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 + p.traverse(unit.body) + if (settings.warnUnusedLocals || settings.warnUnusedPrivates) { + for (defn: DefTree <- p.unusedTerms) { + val sym = defn.symbol + 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 why = if (sym.isPrivate) "private" else "local" + val what = ( + if (sym.isDefaultGetter) "default argument" + else if (sym.isConstructor) "constructor" + else if ( + sym.isVar + || sym.isGetter && (sym.accessed.isVar || (sym.owner.isTrait && !sym.hasFlag(STABLE))) + ) s"var ${sym.name.getterName.decoded}" + else if ( + sym.isVal + || sym.isGetter && (sym.accessed.isVal || (sym.owner.isTrait && sym.hasFlag(STABLE))) + || sym.isLazy + ) s"val ${sym.name.decoded}" + else if (sym.isSetter) s"setter of ${sym.name.getterName.decoded}" + else if (sym.isMethod) s"method ${sym.name.decoded}" + else if (sym.isModule) s"object ${sym.name.decoded}" + else "term" + ) + reporter.warning(pos, s"$why $what in ${sym.owner} is never used") + } + for (v <- p.unsetVars) { + reporter.warning(v.pos, s"local var ${v.name} in ${v.owner} is never set: consider using immutable val") + } + for (t <- p.unusedTypes) { + val sym = t.symbol + val wrn = if (sym.isPrivate) settings.warnUnusedPrivates else settings.warnUnusedLocals + if (wrn) { + val why = if (sym.isPrivate) "private" else "local" + reporter.warning(t.pos, s"$why ${sym.fullLocationString} is never used") } - ) - val why = if (sym.isPrivate) "private" else "local" - val what = ( - if (sym.isDefaultGetter) "default argument" - else if (sym.isConstructor) "constructor" - else if (sym.isVar || sym.isGetter && (sym.accessed.isVar || (sym.owner.isTrait && !sym.hasFlag(STABLE)))) "var" - else if (sym.isVal || sym.isGetter && (sym.accessed.isVal || (sym.owner.isTrait && sym.hasFlag(STABLE))) || sym.isLazy) "val" - else if (sym.isSetter) "setter" - else if (sym.isMethod) "method" - else if (sym.isModule) "object" - else "term" - ) - reporter.warning(pos, s"$why $what in ${sym.owner} is never used") + } } - p.unsetVars foreach { v => - reporter.warning(v.pos, s"local var ${v.name} in ${v.owner} is never set - it could be a val") + if (settings.warnUnusedPatVars) { + for (v <- p.unusedPatVars) + reporter.warning(v.pos, s"pattern var ${v.name} in ${v.owner} is never used; `${v.name}@_' suppresses this warning") } - p.unusedTypes foreach { t => - val sym = t.symbol - val why = if (sym.isPrivate) "private" else "local" - reporter.warning(t.pos, s"$why ${sym.fullLocationString} is never used") + if (settings.warnUnusedParams || settings.warnUnusedImplicits) { + def classOf(s: Symbol): Symbol = if (s.isClass || s == NoSymbol) s else classOf(s.owner) + def isImplementation(m: Symbol): Boolean = { + val opc = new overridingPairs.Cursor(classOf(m)) + opc.iterator.exists(pair => pair.low == m) + } + def isConvention(p: Symbol): Boolean = { + (p.name.decoded == "args" && p.owner.isMethod && p.owner.name.decoded == "main") || + (p.tpe =:= typeOf[scala.Predef.DummyImplicit]) + } + def warnable(s: Symbol) = ( + (settings.warnUnusedParams || s.isImplicit) + && !isImplementation(s.owner) + && !isConvention(s) + ) + for (s <- p.unusedParams if warnable(s)) + reporter.warning(s.pos, s"parameter $s in ${s.owner} is never used") } } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index 05f7deb352..cd4a883a33 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -4267,7 +4267,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper val name = tree.name val body = tree.body name match { - case name: TypeName => assert(body == EmptyTree, context.unit + " typedBind: " + name.debugString + " " + body + " " + body.getClass) + case name: TypeName => + assert(body == EmptyTree, s"${context.unit} typedBind: ${name.debugString} ${body} ${body.getClass}") val sym = if (tree.symbol != NoSymbol) tree.symbol else { diff --git a/src/compiler/scala/tools/reflect/FormatInterpolator.scala b/src/compiler/scala/tools/reflect/FormatInterpolator.scala index 9825acd39f..857b733f59 100644 --- a/src/compiler/scala/tools/reflect/FormatInterpolator.scala +++ b/src/compiler/scala/tools/reflect/FormatInterpolator.scala @@ -19,7 +19,6 @@ abstract class FormatInterpolator { @inline private def truly(body: => Unit): Boolean = { body ; true } @inline private def falsely(body: => Unit): Boolean = { body ; false } - private def fail(msg: String) = c.abort(c.enclosingPosition, msg) private def bail(msg: String) = global.abort(msg) def interpolate: Tree = c.macroApplication match { @@ -93,8 +92,8 @@ abstract class FormatInterpolator { case '\n' => "\\n" case '\f' => "\\f" case '\r' => "\\r" - case '\"' => "${'\"'}" /* avoid lint warn */ + - " or a triple-quoted literal \"\"\"with embedded \" or \\u0022\"\"\"" // $" in future + case '\"' => "$" /* avoid lint warn */ + + "{'\"'} or a triple-quoted literal \"\"\"with embedded \" or \\u0022\"\"\"" case '\'' => "'" case '\\' => """\\""" case x => "\\u%04x" format x diff --git a/src/reflect/scala/reflect/internal/StdAttachments.scala b/src/reflect/scala/reflect/internal/StdAttachments.scala index fc49de1cf6..f72c1eb1b3 100644 --- a/src/reflect/scala/reflect/internal/StdAttachments.scala +++ b/src/reflect/scala/reflect/internal/StdAttachments.scala @@ -57,6 +57,12 @@ trait StdAttachments { */ case object BackquotedIdentifierAttachment extends PlainAttachment + /** Indicates that the host `Ident` has been created from a pattern2 binding, `case x @ p`. + * In the absence of named parameters in patterns, allows nuanced warnings for unused variables. + * Hence, `case X(x = _) =>` would not warn; for now, `case X(x @ _) =>` is documentary if x is unused. + */ + case object AtBoundIdentifierAttachment extends PlainAttachment + /** Identifies trees are either result or intermediate value of for loop desugaring. */ case object ForAttachment extends PlainAttachment diff --git a/src/reflect/scala/reflect/internal/TreeGen.scala b/src/reflect/scala/reflect/internal/TreeGen.scala index 4fecaf70df..ade9ee84ac 100644 --- a/src/reflect/scala/reflect/internal/TreeGen.scala +++ b/src/reflect/scala/reflect/internal/TreeGen.scala @@ -797,7 +797,7 @@ abstract class TreeGen { /** Create tree for for-comprehension generator <val pat0 <- rhs0> */ def mkGenerator(pos: Position, pat: Tree, valeq: Boolean, rhs: Tree)(implicit fresh: FreshNameCreator): Tree = { - val pat1 = patvarTransformer.transform(pat) + val pat1 = patvarTransformerForFor.transform(pat) if (valeq) ValEq(pat1, rhs).setPos(pos) else ValFrom(pat1, mkCheckIfRefutable(pat1, rhs)).setPos(pos) } @@ -894,11 +894,15 @@ abstract class TreeGen { * x becomes x @ _ * x: T becomes x @ (_: T) */ - object patvarTransformer extends Transformer { + class PatvarTransformer(forFor: Boolean) extends Transformer { override def transform(tree: Tree): Tree = tree match { - case Ident(name) if (treeInfo.isVarPattern(tree) && name != nme.WILDCARD) => - atPos(tree.pos)(Bind(name, atPos(tree.pos.focus) (Ident(nme.WILDCARD)))) - case Typed(id @ Ident(name), tpt) if (treeInfo.isVarPattern(id) && name != nme.WILDCARD) => + case Ident(name) if treeInfo.isVarPattern(tree) && name != nme.WILDCARD => + atPos(tree.pos) { + val b = Bind(name, atPos(tree.pos.focus) (Ident(nme.WILDCARD))) + if (!forFor && isPatVarWarnable) b + else b updateAttachment AtBoundIdentifierAttachment + } + case Typed(id @ Ident(name), tpt) if treeInfo.isVarPattern(id) && name != nme.WILDCARD => atPos(tree.pos.withPoint(id.pos.point)) { Bind(name, atPos(tree.pos.withStart(tree.pos.point)) { Typed(Ident(nme.WILDCARD), tpt) @@ -919,6 +923,15 @@ abstract class TreeGen { } } + /** Can be overridden to depend on settings.warnUnusedPatvars. */ + def isPatVarWarnable: Boolean = true + + /** Not in for comprehensions, whether to warn unused pat vars depends on flag. */ + object patvarTransformer extends PatvarTransformer(forFor = false) + + /** Tag pat vars in for comprehensions. */ + object patvarTransformerForFor extends PatvarTransformer(forFor = true) + // annotate the expression with @unchecked def mkUnchecked(expr: Tree): Tree = atPos(expr.pos) { // This can't be "Annotated(New(UncheckedClass), expr)" because annotations diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala index 9138ed3f02..b455a08036 100644 --- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala +++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala @@ -40,6 +40,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse => this.SAMFunction this.DelambdafyTarget this.BackquotedIdentifierAttachment + this.AtBoundIdentifierAttachment this.ForAttachment this.SyntheticUnitAttachment this.SubpatternsAttachment diff --git a/src/repl/scala/tools/nsc/interpreter/ILoop.scala b/src/repl/scala/tools/nsc/interpreter/ILoop.scala index 8be4d159f1..a729ea4f5f 100644 --- a/src/repl/scala/tools/nsc/interpreter/ILoop.scala +++ b/src/repl/scala/tools/nsc/interpreter/ILoop.scala @@ -958,17 +958,19 @@ class ILoop(in0: Option[BufferedReader], protected val out: JPrintWriter) def withSuppressedSettings[A](body: => A): A = { val ss = this.settings import ss._ - val noisy = List(Xprint, Ytyperdebug) + val noisy = List(Xprint, Ytyperdebug, browse) val noisesome = noisy.exists(!_.isDefault) - val current = (Xprint.value, Ytyperdebug.value) + val current = (Xprint.value, Ytyperdebug.value, browse.value) if (isReplDebug || !noisesome) body else { this.settings.Xprint.value = List.empty + this.settings.browse.value = List.empty this.settings.Ytyperdebug.value = false try body finally { Xprint.value = current._1 Ytyperdebug.value = current._2 + browse.value = current._3 intp.global.printTypings = current._2 } } diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala index a351d2da95..b977ab0939 100644 --- a/src/repl/scala/tools/nsc/interpreter/IMain.scala +++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala @@ -751,11 +751,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends lazy val evalClass = load(evalPath) def evalEither = callEither(resultName) match { - case Left(ex) => ex match { - case ex: NullPointerException => Right(null) - case ex => Left(unwrap(ex)) - } - case Right(result) => Right(result) + case Right(result) => Right(result) + case Left(_: NullPointerException) => Right(null) + case Left(e) => Left(unwrap(e)) } def compile(source: String): Boolean = compileAndSaveRun(label, source) @@ -789,7 +787,7 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends } ((pos, msg)) :: loop(filtered) } - val warnings = loop(run.reporting.allConditionalWarnings.map{case (pos, (msg, since)) => (pos, msg)}) + val warnings = loop(run.reporting.allConditionalWarnings.map{ case (pos, (msg, since@_)) => (pos, msg) }) if (warnings.nonEmpty) mostRecentWarnings = warnings } diff --git a/test/files/neg/abstract-inaccessible.check b/test/files/neg/abstract-inaccessible.check index d56f5691be..739620a4ce 100644 --- a/test/files/neg/abstract-inaccessible.check +++ b/test/files/neg/abstract-inaccessible.check @@ -8,7 +8,7 @@ Classes which cannot access Bippy may be unable to override overrideMe. ^ abstract-inaccessible.scala:7: warning: method overrideMeAlso in trait YourTrait references private[foo] trait Bippy. Classes which cannot access Bippy may be unable to override overrideMeAlso. - def overrideMeAlso(x: Map[Int, Set[Bippy]]) = 5 + def overrideMeAlso(x: Map[Int, Set[Bippy]]) = x.keys.head ^ error: No warnings can be incurred under -Xfatal-warnings. three warnings found diff --git a/test/files/neg/abstract-inaccessible.flags b/test/files/neg/abstract-inaccessible.flags index 6c1dd108ae..ea7773e255 100644 --- a/test/files/neg/abstract-inaccessible.flags +++ b/test/files/neg/abstract-inaccessible.flags @@ -1 +1 @@ --Xfatal-warnings -Xlint
\ No newline at end of file +-Xfatal-warnings -Xlint:inaccessible diff --git a/test/files/neg/abstract-inaccessible.scala b/test/files/neg/abstract-inaccessible.scala index 3c80f30522..02b458016f 100644 --- a/test/files/neg/abstract-inaccessible.scala +++ b/test/files/neg/abstract-inaccessible.scala @@ -4,6 +4,6 @@ package foo { trait YourTrait { def implementMe(f: Int => (String, Bippy)): Unit def overrideMe[T <: Bippy](x: T): T = x - def overrideMeAlso(x: Map[Int, Set[Bippy]]) = 5 + def overrideMeAlso(x: Map[Int, Set[Bippy]]) = x.keys.head } } diff --git a/test/files/neg/forgot-interpolator.flags b/test/files/neg/forgot-interpolator.flags index 7949c2afa2..b0d7bc25cb 100644 --- a/test/files/neg/forgot-interpolator.flags +++ b/test/files/neg/forgot-interpolator.flags @@ -1 +1 @@ --Xlint -Xfatal-warnings +-Xlint:missing-interpolator -Xfatal-warnings diff --git a/test/files/neg/overloaded-implicit.flags b/test/files/neg/overloaded-implicit.flags index 9c1e74e4ef..e04a4228ba 100644 --- a/test/files/neg/overloaded-implicit.flags +++ b/test/files/neg/overloaded-implicit.flags @@ -1 +1 @@ --Xlint -Xfatal-warnings -Xdev +-Xlint:poly-implicit-overload -Xfatal-warnings -Xdev diff --git a/test/files/neg/t1980.flags b/test/files/neg/t1980.flags index 7949c2afa2..cdc464a47d 100644 --- a/test/files/neg/t1980.flags +++ b/test/files/neg/t1980.flags @@ -1 +1 @@ --Xlint -Xfatal-warnings +-Xlint:by-name-right-associative -Xfatal-warnings diff --git a/test/files/neg/t4877.flags b/test/files/neg/t4877.flags deleted file mode 100644 index 7ccd56103a..0000000000 --- a/test/files/neg/t4877.flags +++ /dev/null @@ -1 +0,0 @@ --Xlint
\ No newline at end of file diff --git a/test/files/neg/t6567.flags b/test/files/neg/t6567.flags index e93641e931..076333a011 100644 --- a/test/files/neg/t6567.flags +++ b/test/files/neg/t6567.flags @@ -1 +1 @@ --Xlint -Xfatal-warnings
\ No newline at end of file +-Xlint:option-implicit -Xfatal-warnings diff --git a/test/files/neg/t6675.flags b/test/files/neg/t6675.flags index 2843ea9efc..c6bfaf1f64 100644 --- a/test/files/neg/t6675.flags +++ b/test/files/neg/t6675.flags @@ -1 +1 @@ --deprecation -Xlint -Xfatal-warnings
\ No newline at end of file +-deprecation -Xfatal-warnings diff --git a/test/files/neg/t7860.check b/test/files/neg/t7860.check new file mode 100644 index 0000000000..9b9d86c89d --- /dev/null +++ b/test/files/neg/t7860.check @@ -0,0 +1,9 @@ +t7860.scala:5: warning: private class for your eyes only in object Test is never used + private implicit class `for your eyes only`(i: Int) { // warn + ^ +t7860.scala:31: warning: private class C in object Test3 is never used + private implicit class C(val i: Int) extends AnyVal { // warn + ^ +error: No warnings can be incurred under -Xfatal-warnings. +two warnings found +one error found diff --git a/test/files/neg/t7860.flags b/test/files/neg/t7860.flags new file mode 100644 index 0000000000..6ff0dea0b2 --- /dev/null +++ b/test/files/neg/t7860.flags @@ -0,0 +1 @@ +-Xfatal-warnings -Ywarn-unused:privates diff --git a/test/files/neg/t7860.scala b/test/files/neg/t7860.scala new file mode 100644 index 0000000000..6cc0d3e7f5 --- /dev/null +++ b/test/files/neg/t7860.scala @@ -0,0 +1,42 @@ + +class Test + +object Test { + private implicit class `for your eyes only`(i: Int) { // warn + def f = i + } +} + +class Test2 { + import Test2._ + println(5.toStr) +} + +object Test2 { + // was: warning: private object in object Test2 is never used + // i.e. synthetic object C + private implicit class C(val i: Int) extends AnyVal { // no warn + def toStr = i.toString + } +} + +class Test3 { + import Test3._ + //println(5.toStr) +} + +object Test3 { + // was: warning: private object in object Test2 is never used + // i.e. synthetic object C + private implicit class C(val i: Int) extends AnyVal { // warn + def toStr = i.toString + } +} + +object Test4 { + class A { class B } + + private val a: A = new A + + def b = (new a.B).## +} diff --git a/test/files/neg/warn-unused-implicits.check b/test/files/neg/warn-unused-implicits.check new file mode 100644 index 0000000000..4cc5836800 --- /dev/null +++ b/test/files/neg/warn-unused-implicits.check @@ -0,0 +1,9 @@ +warn-unused-implicits.scala:11: warning: parameter value s in method f is never used + )(implicit s: String): Int = { // warn + ^ +warn-unused-implicits.scala:31: warning: parameter value s in method i is never used + def i(implicit s: String, t: Int) = t // yes, warn + ^ +error: No warnings can be incurred under -Xfatal-warnings. +two warnings found +one error found diff --git a/test/files/neg/warn-unused-implicits.flags b/test/files/neg/warn-unused-implicits.flags new file mode 100644 index 0000000000..18169f3218 --- /dev/null +++ b/test/files/neg/warn-unused-implicits.flags @@ -0,0 +1 @@ +-Ywarn-unused:implicits -Xfatal-warnings diff --git a/test/files/neg/warn-unused-implicits.scala b/test/files/neg/warn-unused-implicits.scala new file mode 100644 index 0000000000..54f924eac0 --- /dev/null +++ b/test/files/neg/warn-unused-implicits.scala @@ -0,0 +1,32 @@ + +trait InterFace { + /** Call something. */ + def call(a: Int, b: String, c: Double)(implicit s: String): Int +} + +trait BadAPI extends InterFace { + def f(a: Int, + b: String, + c: Double + )(implicit s: String): Int = { // warn + println(b + c) + a + } + @deprecated ("no warn in deprecated API", since="yesterday") + def g(a: Int, + b: String, + c: Double + )(implicit s: String): Int = { // no warn + println(b + c) + a + } + override def call(a: Int, + b: String, + c: Double + )(implicit s: String): Int = { // no warn, required by superclass + println(b + c) + a + } + + def i(implicit s: String, t: Int) = t // yes, warn +} diff --git a/test/files/neg/warn-unused-imports.check b/test/files/neg/warn-unused-imports.check index 0a53d7a9cd..29d73a6264 100644 --- a/test/files/neg/warn-unused-imports.check +++ b/test/files/neg/warn-unused-imports.check @@ -51,5 +51,8 @@ warn-unused-imports_2.scala:149: warning: Unused import warn-unused-imports_2.scala:150: warning: Unused import import p1.A // warn ^ -16 warnings found +warn-unused-imports_2.scala:158: warning: Unused import + def x = Macro.f // warn, not crash + ^ +17 warnings found one error found diff --git a/test/files/neg/warn-unused-imports.flags b/test/files/neg/warn-unused-imports.flags index 24db705df1..c4e11e7fe7 100644 --- a/test/files/neg/warn-unused-imports.flags +++ b/test/files/neg/warn-unused-imports.flags @@ -1 +1 @@ --Xfatal-warnings -Ywarn-unused-import +-Xfatal-warnings -Ywarn-unused:imports diff --git a/test/files/neg/warn-unused-imports/sample_1.scala b/test/files/neg/warn-unused-imports/sample_1.scala index d2f86239db..eea4d0eb4c 100644 --- a/test/files/neg/warn-unused-imports/sample_1.scala +++ b/test/files/neg/warn-unused-imports/sample_1.scala @@ -15,3 +15,18 @@ object Sample { def f(x: X) = ??? def g(y: Y) = ??? } + +import scala.language.experimental.macros +import scala.reflect.macros.blackbox.Context + +object Macro { + def f: Int = macro fImpl + def fImpl(c: Context): c.Tree = { + import c.universe._ + + q""" + import scala.util.Random + 42 // TODO randomize + """ + } +} diff --git a/test/files/neg/warn-unused-imports/warn-unused-imports_2.scala b/test/files/neg/warn-unused-imports/warn-unused-imports_2.scala index ded1186209..58fe0131d9 100644 --- a/test/files/neg/warn-unused-imports/warn-unused-imports_2.scala +++ b/test/files/neg/warn-unused-imports/warn-unused-imports_2.scala @@ -96,7 +96,7 @@ trait Warn { trait Nested { { import p1._ // warn - trait Warn { // warn about unused local trait for good measure + trait Warn { // don't warn about unused local trait with -Ywarn-unused:imports import p2._ println(new A) println("abc".bippy) @@ -153,3 +153,7 @@ trait Outsiders { //Future("abc".bippy) } } + +class MacroClient { + def x = Macro.f // warn, not crash +} diff --git a/test/files/neg/warn-unused-params.check b/test/files/neg/warn-unused-params.check new file mode 100644 index 0000000000..373417ce08 --- /dev/null +++ b/test/files/neg/warn-unused-params.check @@ -0,0 +1,18 @@ +warn-unused-params.scala:9: warning: parameter value b in method f is never used + b: String, // warn + ^ +warn-unused-params.scala:32: warning: parameter value s in method i is never used + def i(implicit s: String) = 42 // yes, warn + ^ +warn-unused-params.scala:49: warning: parameter value u in class Unusing is never used +class Unusing(u: Int) { // warn + ^ +warn-unused-params.scala:59: warning: parameter value s in class CaseyAtTheBat is never used +case class CaseyAtTheBat(k: Int)(s: String) // warn + ^ +warn-unused-params.scala:62: warning: parameter value readResolve in method f is never used + def f(readResolve: Int) = 42 // warn + ^ +error: No warnings can be incurred under -Xfatal-warnings. +5 warnings found +one error found diff --git a/test/files/neg/warn-unused-params.flags b/test/files/neg/warn-unused-params.flags new file mode 100644 index 0000000000..795fb74272 --- /dev/null +++ b/test/files/neg/warn-unused-params.flags @@ -0,0 +1 @@ +-Ywarn-unused:params -Xfatal-warnings diff --git a/test/files/neg/warn-unused-params.scala b/test/files/neg/warn-unused-params.scala new file mode 100644 index 0000000000..b166e8fae6 --- /dev/null +++ b/test/files/neg/warn-unused-params.scala @@ -0,0 +1,69 @@ + +trait InterFace { + /** Call something. */ + def call(a: Int, b: String, c: Double): Int +} + +trait BadAPI extends InterFace { + def f(a: Int, + b: String, // warn + c: Double): Int = { + println(c) + a + } + @deprecated ("no warn in deprecated API", since="yesterday") + def g(a: Int, + b: String, // no warn + c: Double): Int = { + println(c) + a + } + override def call(a: Int, + b: String, // no warn, required by superclass + c: Double): Int = { + println(c) + a + } + + def meth(x: Int) = x + + override def equals(other: Any): Boolean = true // no warn + + def i(implicit s: String) = 42 // yes, warn + + /* + def future(x: Int): Int = { + val y = 42 + val x = y // maybe option to warn only if shadowed + x + } + */ +} + +// mustn't alter warnings in super +trait PoorClient extends BadAPI { + override def meth(x: Int) = ??? // no warn + override def f(a: Int, b: String, c: Double): Int = a + b.toInt + c.toInt +} + +class Unusing(u: Int) { // warn + def f = ??? +} + +class Valuing(val u: Int) // no warn + +class Revaluing(u: Int) { def f = u } // no warn + +case class CaseyKasem(k: Int) // no warn + +case class CaseyAtTheBat(k: Int)(s: String) // warn + +trait Ignorance { + def f(readResolve: Int) = 42 // warn +} + +class Reusing(u: Int) extends Unusing(u) // no warn + +class Main { + def main(args: Array[String]): Unit = println("hello, args") // no warn +} diff --git a/test/files/neg/warn-unused-patvars.check b/test/files/neg/warn-unused-patvars.check new file mode 100644 index 0000000000..2665126a36 --- /dev/null +++ b/test/files/neg/warn-unused-patvars.check @@ -0,0 +1,12 @@ +warn-unused-patvars.scala:9: warning: private val x in trait Boundings is never used + private val x = 42 // warn, sanity check + ^ +warn-unused-patvars.scala:28: warning: local val x in method v is never used + val D(x) = d // warn, fixme + ^ +warn-unused-patvars.scala:32: warning: local val x in method w is never used + val D(x @ _) = d // warn, fixme (valdef pos is different) + ^ +error: No warnings can be incurred under -Xfatal-warnings. +three warnings found +one error found diff --git a/test/files/neg/warn-unused-patvars.flags b/test/files/neg/warn-unused-patvars.flags new file mode 100644 index 0000000000..d5bd86a658 --- /dev/null +++ b/test/files/neg/warn-unused-patvars.flags @@ -0,0 +1 @@ +-Ywarn-unused:-patvars,_ -Xfatal-warnings diff --git a/test/files/neg/warn-unused-patvars.scala b/test/files/neg/warn-unused-patvars.scala new file mode 100644 index 0000000000..3d35dfedd6 --- /dev/null +++ b/test/files/neg/warn-unused-patvars.scala @@ -0,0 +1,53 @@ + +// verify no warning when -Ywarn-unused:-patvars + +case class C(a: Int, b: String, c: Option[String]) +case class D(a: Int) + +trait Boundings { + + private val x = 42 // warn, sanity check + + def c = C(42, "hello", Some("world")) + def d = D(42) + + def f() = { + val C(x, y, Some(z)) = c // no warn + 17 + } + def g() = { + val C(x @ _, y @ _, Some(z @ _)) = c // no warn + 17 + } + def h() = { + val C(x @ _, y @ _, z @ Some(_)) = c // no warn for z? + 17 + } + + def v() = { + val D(x) = d // warn, fixme + 17 + } + def w() = { + val D(x @ _) = d // warn, fixme (valdef pos is different) + 17 + } + +} + +trait Forever { + def f = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // no warn + } yield (i + j) + } + def g = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // no warn + } yield 42 + } +} diff --git a/test/files/neg/warn-unused-privates.check b/test/files/neg/warn-unused-privates.check index 2e93f338bb..2a88d3e6c3 100644 --- a/test/files/neg/warn-unused-privates.check +++ b/test/files/neg/warn-unused-privates.check @@ -1,66 +1,120 @@ 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 +warn-unused-privates.scala:4: warning: private method boop in class Bippy is never used private def boop(x: Int) = x+a+b // warn ^ -warn-unused-privates.scala:6: warning: private val in class Bippy is never used +warn-unused-privates.scala:6: warning: private val MILLIS2 in class Bippy is never used final private val MILLIS2: Int = 1000 // warn ^ -warn-unused-privates.scala:13: warning: private val in object Bippy is never used +warn-unused-privates.scala:13: warning: private val HEY_INSTANCE in object Bippy is never used private val HEY_INSTANCE: Int = 1000 // warn ^ -warn-unused-privates.scala:14: warning: private val in object Bippy is never used +warn-unused-privates.scala:14: warning: private val BOOL in object Bippy is never used private lazy val BOOL: Boolean = true // warn ^ -warn-unused-privates.scala:36: warning: private val in class Boppy is never used +warn-unused-privates.scala:36: warning: private val hummer in class Boppy is never used private val hummer = "def" // warn ^ -warn-unused-privates.scala:43: warning: private var in trait Accessors is never used +warn-unused-privates.scala:43: warning: private var v1 in trait Accessors is never used private var v1: Int = 0 // warn ^ -warn-unused-privates.scala:44: warning: private var in trait Accessors is never used +warn-unused-privates.scala:44: warning: private var v2 in trait Accessors is never used private var v2: Int = 0 // warn, never set ^ -warn-unused-privates.scala:45: warning: private var in trait Accessors is never used +warn-unused-privates.scala:45: warning: private var v3 in trait Accessors is never used private var v3: Int = 0 // warn, never got ^ -warn-unused-privates.scala:57: warning: private default argument in trait DefaultArgs is never used +warn-unused-privates.scala:56: warning: private var s1 in class StableAccessors is never used + private var s1: Int = 0 // warn + ^ +warn-unused-privates.scala:57: warning: private setter of s2 in class StableAccessors is never used + private var s2: Int = 0 // warn, never set + ^ +warn-unused-privates.scala:58: warning: private var s3 in class StableAccessors is never used + private var s3: Int = 0 // warn, never got + ^ +warn-unused-privates.scala:70: 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:57: warning: private default argument in trait DefaultArgs is never used +warn-unused-privates.scala:70: 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:68: warning: local var in method f0 is never used +warn-unused-privates.scala:86: warning: local var x in method f0 is never used var x = 1 // warn ^ -warn-unused-privates.scala:75: warning: local val in method f1 is never used +warn-unused-privates.scala:93: warning: local val b in method f1 is never used val b = new Outer // warn ^ -warn-unused-privates.scala:85: warning: private object in object Types is never used +warn-unused-privates.scala:103: warning: private object Dongo in object Types is never used private object Dongo { def f = this } // warn ^ -warn-unused-privates.scala:95: warning: local object in method l1 is never used +warn-unused-privates.scala:113: warning: local object HiObject in method l1 is never used object HiObject { def f = this } // warn ^ -warn-unused-privates.scala:79: warning: local var x in method f2 is never set - it could be a val +warn-unused-privates.scala:136: warning: private method x_= in class OtherNames is never used + private def x_=(i: Int): Unit = ??? + ^ +warn-unused-privates.scala:137: warning: private method x in class OtherNames is never used + private def x: Int = 42 + ^ +warn-unused-privates.scala:138: warning: private method y_= in class OtherNames is never used + private def y_=(i: Int): Unit = ??? + ^ +warn-unused-privates.scala:153: warning: local val x in method f is never used + val C(x, y, Some(z)) = c // warn + ^ +warn-unused-privates.scala:153: warning: local val y in method f is never used + val C(x, y, Some(z)) = c // warn + ^ +warn-unused-privates.scala:153: warning: local val z in method f is never used + val C(x, y, Some(z)) = c // warn + ^ +warn-unused-privates.scala:161: warning: local val z in method h is never used + val C(x @ _, y @ _, z @ Some(_)) = c // warn for z? + ^ +warn-unused-privates.scala:166: warning: local val x in method v is never used + val D(x) = d // warn + ^ +warn-unused-privates.scala:170: warning: local val x in method w is never used + val D(x @ _) = d // warn, fixme (valdef pos is different) + ^ +warn-unused-privates.scala:97: warning: local var x in method f2 is never set: consider using immutable val var x = 100 // warn about it being a var ^ -warn-unused-privates.scala:86: warning: private class Bar1 in object Types is never used +warn-unused-privates.scala:104: warning: private class Bar1 in object Types is never used private class Bar1 // warn ^ -warn-unused-privates.scala:88: warning: private type Alias1 in object Types is never used +warn-unused-privates.scala:106: warning: private type Alias1 in object Types is never used private type Alias1 = String // warn ^ -warn-unused-privates.scala:96: warning: local class Hi is never used +warn-unused-privates.scala:114: warning: local class Hi is never used class Hi { // warn ^ -warn-unused-privates.scala:100: warning: local class DingDongDoobie is never used +warn-unused-privates.scala:118: warning: local class DingDongDoobie is never used class DingDongDoobie // warn ^ -warn-unused-privates.scala:103: warning: local type OtherThing is never used +warn-unused-privates.scala:121: warning: local type OtherThing is never used type OtherThing = String // warn ^ +warn-unused-privates.scala:216: warning: private class for your eyes only in object not even using companion privates is never used + private implicit class `for your eyes only`(i: Int) { // warn + ^ +warn-unused-privates.scala:201: warning: pattern var z in method f is never used; `z@_' suppresses this warning + case z => "warn" + ^ +warn-unused-privates.scala:208: warning: pattern var z in method f is never used; `z@_' suppresses this warning + case Some(z) => "warn" + ^ +warn-unused-privates.scala:20: warning: parameter value msg0 in class B3 is never used +class B3(msg0: String) extends A("msg") + ^ +warn-unused-privates.scala:136: warning: parameter value i in method x_= is never used + private def x_=(i: Int): Unit = ??? + ^ +warn-unused-privates.scala:138: warning: parameter value i in method y_= is never used + private def y_=(i: Int): Unit = ??? + ^ error: No warnings can be incurred under -Xfatal-warnings. -21 warnings found +39 warnings found one error found diff --git a/test/files/neg/warn-unused-privates.scala b/test/files/neg/warn-unused-privates.scala index 2eda280d40..f7640927fb 100644 --- a/test/files/neg/warn-unused-privates.scala +++ b/test/files/neg/warn-unused-privates.scala @@ -52,6 +52,19 @@ trait Accessors { } } +class StableAccessors { + private var s1: Int = 0 // warn + private var s2: Int = 0 // warn, never set + private var s3: Int = 0 // warn, never got + private var s4: Int = 0 // no warn + + def bippy(): Int = { + s3 = 5 + s4 = 6 + s2 + s4 + } +} + 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 @@ -59,6 +72,11 @@ trait DefaultArgs { def boppy() = bippy(5, 100, 200) } +/* SI-7707 Both usages warn default arg because using PrivateRyan.apply, not new. +case class PrivateRyan private (ryan: Int = 42) { def f = PrivateRyan() } +object PrivateRyan { def f = PrivateRyan() } +*/ + class Outer { class Inner } @@ -104,3 +122,105 @@ object Types { (new Bippy): Something } } + +trait Underwarn { + def f(): Seq[Int] + + def g() = { + val Seq(_, _) = f() // no warn + true + } +} + +class OtherNames { + private def x_=(i: Int): Unit = ??? + private def x: Int = 42 + private def y_=(i: Int): Unit = ??? + private def y: Int = 42 + + def f = y +} + +case class C(a: Int, b: String, c: Option[String]) +case class D(a: Int) + +trait Boundings { + + def c = C(42, "hello", Some("world")) + def d = D(42) + + def f() = { + val C(x, y, Some(z)) = c // warn + 17 + } + def g() = { + val C(x @ _, y @ _, Some(z @ _)) = c // no warn + 17 + } + def h() = { + val C(x @ _, y @ _, z @ Some(_)) = c // warn for z? + 17 + } + + def v() = { + val D(x) = d // warn + 17 + } + def w() = { + val D(x @ _) = d // warn, fixme (valdef pos is different) + 17 + } + +} + +trait Forever { + def f = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // no warn + } yield (i + j) + } + def g = { + val t = Option((17, 42)) + for { + ns <- t + (i, j) = ns // warn, fixme + } yield 42 // val emitted only if needed, hence nothing unused + } +} + +trait Ignorance { + private val readResolve = 42 // ignore +} + +trait CaseyKasem { + def f = 42 match { + case x if x < 25 => "no warn" + case y if toString.nonEmpty => "no warn" + y + case z => "warn" + } +} +trait CaseyAtTheBat { + def f = Option(42) match { + case Some(x) if x < 25 => "no warn" + case Some(y @ _) if toString.nonEmpty => "no warn" + case Some(z) => "warn" + case None => "no warn" + } +} + +class `not even using companion privates` + +object `not even using companion privates` { + private implicit class `for your eyes only`(i: Int) { // warn + def f = i + } +} + +class `no warn in patmat anonfun isDefinedAt` { + def f(pf: PartialFunction[String, Int]) = pf("42") + def g = f { + case s => s.length // no warn (used to warn case s => true in isDefinedAt) + } +} diff --git a/test/files/pos/t6091.scala b/test/files/pos/t6091.scala index 72e663ec3b..0318640e7b 100644 --- a/test/files/pos/t6091.scala +++ b/test/files/pos/t6091.scala @@ -1,6 +1,6 @@ -object Foo { def eq(x:Int) = x } +object Foo { def eq(x: Int) = x } -class X { def ==(other: String) = true } +class X { def ==(other: String) = other.nonEmpty } object Test { def main(args: Array[String]): Unit = { diff --git a/test/files/pos/t8013.flags b/test/files/pos/t8013.flags index 3955bb6710..219723cec9 100644 --- a/test/files/pos/t8013.flags +++ b/test/files/pos/t8013.flags @@ -1 +1 @@ --Xfatal-warnings -Xlint:-infer-any,_ +-Xfatal-warnings -Xlint:missing-interpolator diff --git a/test/files/pos/t8040.flags b/test/files/pos/t8040.flags new file mode 100644 index 0000000000..3126c059f0 --- /dev/null +++ b/test/files/pos/t8040.flags @@ -0,0 +1 @@ +-Xfatal-warnings -Ywarn-unused:params diff --git a/test/files/pos/t8040.scala b/test/files/pos/t8040.scala new file mode 100644 index 0000000000..3e01014ab4 --- /dev/null +++ b/test/files/pos/t8040.scala @@ -0,0 +1,13 @@ + +object Test { + implicit class C(val sc: StringContext) { // no warn unused sc + def c(args: Any*): String = "c?" + args.mkString(",") // would warn unused args + } + + def f(implicit x: DummyImplicit) = 42 // no warn DummyImplicit + + + def f(x: Int)(y: Int = 1) = x + y // no warn default getter + + def g(@deprecated("","") x: Int) = 42 // no warn deprecated +} |