@@ -27,6 +27,7 @@ import dotty.tools.dotc.core.Symbols.Symbol
2727import dotty .tools .dotc .core .StdNames .nme
2828import scala .math .Ordering
2929
30+
3031/**
3132 * A compiler phase that checks for unused imports or definitions
3233 *
@@ -146,6 +147,13 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
146147 if ! tree.isInstanceOf [tpd.InferredTypeTree ] then typeTraverser(unusedDataApply).traverse(tree.tpe)
147148 ctx
148149
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+
149157 // ========== MiniPhase Transform ==========
150158
151159 override def transformBlock (tree : tpd.Block )(using Context ): tpd.Tree =
@@ -172,6 +180,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
172180 unusedDataApply(_.removeIgnoredUsage(tree.symbol))
173181 tree
174182
183+
175184 // ---------- MiniPhase HELPERS -----------
176185
177186 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
215224 case sel : Select =>
216225 prepareForSelect(sel)
217226 traverseChildren(tree)(using newCtx)
218- case _ : (tpd.Block | tpd.Template | tpd.PackageDef ) =>
227+ case tree : (tpd.Block | tpd.Template | tpd.PackageDef ) =>
219228 // ! 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()
223232 case t: tpd.ValDef =>
224233 prepareForValDef(t)
225234 traverseChildren(tree)(using newCtx)
@@ -235,6 +244,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
235244 case t : tpd.Bind =>
236245 prepareForBind(t)
237246 traverseChildren(tree)(using newCtx)
247+ case t: tpd.Assign =>
248+ prepareForAssign(t)
249+ traverseChildren(tree)
238250 case _ : tpd.InferredTypeTree =>
239251 case t@ tpd.TypeTree () =>
240252 // ! DIFFERS FROM MINIPHASE
@@ -278,6 +290,10 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
278290 report.warning(s " unused private member " , t)
279291 case UnusedSymbol (t, _, WarnTypes .PatVars ) =>
280292 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)
281297 }
282298
283299end CheckUnused
@@ -297,6 +313,8 @@ object CheckUnused:
297313 case ImplicitParams
298314 case PrivateMembers
299315 case PatVars
316+ case UnsetLocals
317+ case UnsetPrivates
300318
301319 /**
302320 * The key used to retrieve the "unused entity" analysis metadata,
@@ -343,12 +361,8 @@ object CheckUnused:
343361 private val implicitParamInScope = MutSet [tpd.MemberDef ]()
344362 private val patVarsInScope = MutSet [tpd.Bind ]()
345363
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 ]()
352366
353367 /** All used symbols */
354368 private val usedDef = MutSet [Symbol ]()
@@ -360,15 +374,6 @@ object CheckUnused:
360374
361375 private val paramsToSkip = MutSet [Symbol ]()
362376
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()
372377
373378 def finishAggregation (using Context )(): Unit =
374379 val unusedInThisStage = this .getUnused
@@ -443,6 +448,9 @@ object CheckUnused:
443448 impInScope.push(MutSet ())
444449 usedInScope.push(MutSet ())
445450
451+ def registerSetVar (sym : Symbol ): Unit =
452+ setVars += sym
453+
446454 /**
447455 * leave the current scope and do :
448456 *
@@ -501,15 +509,19 @@ object CheckUnused:
501509 unusedImport.map(d => UnusedSymbol (d.srcPos, d.name, WarnTypes .Imports )).toList
502510 else
503511 Nil
504- val sortedLocalDefs =
512+ // Partition to extract unset local variables from usedLocalDefs
513+ val (usedLocalDefs, unusedLocalDefs) =
505514 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)
511516 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 = unsetVarsFromUsedSym(usedLocalDefs).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .UnsetLocals )).toList
524+
513525 val sortedExplicitParams =
514526 if ctx.settings.WunusedHas .explicits then
515527 explicitParamInScope
@@ -527,14 +539,14 @@ object CheckUnused:
527539 .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .ImplicitParams )).toList
528540 else
529541 Nil
530- val sortedPrivateDefs =
542+ // Partition to extract unset private variables from usedPrivates
543+ val (usedPrivates, unusedPrivates) =
531544 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)
536546 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 = unsetVarsFromUsedSym(usedPrivates).map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .UnsetPrivates )).toList
538550 val sortedPatVars =
539551 if ctx.settings.WunusedHas .patvars then
540552 patVarsInScope
@@ -544,7 +556,9 @@ object CheckUnused:
544556 .map(d => UnusedSymbol (d.namePos, d.name, WarnTypes .PatVars )).toList
545557 else
546558 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 =>
548562 val pos = s.pos.sourcePos
549563 (pos.line, pos.column)
550564 }
@@ -744,6 +758,9 @@ object CheckUnused:
744758 private def isWildcard : Boolean =
745759 thisName == StdNames .nme.WILDCARD || thisName.is(WildcardParamName )
746760
761+ def unsetVarsFromUsedSym (usedDefs : Iterable [tpd.MemberDef ])(using Context ): Iterable [tpd.MemberDef ] =
762+ usedDefs.filter(d => d.symbol.is(Mutable ) && ! setVars(d.symbol))
763+
747764 end UnusedData
748765
749766 private object UnusedData :
0 commit comments