@@ -27,6 +27,7 @@ import dotty.tools.dotc.core.Symbols.Symbol
27
27
import dotty .tools .dotc .core .StdNames .nme
28
28
import scala .math .Ordering
29
29
30
+
30
31
/**
31
32
* A compiler phase that checks for unused imports or definitions
32
33
*
@@ -146,6 +147,13 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
146
147
if ! tree.isInstanceOf [tpd.InferredTypeTree ] then typeTraverser(unusedDataApply).traverse(tree.tpe)
147
148
ctx
148
149
150
+ override def prepareForAssign (tree : tpd.Assign )(using Context ): Context =
151
+ unusedDataApply{ ud =>
152
+ val sym = tree.lhs.symbol
153
+ if sym.exists then
154
+ ud.registerSetVar(sym)
155
+ }
156
+
149
157
// ========== MiniPhase Transform ==========
150
158
151
159
override def transformBlock (tree : tpd.Block )(using Context ): tpd.Tree =
@@ -172,6 +180,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
172
180
unusedDataApply(_.removeIgnoredUsage(tree.symbol))
173
181
tree
174
182
183
+
175
184
// ---------- MiniPhase HELPERS -----------
176
185
177
186
private def pushInBlockTemplatePackageDef (tree : tpd.Block | tpd.Template | tpd.PackageDef )(using Context ): Context =
@@ -215,11 +224,11 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
215
224
case sel : Select =>
216
225
prepareForSelect(sel)
217
226
traverseChildren(tree)(using newCtx)
218
- case _ : (tpd.Block | tpd.Template | tpd.PackageDef ) =>
227
+ case tree : (tpd.Block | tpd.Template | tpd.PackageDef ) =>
219
228
// ! DIFFERS FROM MINIPHASE
220
- unusedDataApply { ud =>
221
- ud.inNewScope( ScopeType .fromTree(tree))( traverseChildren(tree)(using newCtx) )
222
- }
229
+ pushInBlockTemplatePackageDef(tree)
230
+ traverseChildren(tree)(using newCtx)
231
+ popOutBlockTemplatePackageDef()
223
232
case t: tpd.ValDef =>
224
233
prepareForValDef(t)
225
234
traverseChildren(tree)(using newCtx)
@@ -235,6 +244,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
235
244
case t : tpd.Bind =>
236
245
prepareForBind(t)
237
246
traverseChildren(tree)(using newCtx)
247
+ case t: tpd.Assign =>
248
+ prepareForAssign(t)
249
+ traverseChildren(tree)
238
250
case _ : tpd.InferredTypeTree =>
239
251
case t@ tpd.TypeTree () =>
240
252
// ! DIFFERS FROM MINIPHASE
@@ -278,6 +290,10 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
278
290
report.warning(s " unused private member " , t)
279
291
case UnusedSymbol (t, _, WarnTypes .PatVars ) =>
280
292
report.warning(s " unused pattern variable " , t)
293
+ case UnusedSymbol (t, _, WarnTypes .UnsetLocals ) =>
294
+ report.warning(s " unset local variable " , t)
295
+ case UnusedSymbol (t, _, WarnTypes .UnsetPrivates ) =>
296
+ report.warning(s " unset private variable " , t)
281
297
}
282
298
283
299
end CheckUnused
@@ -297,6 +313,8 @@ object CheckUnused:
297
313
case ImplicitParams
298
314
case PrivateMembers
299
315
case PatVars
316
+ case UnsetLocals
317
+ case UnsetPrivates
300
318
301
319
/**
302
320
* The key used to retrieve the "unused entity" analysis metadata,
@@ -343,12 +361,8 @@ object CheckUnused:
343
361
private val implicitParamInScope = MutSet [tpd.MemberDef ]()
344
362
private val patVarsInScope = MutSet [tpd.Bind ]()
345
363
346
- /* Unused collection collected at the end */
347
- private val unusedLocalDef = MutSet [tpd.MemberDef ]()
348
- private val unusedPrivateDef = MutSet [tpd.MemberDef ]()
349
- private val unusedExplicitParams = MutSet [tpd.MemberDef ]()
350
- private val unusedImplicitParams = MutSet [tpd.MemberDef ]()
351
- private val unusedPatVars = MutSet [tpd.Bind ]()
364
+ /** All variables sets*/
365
+ private val setVars = MutSet [Symbol ]()
352
366
353
367
/** All used symbols */
354
368
private val usedDef = MutSet [Symbol ]()
@@ -360,15 +374,6 @@ object CheckUnused:
360
374
361
375
private val paramsToSkip = MutSet [Symbol ]()
362
376
363
- /**
364
- * Push a new Scope of the given type, executes the given Unit and
365
- * pop it back to the original type.
366
- */
367
- def inNewScope (newScope : ScopeType )(execInNewScope : => Unit )(using Context ): Unit =
368
- val prev = currScopeType
369
- pushScope(newScope)
370
- execInNewScope
371
- popScope()
372
377
373
378
def finishAggregation (using Context )(): Unit =
374
379
val unusedInThisStage = this .getUnused
@@ -443,6 +448,9 @@ object CheckUnused:
443
448
impInScope.push(MutSet ())
444
449
usedInScope.push(MutSet ())
445
450
451
+ def registerSetVar (sym : Symbol ): Unit =
452
+ setVars += sym
453
+
446
454
/**
447
455
* leave the current scope and do :
448
456
*
@@ -501,15 +509,19 @@ object CheckUnused:
501
509
unusedImport.map(d => UnusedSymbol (d.srcPos, d.name, WarnTypes .Imports )).toList
502
510
else
503
511
Nil
504
- val sortedLocalDefs =
512
+ // Partition to extract unset local variables from usedLocalDefs
513
+ val (usedLocalDefs, unusedLocalDefs) =
505
514
if ctx.settings.WunusedHas .locals then
506
- localDefInScope
507
- .filterNot(d => d.symbol.usedDefContains)
508
- .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
509
- .filterNot(d => containsSyntheticSuffix(d.symbol))
510
- .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .LocalDefs )).toList
515
+ localDefInScope.partition(d => d.symbol.usedDefContains)
511
516
else
512
- Nil
517
+ (Nil , Nil )
518
+ val sortedLocalDefs =
519
+ unusedLocalDefs
520
+ .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
521
+ .filterNot(d => containsSyntheticSuffix(d.symbol))
522
+ .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .LocalDefs )).toList
523
+ val unsetLocalDefs = usedLocalDefs.filter(isUnsetVarDef).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .UnsetLocals )).toList
524
+
513
525
val sortedExplicitParams =
514
526
if ctx.settings.WunusedHas .explicits then
515
527
explicitParamInScope
@@ -527,14 +539,14 @@ object CheckUnused:
527
539
.map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .ImplicitParams )).toList
528
540
else
529
541
Nil
530
- val sortedPrivateDefs =
542
+ // Partition to extract unset private variables from usedPrivates
543
+ val (usedPrivates, unusedPrivates) =
531
544
if ctx.settings.WunusedHas .privates then
532
- privateDefInScope
533
- .filterNot(d => d.symbol.usedDefContains)
534
- .filterNot(d => containsSyntheticSuffix(d.symbol))
535
- .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PrivateMembers )).toList
545
+ privateDefInScope.partition(d => d.symbol.usedDefContains)
536
546
else
537
- Nil
547
+ (Nil , Nil )
548
+ val sortedPrivateDefs = unusedPrivates.filterNot(d => containsSyntheticSuffix(d.symbol)).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PrivateMembers )).toList
549
+ val unsetPrivateDefs = usedPrivates.filter(isUnsetVarDef).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .UnsetPrivates )).toList
538
550
val sortedPatVars =
539
551
if ctx.settings.WunusedHas .patvars then
540
552
patVarsInScope
@@ -544,7 +556,9 @@ object CheckUnused:
544
556
.map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PatVars )).toList
545
557
else
546
558
Nil
547
- val warnings = List (sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s =>
559
+ val warnings =
560
+ List (sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams,
561
+ sortedPrivateDefs, sortedPatVars, unsetLocalDefs, unsetPrivateDefs).flatten.sortBy { s =>
548
562
val pos = s.pos.sourcePos
549
563
(pos.line, pos.column)
550
564
}
@@ -731,10 +745,13 @@ object CheckUnused:
731
745
! isSyntheticMainParam(sym) &&
732
746
! sym.shouldNotReportParamOwner
733
747
734
-
735
748
private def shouldReportPrivateDef (using Context ): Boolean =
736
749
currScopeType.top == ScopeType .Template && ! memDef.symbol.isConstructor && memDef.symbol.is(Private , butNot = SelfName | Synthetic | CaseAccessor )
737
750
751
+ private def isUnsetVarDef (using Context ): Boolean =
752
+ val sym = memDef.symbol
753
+ sym.is(Mutable ) && ! setVars(sym)
754
+
738
755
extension (imp : tpd.Import )
739
756
/** Enum generate an import for its cases (but outside them), which should be ignored */
740
757
def isGeneratedByEnum (using Context ): Boolean =
0 commit comments