@@ -33,22 +33,15 @@ import dotty.tools.dotc.core.StdNames.nme
33
33
* Basically, it gathers definition/imports and their usage. If a
34
34
* definition/imports does not have any usage, then it is reported.
35
35
*/
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 .*
44
39
45
40
private def unusedDataApply [U ](f : UnusedData => U )(using Context ): Context =
46
41
ctx.property(_key).foreach(f)
47
42
ctx
48
- private def getUnusedData (using Context ): Option [UnusedData ] =
49
- ctx.property(_key)
50
43
51
- override def phaseName : String = CheckUnused .phaseName
44
+ override def phaseName : String = CheckUnused .phaseNamePrefix + suffix
52
45
53
46
override def description : String = CheckUnused .description
54
47
@@ -60,13 +53,21 @@ class CheckUnused extends MiniPhase:
60
53
61
54
override def prepareForUnit (tree : tpd.Tree )(using Context ): Context =
62
55
val data = UnusedData ()
56
+ tree.getAttachment(_key).foreach(oldData =>
57
+ data.unusedAggregate = oldData.unusedAggregate
58
+ )
63
59
val fresh = ctx.fresh.setProperty(_key, data)
60
+ tree.putAttachment(_key, data)
64
61
fresh
65
62
66
63
// ========== END + REPORTING ==========
67
64
68
65
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
+ }
70
71
tree
71
72
72
73
// ========== MiniPhase Prepare ==========
@@ -252,31 +253,45 @@ class CheckUnused extends MiniPhase:
252
253
private def traverseAnnotations (sym : Symbol )(using Context ): Unit =
253
254
sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree))
254
255
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
+
255
267
/** Do the actual reporting given the result of the anaylsis */
256
268
private def reportUnused (res : UnusedData .UnusedResult )(using Context ): Unit =
257
- import CheckUnused .WarnTypes
258
269
res.warnings.foreach { s =>
259
270
s match
260
- case (t , WarnTypes .Imports ) =>
271
+ case UnusedSymbol (t, _ , WarnTypes .Imports ) =>
261
272
report.warning(s " unused import " , t)
262
- case (t , WarnTypes .LocalDefs ) =>
273
+ case UnusedSymbol (t, _ , WarnTypes .LocalDefs ) =>
263
274
report.warning(s " unused local definition " , t)
264
- case (t , WarnTypes .ExplicitParams ) =>
275
+ case UnusedSymbol (t, _ , WarnTypes .ExplicitParams ) =>
265
276
report.warning(s " unused explicit parameter " , t)
266
- case (t , WarnTypes .ImplicitParams ) =>
277
+ case UnusedSymbol (t, _ , WarnTypes .ImplicitParams ) =>
267
278
report.warning(s " unused implicit parameter " , t)
268
- case (t , WarnTypes .PrivateMembers ) =>
279
+ case UnusedSymbol (t, _ , WarnTypes .PrivateMembers ) =>
269
280
report.warning(s " unused private member " , t)
270
- case (t , WarnTypes .PatVars ) =>
281
+ case UnusedSymbol (t, _ , WarnTypes .PatVars ) =>
271
282
report.warning(s " unused pattern variable " , t)
272
283
}
273
284
274
285
end CheckUnused
275
286
276
287
object CheckUnused :
277
- val phaseName : String = " checkUnused"
288
+ val phaseNamePrefix : String = " checkUnused"
278
289
val description : String = " check for unused elements"
279
290
291
+ enum PhaseMode :
292
+ case Aggregate
293
+ case Report
294
+
280
295
private enum WarnTypes :
281
296
case Imports
282
297
case LocalDefs
@@ -285,20 +300,30 @@ object CheckUnused:
285
300
case PrivateMembers
286
301
case PatVars
287
302
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
+
288
312
/**
289
313
* A stateful class gathering the infos on :
290
314
* - imports
291
315
* - definitions
292
316
* - usage
293
317
*/
294
318
private class UnusedData :
295
- import dotty .tools .dotc .transform .CheckUnused .UnusedData .UnusedResult
296
319
import collection .mutable .{Set => MutSet , Map => MutMap , Stack => MutStack }
297
- import UnusedData .ScopeType
320
+ import UnusedData .*
298
321
299
322
/** The current scope during the tree traversal */
300
323
var currScopeType : MutStack [ScopeType ] = MutStack (ScopeType .Other )
301
324
325
+ var unusedAggregate : Option [UnusedResult ] = None
326
+
302
327
/* IMPORTS */
303
328
private val impInScope = MutStack (MutSet [tpd.Import ]())
304
329
/**
@@ -452,12 +477,13 @@ object CheckUnused:
452
477
*
453
478
* The given `List` is sorted by line and then column of the position
454
479
*/
480
+
455
481
def getUnused (using Context ): UnusedResult =
456
482
popScope()
457
483
458
484
val sortedImp =
459
485
if ctx.settings.WunusedHas .imports || ctx.settings.WunusedHas .strictNoImplicitWarn then
460
- unusedImport.map(d => d.srcPos -> WarnTypes .Imports ).toList
486
+ unusedImport.map(d => UnusedSymbol ( d.srcPos, d.name, WarnTypes .Imports ) ).toList
461
487
else
462
488
Nil
463
489
val sortedLocalDefs =
@@ -466,31 +492,31 @@ object CheckUnused:
466
492
.filterNot(d => d.symbol.usedDefContains)
467
493
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
468
494
.filterNot(d => containsSyntheticSuffix(d.symbol))
469
- .map(d => d.namePos -> WarnTypes .LocalDefs ).toList
495
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .LocalDefs ) ).toList
470
496
else
471
497
Nil
472
498
val sortedExplicitParams =
473
499
if ctx.settings.WunusedHas .explicits then
474
500
explicitParamInScope
475
501
.filterNot(d => d.symbol.usedDefContains)
476
502
.filterNot(d => containsSyntheticSuffix(d.symbol))
477
- .map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
503
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ExplicitParams ) ).toList
478
504
else
479
505
Nil
480
506
val sortedImplicitParams =
481
507
if ctx.settings.WunusedHas .implicits then
482
508
implicitParamInScope
483
509
.filterNot(d => d.symbol.usedDefContains)
484
510
.filterNot(d => containsSyntheticSuffix(d.symbol))
485
- .map(d => d.namePos -> WarnTypes .ImplicitParams ).toList
511
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ImplicitParams ) ).toList
486
512
else
487
513
Nil
488
514
val sortedPrivateDefs =
489
515
if ctx.settings.WunusedHas .privates then
490
516
privateDefInScope
491
517
.filterNot(d => d.symbol.usedDefContains)
492
518
.filterNot(d => containsSyntheticSuffix(d.symbol))
493
- .map(d => d.namePos -> WarnTypes .PrivateMembers ).toList
519
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .PrivateMembers ) ).toList
494
520
else
495
521
Nil
496
522
val sortedPatVars =
@@ -499,14 +525,14 @@ object CheckUnused:
499
525
.filterNot(d => d.symbol.usedDefContains)
500
526
.filterNot(d => containsSyntheticSuffix(d.symbol))
501
527
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
502
- .map(d => d.namePos -> WarnTypes .PatVars ).toList
528
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .PatVars ) ).toList
503
529
else
504
530
Nil
505
531
val warnings = List (sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s =>
506
- val pos = s._1 .sourcePos
532
+ val pos = s.pos .sourcePos
507
533
(pos.line, pos.column)
508
534
}
509
- UnusedResult (warnings, Nil )
535
+ UnusedResult (warnings)
510
536
end getUnused
511
537
// ============================ HELPERS ====================================
512
538
@@ -703,7 +729,11 @@ object CheckUnused:
703
729
case _:tpd.Block => Local
704
730
case _ => Other
705
731
732
+ case class UnusedSymbol (pos : SrcPos , name : Name , warnType : WarnTypes )
706
733
/** A container for the results of the used elements analysis */
707
- case class UnusedResult (warnings : List [(dotty.tools.dotc.util.SrcPos , WarnTypes )], usedImports : List [(tpd.Import , untpd.ImportSelector )])
734
+ case class UnusedResult (warnings : List [UnusedSymbol ])
735
+ object UnusedResult :
736
+ val Empty = UnusedResult (Nil )
737
+
708
738
end CheckUnused
709
739
0 commit comments