Skip to content

Commit 892621b

Browse files
committed
Annotation usage, allow unsed Self, add tests
- Register annotation usage, so (i.e. @tailrec) is not reported - Allow unsed self name (`self:A =>` members) - Add tests suggested in the PR discussion thread
1 parent 8ac97b3 commit 892621b

File tree

3 files changed

+105
-34
lines changed

3 files changed

+105
-34
lines changed

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

Lines changed: 48 additions & 34 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, GivenVal, Param, Private}
10+
import dotty.tools.dotc.core.Flags.{Given, GivenVal, Param, Private, SelfName}
1111
import dotty.tools.dotc.core.Phases.Phase
1212
import dotty.tools.dotc.core.StdNames
1313
import dotty.tools.dotc.report
@@ -55,29 +55,32 @@ class CheckUnused extends Phase:
5555
import tpd._
5656
import UnusedData.ScopeType
5757

58-
override def traverse(tree: tpd.Tree)(using Context): Unit = tree match
59-
case imp:tpd.Import =>
60-
ctx.property(_key).foreach(_.registerImport(imp))
61-
case ident: Ident =>
62-
ctx.property(_key).foreach(_.registerUsed(ident.symbol))
63-
traverseChildren(tree)
64-
case sel: Select =>
65-
ctx.property(_key).foreach(_.registerUsed(sel.symbol))
66-
traverseChildren(tree)
67-
case _: (tpd.Block | tpd.Template) =>
68-
ctx.property(_key).foreach { ud =>
69-
ud.inNewScope(ScopeType.fromTree(tree))(traverseChildren(tree))
70-
}
71-
case t:tpd.ValDef =>
72-
ctx.property(_key).foreach(_.registerDef(t))
73-
traverseChildren(tree)
74-
case t:tpd.DefDef =>
75-
ctx.property(_key).foreach(_.registerDef(t))
76-
traverseChildren(tree)
77-
case t: tpd.Bind =>
78-
ctx.property(_key).foreach(_.registerPatVar(t))
79-
traverseChildren(tree)
80-
case _ => traverseChildren(tree)
58+
override def traverse(tree: tpd.Tree)(using Context): Unit =
59+
if tree.isDef then // register the annotations for usage
60+
ctx.property(_key).foreach(_.registerUsedAnnotation(tree.symbol))
61+
tree match
62+
case imp:tpd.Import =>
63+
ctx.property(_key).foreach(_.registerImport(imp))
64+
case ident: Ident =>
65+
ctx.property(_key).foreach(_.registerUsed(ident.symbol))
66+
traverseChildren(tree)
67+
case sel: Select =>
68+
ctx.property(_key).foreach(_.registerUsed(sel.symbol))
69+
traverseChildren(tree)
70+
case _: (tpd.Block | tpd.Template) =>
71+
ctx.property(_key).foreach { ud =>
72+
ud.inNewScope(ScopeType.fromTree(tree))(traverseChildren(tree))
73+
}
74+
case t:tpd.ValDef =>
75+
ctx.property(_key).foreach(_.registerDef(t))
76+
traverseChildren(tree)
77+
case t:tpd.DefDef =>
78+
ctx.property(_key).foreach(_.registerDef(t))
79+
traverseChildren(tree)
80+
case t: tpd.Bind =>
81+
ctx.property(_key).foreach(_.registerPatVar(t))
82+
traverseChildren(tree)
83+
case _ => traverseChildren(tree)
8184

8285
}
8386

@@ -162,10 +165,21 @@ object CheckUnused:
162165
case untpd.Ident(name) => name == StdNames.nme.WILDCARD
163166
case _ => false
164167

165-
/** Register the id of a found (used) symbol */
166-
def registerUsed(id: Symbol): Unit =
167-
usedImports.top += id
168-
usedDef += id
168+
169+
/** Register all annotations of this symbol's denotation */
170+
def registerUsedAnnotation(sym: Symbol)(using Context): Unit =
171+
val annotSym = sym.denot.annotations.map(_.symbol)
172+
registerUsed(annotSym)
173+
174+
/** Register a found (used) symbol */
175+
def registerUsed(sym: Symbol): Unit =
176+
usedImports.top += sym
177+
usedDef += sym
178+
179+
/** Register a list of found (used) symbols */
180+
def registerUsed(sym: Seq[Symbol]): Unit =
181+
usedImports.top ++= sym
182+
usedDef ++= sym
169183

170184
/** Register an import */
171185
def registerImport(imp: tpd.Import)(using Context): Unit =
@@ -183,9 +197,9 @@ object CheckUnused:
183197
val typeSymbol = tree.tpe.member(s.name.toTypeName).symbol
184198
List(termSymbol -> s, typeSymbol -> s)
185199
}
186-
entries.foreach{(id, sel) =>
187-
map.get(id) match
188-
case None => map.put(id, ListBuffer(sel))
200+
entries.foreach{(sym, sel) =>
201+
map.get(sym) match
202+
case None => map.put(sym, ListBuffer(sel))
189203
case Some(value) => value += sel
190204
}
191205

@@ -197,7 +211,7 @@ object CheckUnused:
197211
explicitParamInScope.top += valOrDef
198212
else if currScopeType == ScopeType.Local then
199213
localDefInScope.top += valOrDef
200-
else if currScopeType == ScopeType.Template && valOrDef.symbol.is(Private) then
214+
else if currScopeType == ScopeType.Template && valOrDef.symbol.is(Private, butNot = SelfName) then
201215
privateDefInScope.top += valOrDef
202216

203217
def registerPatVar(patvar: tpd.Bind)(using Context): Unit =
@@ -227,8 +241,8 @@ object CheckUnused:
227241
def popScopeImport(): Unit =
228242
val usedImp = MutSet[ImportSelector]()
229243
val poppedImp = impInScope.pop()
230-
val notDefined = usedImports.pop.filter{id =>
231-
poppedImp.remove(id) match
244+
val notDefined = usedImports.pop.filter{sym =>
245+
poppedImp.remove(sym) match
232246
case None => true
233247
case Some(value) =>
234248
usedImp.addAll(value)

tests/neg-custom-args/fatal-warnings/i15503a.scala

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,58 @@ object SomeGivenImports:
9999
given Int = 0
100100
given String = "foo"
101101

102+
/* BEGIN : Check on packages*/
103+
package p {
104+
class C
105+
}
106+
107+
package p {
108+
import p._ // error
109+
package q {
110+
class U {
111+
def f = new C
112+
}
113+
}
114+
}
115+
/* END : Check on packages*/
116+
117+
/* BEGIN : tests on meta-language features */
118+
object TestGivenCoversionScala2:
119+
/* note: scala3 Conversion[U,T] do not require an import */
120+
import language.implicitConversions // OK
121+
122+
implicit def doubleToInt(d:Double):Int = d.toInt
123+
124+
def idInt(i:Int):Int = i
125+
val someInt = idInt(1.0)
126+
127+
object TestTailrecImport:
128+
import annotation.tailrec // OK
129+
@tailrec
130+
def fac(x:Int, acc:Int = 1): Int =
131+
if x == 0 then acc else fac(x - 1, acc * x)
132+
/* END : tests on meta-language features */
133+
134+
/* BEGIN : tests on given import order */
135+
object GivenImportOrderAtoB:
136+
class X
137+
class Y extends X
138+
object A { implicit val x: X = new X }
139+
object B { implicit val y: Y = new Y }
140+
class C {
141+
import A._ // error
142+
import B._
143+
def t = implicitly[X]
144+
}
145+
146+
object GivenImportOrderBtoA:
147+
class X
148+
class Y extends X
149+
object A { implicit val x: X = new X }
150+
object B { implicit val y: Y = new Y }
151+
class C {
152+
import B._
153+
import A._ // error
154+
def t = implicitly[X]
155+
}
156+
/* END : tests on given import order */

tests/neg-custom-args/fatal-warnings/i15503c.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// scalac: -Wunused:privates
2+
trait C
23
class A:
4+
self: C => // OK
35
class B:
46
private[A] val a = 1 // OK
57
private[B] val b = 1 // OK

0 commit comments

Comments
 (0)