@@ -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
/**
@@ -453,12 +478,13 @@ object CheckUnused:
453
478
*
454
479
* The given `List` is sorted by line and then column of the position
455
480
*/
481
+
456
482
def getUnused (using Context ): UnusedResult =
457
483
popScope()
458
484
459
485
val sortedImp =
460
486
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
462
488
else
463
489
Nil
464
490
val sortedLocalDefs =
@@ -467,7 +493,7 @@ object CheckUnused:
467
493
.filterNot(d => d.symbol.usedDefContains)
468
494
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
469
495
.filterNot(d => containsSyntheticSuffix(d.symbol))
470
- .map(d => d.namePos -> WarnTypes .LocalDefs ).toList
496
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .LocalDefs ) ).toList
471
497
else
472
498
Nil
473
499
val sortedExplicitParams =
@@ -476,23 +502,23 @@ object CheckUnused:
476
502
.filterNot(d => d.symbol.usedDefContains)
477
503
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
478
504
.filterNot(d => containsSyntheticSuffix(d.symbol))
479
- .map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
505
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ExplicitParams ) ).toList
480
506
else
481
507
Nil
482
508
val sortedImplicitParams =
483
509
if ctx.settings.WunusedHas .implicits then
484
510
implicitParamInScope
485
511
.filterNot(d => d.symbol.usedDefContains)
486
512
.filterNot(d => containsSyntheticSuffix(d.symbol))
487
- .map(d => d.namePos -> WarnTypes .ImplicitParams ).toList
513
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ImplicitParams ) ).toList
488
514
else
489
515
Nil
490
516
val sortedPrivateDefs =
491
517
if ctx.settings.WunusedHas .privates then
492
518
privateDefInScope
493
519
.filterNot(d => d.symbol.usedDefContains)
494
520
.filterNot(d => containsSyntheticSuffix(d.symbol))
495
- .map(d => d.namePos -> WarnTypes .PrivateMembers ).toList
521
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .PrivateMembers ) ).toList
496
522
else
497
523
Nil
498
524
val sortedPatVars =
@@ -501,14 +527,14 @@ object CheckUnused:
501
527
.filterNot(d => d.symbol.usedDefContains)
502
528
.filterNot(d => containsSyntheticSuffix(d.symbol))
503
529
.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
505
531
else
506
532
Nil
507
533
val warnings = List (sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s =>
508
- val pos = s._1 .sourcePos
534
+ val pos = s.pos .sourcePos
509
535
(pos.line, pos.column)
510
536
}
511
- UnusedResult (warnings, Nil )
537
+ UnusedResult (warnings)
512
538
end getUnused
513
539
// ============================ HELPERS ====================================
514
540
@@ -707,7 +733,11 @@ object CheckUnused:
707
733
case _:tpd.Block => Local
708
734
case _ => Other
709
735
736
+ case class UnusedSymbol (pos : SrcPos , name : Name , warnType : WarnTypes )
710
737
/** 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
+
712
742
end CheckUnused
713
743
0 commit comments