@@ -17,7 +17,6 @@ import dotty.tools.dotc.util.Property
17
17
import dotty .tools .dotc .transform .CheckUnused .UnusedData .UnusedResult
18
18
import dotty .tools .dotc .core .Mode
19
19
20
- import scala .collection .mutable
21
20
22
21
23
22
/**
@@ -56,31 +55,33 @@ class CheckUnused extends Phase:
56
55
import UnusedData .ScopeType
57
56
58
57
override def traverse (tree : tpd.Tree )(using Context ): Unit =
58
+ val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx
59
59
if tree.isDef then // register the annotations for usage
60
60
ctx.property(_key).foreach(_.registerUsedAnnotation(tree.symbol))
61
61
tree match
62
62
case imp: tpd.Import =>
63
63
ctx.property(_key).foreach(_.registerImport(imp))
64
64
case ident : Ident =>
65
65
ctx.property(_key).foreach(_.registerUsed(ident.symbol))
66
- traverseChildren(tree)
66
+ traverseChildren(tree)( using newCtx)
67
67
case sel : Select =>
68
68
ctx.property(_key).foreach(_.registerUsed(sel.symbol))
69
- traverseChildren(tree)
69
+ traverseChildren(tree)( using newCtx)
70
70
case _ : (tpd.Block | tpd.Template ) =>
71
71
ctx.property(_key).foreach { ud =>
72
- ud.inNewScope(ScopeType .fromTree(tree))(traverseChildren(tree))
72
+ ud.inNewScope(ScopeType .fromTree(tree))(traverseChildren(tree)( using newCtx) )
73
73
}
74
74
case t: tpd.ValDef =>
75
75
ctx.property(_key).foreach(_.registerDef(t))
76
- traverseChildren(tree)
76
+ traverseChildren(tree)( using newCtx)
77
77
case t: tpd.DefDef =>
78
78
ctx.property(_key).foreach(_.registerDef(t))
79
- traverseChildren(tree)
79
+ traverseChildren(tree)( using newCtx)
80
80
case t : tpd.Bind =>
81
81
ctx.property(_key).foreach(_.registerPatVar(t))
82
- traverseChildren(tree)
83
- case _ => traverseChildren(tree)
82
+ traverseChildren(tree)(using newCtx)
83
+ case _ =>
84
+ traverseChildren(tree)(using newCtx)
84
85
85
86
}
86
87
@@ -123,29 +124,29 @@ object CheckUnused:
123
124
* - usage
124
125
*/
125
126
private class UnusedData :
126
- import collection .mutable .{Set => MutSet , Map => MutMap , Stack => MutStack , ListBuffer }
127
+ import collection .mutable .{Set => MutSet , Map => MutMap , Stack => MutStack }
127
128
import dotty .tools .dotc .core .Symbols .Symbol
128
129
import UnusedData .ScopeType
129
130
130
131
var currScopeType : ScopeType = ScopeType .Other
131
132
132
133
/* IMPORTS */
133
- private val impInScope = MutStack (MutMap [Symbol , ListBuffer [ImportSelector ]]())
134
+ private val impInScope = MutStack (MutSet [tpd.Import ]())
135
+ private val usedInScope = MutStack (MutSet [(Symbol ,Boolean )]())
134
136
private val unusedImport = MutSet [ImportSelector ]()
135
- private val usedImports = MutStack (MutSet [Symbol ]())
136
137
137
138
/* LOCAL DEF OR VAL / Private Def or Val / Pattern variables */
138
- private val localDefInScope = MutStack ( MutSet [tpd.ValOrDefDef ]() )
139
- private val privateDefInScope = MutStack ( MutSet [tpd.ValOrDefDef ]() )
140
- private val explicitParamInScope = MutStack ( MutSet [tpd.ValOrDefDef ]() )
141
- private val implicitParamInScope = MutStack ( MutSet [tpd.ValOrDefDef ]() )
142
- private val patVarsInScope = MutStack ( MutSet [tpd.Bind ]() )
143
-
144
- private val unusedLocalDef = ListBuffer [tpd.ValOrDefDef ]()
145
- private val unusedPrivateDef = ListBuffer [tpd.ValOrDefDef ]()
146
- private val unusedExplicitParams = ListBuffer [tpd.ValOrDefDef ]()
147
- private val unusedImplicitParams = ListBuffer [tpd.ValOrDefDef ]()
148
- private val unusedPatVars = ListBuffer [tpd.Bind ]()
139
+ private val localDefInScope = MutSet [tpd.ValOrDefDef ]()
140
+ private val privateDefInScope = MutSet [tpd.ValOrDefDef ]()
141
+ private val explicitParamInScope = MutSet [tpd.ValOrDefDef ]()
142
+ private val implicitParamInScope = MutSet [tpd.ValOrDefDef ]()
143
+ private val patVarsInScope = MutSet [tpd.Bind ]()
144
+
145
+ private val unusedLocalDef = MutSet [tpd.ValOrDefDef ]()
146
+ private val unusedPrivateDef = MutSet [tpd.ValOrDefDef ]()
147
+ private val unusedExplicitParams = MutSet [tpd.ValOrDefDef ]()
148
+ private val unusedImplicitParams = MutSet [tpd.ValOrDefDef ]()
149
+ private val unusedPatVars = MutSet [tpd.Bind ]()
149
150
150
151
private val usedDef = MutSet [Symbol ]()
151
152
@@ -161,122 +162,66 @@ object CheckUnused:
161
162
popScope()
162
163
currScopeType = prev
163
164
164
- private def isImportExclusion (sel : ImportSelector ): Boolean = sel.renamed match
165
- case untpd.Ident (name) => name == StdNames .nme.WILDCARD
166
- case _ => false
167
-
168
-
169
165
/** Register all annotations of this symbol's denotation */
170
166
def registerUsedAnnotation (sym : Symbol )(using Context ): Unit =
171
167
val annotSym = sym.denot.annotations.map(_.symbol)
172
168
registerUsed(annotSym)
173
169
174
170
/** Register a found (used) symbol */
175
- def registerUsed (sym : Symbol ): Unit =
176
- usedImports .top += sym
171
+ def registerUsed (sym : Symbol )( using Context ) : Unit =
172
+ usedInScope .top += sym -> sym.isAccessibleAsIdent
177
173
usedDef += sym
178
174
179
175
/** Register a list of found (used) symbols */
180
- def registerUsed (sym : Seq [Symbol ]): Unit =
181
- usedImports .top ++= sym
182
- usedDef ++= sym
176
+ def registerUsed (syms : Seq [Symbol ])( using Context ): Unit =
177
+ usedInScope .top ++= syms.map(s => s -> s.isAccessibleAsIdent)
178
+ usedDef ++= syms
183
179
184
180
/** Register an import */
185
181
def registerImport (imp : tpd.Import )(using Context ): Unit =
186
- val tpd .Import (tree, sels) = imp
187
- val map = impInScope.top
188
- val entries = sels.flatMap{ s =>
189
- if isImportExclusion(s) then
190
- Nil // ignore exclusion import
191
- else if s.isWildcard then
192
- tree.tpe.allMembers
193
- .filter(m => m.symbol.is(Given ) == s.isGiven) // given imports
194
- .map(_.symbol -> s)
195
- else
196
- val termSymbol = tree.tpe.member(s.name.toTermName).symbol
197
- val typeSymbol = tree.tpe.member(s.name.toTypeName).symbol
198
- List (termSymbol -> s, typeSymbol -> s)
199
- }
200
- entries.foreach{(sym, sel) =>
201
- map.get(sym) match
202
- case None => map.put(sym, ListBuffer (sel))
203
- case Some (value) => value += sel
204
- }
182
+ impInScope.top += imp
183
+ unusedImport ++= imp.selectors.filter(s => ! isImportExclusion(s))
205
184
206
185
def registerDef (valOrDef : tpd.ValOrDefDef )(using Context ): Unit =
207
186
if valOrDef.symbol.is(Param ) then
208
187
if valOrDef.symbol.is(Given ) then
209
- implicitParamInScope.top += valOrDef
188
+ implicitParamInScope += valOrDef
210
189
else
211
- explicitParamInScope.top += valOrDef
190
+ explicitParamInScope += valOrDef
212
191
else if currScopeType == ScopeType .Local then
213
- localDefInScope.top += valOrDef
192
+ localDefInScope += valOrDef
214
193
else if currScopeType == ScopeType .Template && valOrDef.symbol.is(Private , butNot = SelfName ) then
215
- privateDefInScope.top += valOrDef
194
+ privateDefInScope += valOrDef
216
195
217
196
def registerPatVar (patvar : tpd.Bind )(using Context ): Unit =
218
- patVarsInScope.top += patvar
197
+ patVarsInScope += patvar
219
198
220
199
/** enter a new scope */
221
200
def pushScope (): Unit =
222
201
// unused imports :
223
- usedImports.push(MutSet ())
224
- impInScope.push(MutMap ())
225
- // local and private defs :
226
- localDefInScope.push(MutSet ())
227
- explicitParamInScope.push(MutSet ())
228
- implicitParamInScope.push(MutSet ())
229
- privateDefInScope.push(MutSet ())
230
- patVarsInScope.push(MutSet ())
202
+ impInScope.push(MutSet ())
203
+ usedInScope.push(MutSet ())
231
204
232
205
/** leave the current scope */
233
206
def popScope ()(using Context ): Unit =
234
207
popScopeImport()
235
- popScopeLocalDef()
236
- popScopeExplicitParam()
237
- popScopeImplicitParam()
238
- popScopePrivateDef()
239
- popScopePatVars()
240
-
241
- def popScopeImport (): Unit =
242
- val usedImp = MutSet [ImportSelector ]()
243
- val poppedImp = impInScope.pop()
244
- val notDefined = usedImports.pop.filter{sym =>
245
- poppedImp.remove(sym) match
246
- case None => true
247
- case Some (value) =>
248
- usedImp.addAll(value)
249
- false
250
- }
251
- if usedImports.nonEmpty then
252
- usedImports.top.addAll(notDefined)
253
-
254
- poppedImp.values.flatten.foreach{ sel =>
255
- // If **any** of the entities used by the import is used,
256
- // do not add to the `unused` Set
257
- if ! usedImp(sel) then
258
- unusedImport += sel
259
- }
260
-
261
- def popScopeLocalDef ()(using Context ): Unit =
262
- val unused = localDefInScope.pop().filterInPlace(d => ! usedDef(d.symbol))
263
- unusedLocalDef ++= unused
264
208
265
- def popScopeExplicitParam ()(using Context ): Unit =
266
- val unused = explicitParamInScope.pop().filterInPlace(d => ! usedDef(d.symbol))
267
- unusedExplicitParams ++= unused
268
-
269
- def popScopeImplicitParam ()(using Context ): Unit =
270
- val unused = implicitParamInScope.pop().filterInPlace(d => ! usedDef(d.symbol))
271
- unusedImplicitParams ++= unused
272
-
273
- def popScopePrivateDef ()(using Context ): Unit =
274
- val unused = privateDefInScope.pop().filterInPlace(d => ! usedDef(d.symbol))
275
- unusedPrivateDef ++= unused
276
-
277
- def popScopePatVars ()(using Context ): Unit =
278
- val unused = patVarsInScope.pop().filterInPlace(d => ! usedDef(d.symbol))
279
- unusedPatVars ++= unused
209
+ def popScopeImport ()(using Context ): Unit =
210
+ val used = usedInScope.pop().toSet
211
+ val imports = impInScope.pop().toSet
212
+ val kept = used.filter { t =>
213
+ val (sym, isAccessible) = t
214
+ ! imports.exists { imp =>
215
+ sym.isInImport(imp, isAccessible) match
216
+ case None => false
217
+ case Some (sel) =>
218
+ unusedImport -= sel
219
+ true
220
+ }
221
+ }
222
+ if usedInScope.nonEmpty then
223
+ usedInScope.top ++= kept
224
+ usedDef ++= used.map(_._1)
280
225
281
226
/**
282
227
* Leave the scope and return a `List` of unused `ImportSelector`s
@@ -292,27 +237,27 @@ object CheckUnused:
292
237
Nil
293
238
val sortedLocalDefs =
294
239
if ctx.settings.WunusedHas .locals then
295
- unusedLocalDef .map(d => d.namePos -> WarnTypes .LocalDefs ).toList
240
+ localDefInScope.filter(d => ! usedDef(d.symbol)) .map(d => d.namePos -> WarnTypes .LocalDefs ).toList
296
241
else
297
242
Nil
298
243
val sortedExplicitParams =
299
244
if ctx.settings.WunusedHas .explicits then
300
- unusedExplicitParams .map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
245
+ explicitParamInScope.filter(d => ! usedDef(d.symbol)) .map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
301
246
else
302
247
Nil
303
248
val sortedImplicitParams =
304
249
if ctx.settings.WunusedHas .implicits then
305
- unusedImplicitParams .map(d => d.namePos -> WarnTypes .ImplicitParams ).toList
250
+ implicitParamInScope.filter(d => ! usedDef(d.symbol)) .map(d => d.namePos -> WarnTypes .ImplicitParams ).toList
306
251
else
307
252
Nil
308
253
val sortedPrivateDefs =
309
254
if ctx.settings.WunusedHas .privates then
310
- unusedPrivateDef .map(d => d.namePos -> WarnTypes .PrivateMembers ).toList
255
+ privateDefInScope.filter(d => ! usedDef(d.symbol)) .map(d => d.namePos -> WarnTypes .PrivateMembers ).toList
311
256
else
312
257
Nil
313
258
val sortedPatVars =
314
259
if ctx.settings.WunusedHas .patvars then
315
- unusedPatVars .map(d => d.namePos -> WarnTypes .PatVars ).toList
260
+ patVarsInScope.filter(d => ! usedDef(d.symbol)) .map(d => d.namePos -> WarnTypes .PatVars ).toList
316
261
else
317
262
Nil
318
263
val warnings = List (sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s =>
@@ -321,6 +266,31 @@ object CheckUnused:
321
266
}
322
267
UnusedResult (warnings, Nil )
323
268
269
+ // ============================ HELPERS ====================================
270
+
271
+ private def isImportExclusion (sel : ImportSelector ): Boolean = sel.renamed match
272
+ case untpd.Ident (name) => name == StdNames .nme.WILDCARD
273
+ case _ => false
274
+ extension (sym : Symbol )
275
+ def isAccessibleAsIdent (using Context ): Boolean =
276
+ sym.exists &&
277
+ ctx.outersIterator.exists{ c =>
278
+ c.owner == sym.owner
279
+ || sym.owner.isClass && c.owner.isClass
280
+ && c.owner.thisType.baseClasses.contains(sym.owner)
281
+ && c.owner.thisType.member(sym.name).alternatives.contains(sym)
282
+ }
283
+ def isInImport (imp : tpd.Import , isAccessible : Boolean )(using Context ): Option [ImportSelector ] =
284
+ val tpd .Import (qual, sels) = imp
285
+ val sameQualType =
286
+ qual.tpe.member(sym.name.toTermName).symbol == sym ||
287
+ qual.tpe.member(sym.name.toTypeName).symbol == sym
288
+ def selector = sels.find(sel => sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name)
289
+ def wildcard = sels.find(sel => sel.isWildcard && (sym.is(Given ) == sel.isGiven))
290
+ if sameQualType && ! isAccessible then
291
+ selector.orElse(wildcard)
292
+ else
293
+ None
324
294
end UnusedData
325
295
326
296
object UnusedData :
0 commit comments