Skip to content

Commit c70fa68

Browse files
committed
Fixes for -Wunused:params
- Also add a (adapted) Scala 2 tests suits
1 parent bfa20a1 commit c70fa68

File tree

2 files changed

+154
-18
lines changed

2 files changed

+154
-18
lines changed

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import dotty.tools.dotc.ast.untpd.ImportSelector
77
import dotty.tools.dotc.config.ScalaSettings
88
import dotty.tools.dotc.core.Contexts.*
99
import dotty.tools.dotc.core.Decorators.{em, i}
10-
import dotty.tools.dotc.core.Flags.{Given, Implicit, GivenOrImplicit, Param, Private, SelfName, Synthetic}
10+
import dotty.tools.dotc.core.Flags._
1111
import dotty.tools.dotc.core.Phases.Phase
1212
import dotty.tools.dotc.core.StdNames
1313
import dotty.tools.dotc.report
@@ -25,6 +25,7 @@ import dotty.tools.dotc.transform.MegaPhase.MiniPhase
2525
import dotty.tools.dotc.core.Annotations
2626
import dotty.tools.dotc.core.Definitions
2727
import dotty.tools.dotc.core.Types.ConstantType
28+
import dotty.tools.dotc.core.NameKinds.WildcardParamName
2829

2930

3031

@@ -108,8 +109,9 @@ class CheckUnused extends MiniPhase:
108109

109110
override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context =
110111
_key.unusedDataApply{ ud =>
111-
ud.registerDef(tree)
112-
ud.addIgnoredUsage(tree.symbol)
112+
if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2)
113+
ud.registerDef(tree)
114+
ud.addIgnoredUsage(tree.symbol)
113115
}
114116

115117
override def prepareForBind(tree: tpd.Bind)(using Context): Context =
@@ -313,7 +315,7 @@ object CheckUnused:
313315
/** Register all annotations of this symbol's denotation */
314316
def registerUsedAnnotation(sym: Symbol)(using Context): Unit =
315317
val annotSym = sym.denot.annotations.map(_.symbol)
316-
registerUsed(annotSym)
318+
annotSym.foreach(s => registerUsed(s, None))
317319

318320
/**
319321
* Register a found (used) symbol along with its name
@@ -327,13 +329,6 @@ object CheckUnused:
327329
if sym.isConstructor && sym.exists then
328330
registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class
329331

330-
/** Register a list of found (used) symbols */
331-
def registerUsed(syms: Seq[Symbol])(using Context): Unit =
332-
usedInScope.top ++=
333-
syms
334-
.filterNot(s => isConstructorOfSynth(s) || doNotRegister(s))
335-
.map(s => (s, s.isAccessibleAsIdent, None))
336-
337332
/** Register a symbol that should be ignored */
338333
def addIgnoredUsage(sym: Symbol)(using Context): Unit =
339334
doNotRegister += sym
@@ -359,8 +354,8 @@ object CheckUnused:
359354
def registerDef(memDef: tpd.MemberDef)(using Context): Unit =
360355
// register the annotations for usage
361356
registerUsedAnnotation(memDef.symbol)
362-
if !memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) then
363-
if memDef.symbol.is(Param) && !isSyntheticMainParam(memDef.symbol) && !memDef.symbol.ownerIsTrivial then
357+
if memDef.isValidMemberDef then
358+
if memDef.isValidParam then
364359
if memDef.symbol.isOneOf(GivenOrImplicit) then
365360
implicitParamInScope += memDef
366361
else
@@ -526,7 +521,7 @@ object CheckUnused:
526521

527522
extension (sym: Symbol)
528523
/** is accessible without import in current context */
529-
def isAccessibleAsIdent(using Context): Boolean =
524+
private def isAccessibleAsIdent(using Context): Boolean =
530525
sym.exists &&
531526
ctx.outersIterator.exists{ c =>
532527
c.owner == sym.owner
@@ -536,7 +531,7 @@ object CheckUnused:
536531
}
537532

538533
/** Given an import and accessibility, return an option of selector that match import<->symbol */
539-
def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] =
534+
private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] =
540535
val tpd.Import(qual, sels) = imp
541536
val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).contains(sym)
542537
def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true))
@@ -547,24 +542,55 @@ object CheckUnused:
547542
None
548543

549544
/** Annotated with @unused */
550-
def isUnusedAnnot(using Context): Boolean =
545+
private def isUnusedAnnot(using Context): Boolean =
551546
sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot)
552547

553-
def ownerIsTrivial(using Context): Boolean =
548+
private def shouldNotReportParamOwner(using Context): Boolean =
549+
if sym.exists then
550+
val owner = sym.owner
551+
trivialDefs(owner) ||
552+
owner.is(Flags.Override) ||
553+
owner.isPrimaryConstructor ||
554+
owner.annotations.exists (
555+
_.symbol == ctx.definitions.DeprecatedAnnot
556+
)
557+
else
558+
false
559+
560+
private def ownerIsTrivial(using Context): Boolean =
554561
sym.exists && trivialDefs(sym.owner)
555562

556563
extension (defdef: tpd.DefDef)
557564
// so trivial that it never consumes params
558565
private def isTrivial(using Context): Boolean =
559566
val rhs = defdef.rhs
560-
rhs.symbol == ctx.definitions.Predef_undefined || rhs.tpe =:= ctx.definitions.NothingType || (rhs match {
567+
rhs.symbol == ctx.definitions.Predef_undefined ||
568+
rhs.tpe =:= ctx.definitions.NothingType ||
569+
defdef.symbol.is(Deferred) ||
570+
(rhs match {
561571
case _: tpd.Literal => true
562572
case _ => rhs.tpe.isInstanceOf[ConstantType]
563573
})
564574
def registerTrivial(using Context): Unit =
565575
if defdef.isTrivial then
566576
trivialDefs += defdef.symbol
567577

578+
extension (memDef: tpd.MemberDef)
579+
private def isValidMemberDef(using Context): Boolean =
580+
!memDef.symbol.isUnusedAnnot && !memDef.symbol.isAllOf(Flags.AccessorCreationFlags) && !memDef.name.isWildcard
581+
582+
private def isValidParam(using Context): Boolean =
583+
val sym = memDef.symbol
584+
(sym.is(Param) || sym.isAllOf(PrivateParamAccessor)) &&
585+
!isSyntheticMainParam(sym) &&
586+
!sym.shouldNotReportParamOwner
587+
588+
589+
extension (thisName: Name)
590+
private def isWildcard: Boolean =
591+
thisName == StdNames.nme.WILDCARD || thisName.is(WildcardParamName)
592+
593+
568594
end UnusedData
569595

570596
private object UnusedData:
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// scalac: -Wunused:params
2+
//
3+
4+
import Answers._
5+
6+
trait InterFace {
7+
/** Call something. */
8+
def call(a: Int, b: String, c: Double): Int
9+
}
10+
11+
trait BadAPI extends InterFace {
12+
def f(a: Int,
13+
b: String, // error
14+
c: Double): Int = {
15+
println(c)
16+
a
17+
}
18+
@deprecated("no warn in deprecated API", since="yesterday")
19+
def g(a: Int,
20+
b: String, // OK
21+
c: Double): Int = {
22+
println(c)
23+
a
24+
}
25+
override def call(a: Int,
26+
b: String, // OK
27+
c: Double): Int = {
28+
println(c)
29+
a
30+
}
31+
32+
def meth(x: Int) = x
33+
34+
override def equals(other: Any): Boolean = true // OK
35+
36+
def i(implicit s: String) = answer // error
37+
38+
/*
39+
def future(x: Int): Int = {
40+
val y = 42
41+
val x = y // maybe option to warn only if shadowed
42+
x
43+
}
44+
*/
45+
}
46+
47+
// mustn't alter warnings in super
48+
trait PoorClient extends BadAPI {
49+
override def meth(x: Int) = ??? // OK
50+
override def f(a: Int, b: String, c: Double): Int = a + b.toInt + c.toInt
51+
}
52+
53+
class Unusing(u: Int) { // error
54+
def f = ???
55+
}
56+
57+
class Valuing(val u: Int) // OK
58+
59+
class Revaluing(u: Int) { def f = u } // OK
60+
61+
case class CaseyKasem(k: Int) // OK
62+
63+
case class CaseyAtTheBat(k: Int)(s: String) // error
64+
65+
trait Ignorance {
66+
def f(readResolve: Int) = answer // error
67+
}
68+
69+
class Reusing(u: Int) extends Unusing(u) // OK
70+
71+
// TODO: check
72+
// class Main {
73+
// def main(args: Array[String]): Unit = println("hello, args") // OK
74+
// }
75+
76+
trait Unimplementation {
77+
def f(u: Int): Int = ??? // OK
78+
}
79+
80+
trait DumbStuff {
81+
def f(implicit dummy: DummyImplicit) = answer // todo // error
82+
def g(dummy: DummyImplicit) = answer // error
83+
}
84+
trait Proofs {
85+
def f[A, B](implicit ev: A =:= B) = answer // todo // error
86+
def g[A, B](implicit ev: A <:< B) = answer // todo // error
87+
def f2[A, B](ev: A =:= B) = answer // error
88+
def g2[A, B](ev: A <:< B) = answer // error
89+
}
90+
91+
trait Anonymous {
92+
def f = (i: Int) => answer // error
93+
94+
def f1 = (_: Int) => answer // OK
95+
96+
def f2: Int => Int = _ + 1 // OK
97+
98+
def g = for (i <- List(1)) yield answer // error
99+
}
100+
trait Context[A]
101+
trait Implicits {
102+
def f[A](implicit ctx: Context[A]) = answer // error
103+
def g[A: Context] = answer // error
104+
}
105+
class Bound[A: Context] // error
106+
object Answers {
107+
def answer: Int = 42
108+
}
109+
110+
val a$1 = 2

0 commit comments

Comments
 (0)