@@ -33,22 +33,15 @@ import dotty.tools.dotc.core.StdNames.nme
3333 * Basically, it gathers definition/imports and their usage. If a
3434 * definition/imports does not have any usage, then it is reported.
3535 */
36- class CheckUnused extends MiniPhase :
37- import CheckUnused .UnusedData
38-
39- /**
40- * The key used to retrieve the "unused entity" analysis metadata,
41- * from the compilation `Context`
42- */
43- private val _key = Property .Key [UnusedData ]
36+ class CheckUnused private (phaseMode : CheckUnused .PhaseMode , suffix : String , _key : Property .Key [CheckUnused .UnusedData ]) extends MiniPhase :
37+ import CheckUnused .*
38+ import UnusedData .*
4439
4540 private def unusedDataApply [U ](f : UnusedData => U )(using Context ): Context =
4641 ctx.property(_key).foreach(f)
4742 ctx
48- private def getUnusedData (using Context ): Option [UnusedData ] =
49- ctx.property(_key)
5043
51- override def phaseName : String = CheckUnused .phaseName
44+ override def phaseName : String = CheckUnused .phaseNamePrefix + suffix
5245
5346 override def description : String = CheckUnused .description
5447
@@ -60,13 +53,21 @@ class CheckUnused extends MiniPhase:
6053
6154 override def prepareForUnit (tree : tpd.Tree )(using Context ): Context =
6255 val data = UnusedData ()
56+ tree.getAttachment(_key).foreach(oldData =>
57+ data.unusedAggregate = oldData.unusedAggregate
58+ )
6359 val fresh = ctx.fresh.setProperty(_key, data)
60+ tree.putAttachment(_key, data)
6461 fresh
6562
6663 // ========== END + REPORTING ==========
6764
6865 override def transformUnit (tree : tpd.Tree )(using Context ): tpd.Tree =
69- unusedDataApply(ud => reportUnused(ud.getUnused))
66+ unusedDataApply { ud =>
67+ aggregateUnused(ud, ud.getUnused)
68+ if (phaseMode == PhaseMode .Report ) then
69+ ud.unusedAggregate.foreach(reportUnused)
70+ }
7071 tree
7172
7273 // ========== MiniPhase Prepare ==========
@@ -252,31 +253,45 @@ class CheckUnused extends MiniPhase:
252253 private def traverseAnnotations (sym : Symbol )(using Context ): Unit =
253254 sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree))
254255
256+ private def aggregateUnused (data : UnusedData , res : UnusedData .UnusedResult )(using Context ): Unit =
257+ data.unusedAggregate match {
258+ case None =>
259+ data.unusedAggregate = Some (res)
260+ case Some (prevUnused) =>
261+ val intersection = res.warnings.filter(sym => prevUnused.warnings.contains(sym))
262+ data.unusedAggregate = Some (UnusedResult (intersection))
263+ }
264+
265+
266+
255267 /** Do the actual reporting given the result of the anaylsis */
256268 private def reportUnused (res : UnusedData .UnusedResult )(using Context ): Unit =
257- import CheckUnused .WarnTypes
258269 res.warnings.foreach { s =>
259270 s match
260- case (t , WarnTypes .Imports ) =>
271+ case UnusedSymbol (t, _ , WarnTypes .Imports ) =>
261272 report.warning(s " unused import " , t)
262- case (t , WarnTypes .LocalDefs ) =>
273+ case UnusedSymbol (t, _ , WarnTypes .LocalDefs ) =>
263274 report.warning(s " unused local definition " , t)
264- case (t , WarnTypes .ExplicitParams ) =>
275+ case UnusedSymbol (t, _ , WarnTypes .ExplicitParams ) =>
265276 report.warning(s " unused explicit parameter " , t)
266- case (t , WarnTypes .ImplicitParams ) =>
277+ case UnusedSymbol (t, _ , WarnTypes .ImplicitParams ) =>
267278 report.warning(s " unused implicit parameter " , t)
268- case (t , WarnTypes .PrivateMembers ) =>
279+ case UnusedSymbol (t, _ , WarnTypes .PrivateMembers ) =>
269280 report.warning(s " unused private member " , t)
270- case (t , WarnTypes .PatVars ) =>
281+ case UnusedSymbol (t, _ , WarnTypes .PatVars ) =>
271282 report.warning(s " unused pattern variable " , t)
272283 }
273284
274285end CheckUnused
275286
276287object CheckUnused :
277- val phaseName : String = " checkUnused"
288+ val phaseNamePrefix : String = " checkUnused"
278289 val description : String = " check for unused elements"
279290
291+ enum PhaseMode :
292+ case Aggregate
293+ case Report
294+
280295 private enum WarnTypes :
281296 case Imports
282297 case LocalDefs
@@ -285,20 +300,30 @@ object CheckUnused:
285300 case PrivateMembers
286301 case PatVars
287302
303+ /**
304+ * The key used to retrieve the "unused entity" analysis metadata,
305+ * from the compilation `Context`
306+ */
307+ private val _key = Property .StickyKey [UnusedData ]
308+
309+ val PostTyper = new CheckUnused (PhaseMode .Aggregate , " PostTyper" , _key)
310+ val PostInlining = new CheckUnused (PhaseMode .Report , " PostInlining" , _key)
311+
288312 /**
289313 * A stateful class gathering the infos on :
290314 * - imports
291315 * - definitions
292316 * - usage
293317 */
294318 private class UnusedData :
295- import dotty .tools .dotc .transform .CheckUnused .UnusedData .UnusedResult
296319 import collection .mutable .{Set => MutSet , Map => MutMap , Stack => MutStack }
297- import UnusedData .ScopeType
320+ import UnusedData .*
298321
299322 /** The current scope during the tree traversal */
300323 var currScopeType : MutStack [ScopeType ] = MutStack (ScopeType .Other )
301324
325+ var unusedAggregate : Option [UnusedResult ] = None
326+
302327 /* IMPORTS */
303328 private val impInScope = MutStack (MutSet [tpd.Import ]())
304329 /**
@@ -453,12 +478,13 @@ object CheckUnused:
453478 *
454479 * The given `List` is sorted by line and then column of the position
455480 */
481+
456482 def getUnused (using Context ): UnusedResult =
457483 popScope()
458484
459485 val sortedImp =
460486 if ctx.settings.WunusedHas .imports || ctx.settings.WunusedHas .strictNoImplicitWarn then
461- unusedImport.map(d => d.srcPos -> WarnTypes .Imports ).toList
487+ unusedImport.map(d => UnusedSymbol ( d.srcPos, d.name, WarnTypes .Imports ) ).toList
462488 else
463489 Nil
464490 val sortedLocalDefs =
@@ -467,7 +493,7 @@ object CheckUnused:
467493 .filterNot(d => d.symbol.usedDefContains)
468494 .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
469495 .filterNot(d => containsSyntheticSuffix(d.symbol))
470- .map(d => d.namePos -> WarnTypes .LocalDefs ).toList
496+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .LocalDefs ) ).toList
471497 else
472498 Nil
473499 val sortedExplicitParams =
@@ -476,23 +502,23 @@ object CheckUnused:
476502 .filterNot(d => d.symbol.usedDefContains)
477503 .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
478504 .filterNot(d => containsSyntheticSuffix(d.symbol))
479- .map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
505+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ExplicitParams ) ).toList
480506 else
481507 Nil
482508 val sortedImplicitParams =
483509 if ctx.settings.WunusedHas .implicits then
484510 implicitParamInScope
485511 .filterNot(d => d.symbol.usedDefContains)
486512 .filterNot(d => containsSyntheticSuffix(d.symbol))
487- .map(d => d.namePos -> WarnTypes .ImplicitParams ).toList
513+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ImplicitParams ) ).toList
488514 else
489515 Nil
490516 val sortedPrivateDefs =
491517 if ctx.settings.WunusedHas .privates then
492518 privateDefInScope
493519 .filterNot(d => d.symbol.usedDefContains)
494520 .filterNot(d => containsSyntheticSuffix(d.symbol))
495- .map(d => d.namePos -> WarnTypes .PrivateMembers ).toList
521+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .PrivateMembers ) ).toList
496522 else
497523 Nil
498524 val sortedPatVars =
@@ -501,14 +527,14 @@ object CheckUnused:
501527 .filterNot(d => d.symbol.usedDefContains)
502528 .filterNot(d => containsSyntheticSuffix(d.symbol))
503529 .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
504- .map(d => d.namePos -> WarnTypes .PatVars ).toList
530+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .PatVars ) ).toList
505531 else
506532 Nil
507533 val warnings = List (sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s =>
508- val pos = s._1 .sourcePos
534+ val pos = s.pos .sourcePos
509535 (pos.line, pos.column)
510536 }
511- UnusedResult (warnings, Nil )
537+ UnusedResult (warnings)
512538 end getUnused
513539 // ============================ HELPERS ====================================
514540
@@ -707,7 +733,11 @@ object CheckUnused:
707733 case _:tpd.Block => Local
708734 case _ => Other
709735
736+ case class UnusedSymbol (pos : SrcPos , name : Name , warnType : WarnTypes )
710737 /** A container for the results of the used elements analysis */
711- case class UnusedResult (warnings : List [(dotty.tools.dotc.util.SrcPos , WarnTypes )], usedImports : List [(tpd.Import , untpd.ImportSelector )])
738+ case class UnusedResult (warnings : List [UnusedSymbol ])
739+ object UnusedResult :
740+ val Empty = UnusedResult (Nil )
741+
712742end CheckUnused
713743
0 commit comments