@@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames
13
13
import dotty .tools .dotc .report
14
14
import dotty .tools .dotc .reporting .Message
15
15
import dotty .tools .dotc .typer .ImportInfo
16
- import dotty .tools .dotc .util .{Property , SrcPos }
16
+ import dotty .tools .dotc .util .{Property , SrcPos , Store }
17
17
import dotty .tools .dotc .core .Mode
18
18
import dotty .tools .dotc .core .Types .{AnnotatedType , ConstantType , NoType , TermRef , Type , TypeTraverser }
19
19
import dotty .tools .dotc .core .Flags .flagsString
@@ -24,6 +24,7 @@ import dotty.tools.dotc.core.Annotations
24
24
import dotty .tools .dotc .core .Definitions
25
25
import dotty .tools .dotc .core .NameKinds .WildcardParamName
26
26
import dotty .tools .dotc .core .Symbols .Symbol
27
+ import dotty .tools .dotc .interfaces .CompilerCallback
27
28
28
29
29
30
@@ -33,22 +34,15 @@ import dotty.tools.dotc.core.Symbols.Symbol
33
34
* Basically, it gathers definition/imports and their usage. If a
34
35
* definition/imports does not have any usage, then it is reported.
35
36
*/
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 ]
37
+ class CheckUnused private (phaseMode : CheckUnused .PhaseMode , suffix : String , _key : Property .Key [CheckUnused .UnusedData ]) extends MiniPhase :
38
+ import CheckUnused .*
39
+ import UnusedData .*
44
40
45
41
private def unusedDataApply [U ](f : UnusedData => U )(using Context ): Context =
46
42
ctx.property(_key).foreach(f)
47
43
ctx
48
- private def getUnusedData (using Context ): Option [UnusedData ] =
49
- ctx.property(_key)
50
44
51
- override def phaseName : String = CheckUnused .phaseName
45
+ override def phaseName : String = CheckUnused .phaseNamePrefix + suffix
52
46
53
47
override def description : String = CheckUnused .description
54
48
@@ -60,13 +54,21 @@ class CheckUnused extends MiniPhase:
60
54
61
55
override def prepareForUnit (tree : tpd.Tree )(using Context ): Context =
62
56
val data = UnusedData ()
57
+ tree.getAttachment(_key).foreach(oldData =>
58
+ data.unusedAggregate = oldData.unusedAggregate
59
+ )
63
60
val fresh = ctx.fresh.setProperty(_key, data)
61
+ tree.putAttachment(_key, data)
64
62
fresh
65
63
66
64
// ========== END + REPORTING ==========
67
65
68
66
override def transformUnit (tree : tpd.Tree )(using Context ): tpd.Tree =
69
- unusedDataApply(ud => reportUnused(ud.getUnused))
67
+ unusedDataApply { ud =>
68
+ aggregateUnused(ud, ud.getUnused)
69
+ if (phaseMode == PhaseMode .Report ) then
70
+ ud.unusedAggregate.foreach(reportUnused)
71
+ }
70
72
tree
71
73
72
74
// ========== MiniPhase Prepare ==========
@@ -249,31 +251,45 @@ class CheckUnused extends MiniPhase:
249
251
private def traverseAnnotations (sym : Symbol )(using Context ): Unit =
250
252
sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree))
251
253
254
+ private def aggregateUnused (data : UnusedData , res : UnusedData .UnusedResult )(using Context ): Unit =
255
+ data.unusedAggregate match {
256
+ case None =>
257
+ data.unusedAggregate = Some (res)
258
+ case Some (prevUnused) =>
259
+ val intersection = res.warnings.filter(sym => prevUnused.warnings.contains(sym))
260
+ data.unusedAggregate = Some (UnusedResult (intersection))
261
+ }
262
+
263
+
264
+
252
265
/** Do the actual reporting given the result of the anaylsis */
253
266
private def reportUnused (res : UnusedData .UnusedResult )(using Context ): Unit =
254
- import CheckUnused .WarnTypes
255
267
res.warnings.foreach { s =>
256
268
s match
257
- case (t , WarnTypes .Imports ) =>
269
+ case UnusedSymbol (t, _ , WarnTypes .Imports ) =>
258
270
report.warning(s " unused import " , t)
259
- case (t , WarnTypes .LocalDefs ) =>
271
+ case UnusedSymbol (t, _ , WarnTypes .LocalDefs ) =>
260
272
report.warning(s " unused local definition " , t)
261
- case (t , WarnTypes .ExplicitParams ) =>
273
+ case UnusedSymbol (t, _ , WarnTypes .ExplicitParams ) =>
262
274
report.warning(s " unused explicit parameter " , t)
263
- case (t , WarnTypes .ImplicitParams ) =>
275
+ case UnusedSymbol (t, _ , WarnTypes .ImplicitParams ) =>
264
276
report.warning(s " unused implicit parameter " , t)
265
- case (t , WarnTypes .PrivateMembers ) =>
277
+ case UnusedSymbol (t, _ , WarnTypes .PrivateMembers ) =>
266
278
report.warning(s " unused private member " , t)
267
- case (t , WarnTypes .PatVars ) =>
279
+ case UnusedSymbol (t, _ , WarnTypes .PatVars ) =>
268
280
report.warning(s " unused pattern variable " , t)
269
281
}
270
282
271
283
end CheckUnused
272
284
273
285
object CheckUnused :
274
- val phaseName : String = " checkUnused"
286
+ val phaseNamePrefix : String = " checkUnused"
275
287
val description : String = " check for unused elements"
276
288
289
+ enum PhaseMode :
290
+ case Aggregate
291
+ case Report
292
+
277
293
private enum WarnTypes :
278
294
case Imports
279
295
case LocalDefs
@@ -282,20 +298,30 @@ object CheckUnused:
282
298
case PrivateMembers
283
299
case PatVars
284
300
301
+ /**
302
+ * The key used to retrieve the "unused entity" analysis metadata,
303
+ * from the compilation `Context`
304
+ */
305
+ private val _key = Property .StickyKey [UnusedData ]
306
+
307
+ val PostTyper = new CheckUnused (PhaseMode .Aggregate , " PostTyper" , _key)
308
+ val PostInlining = new CheckUnused (PhaseMode .Report , " PostInlining" , _key)
309
+
285
310
/**
286
311
* A stateful class gathering the infos on :
287
312
* - imports
288
313
* - definitions
289
314
* - usage
290
315
*/
291
316
private class UnusedData :
292
- import dotty .tools .dotc .transform .CheckUnused .UnusedData .UnusedResult
293
317
import collection .mutable .{Set => MutSet , Map => MutMap , Stack => MutStack }
294
- import UnusedData .ScopeType
318
+ import UnusedData .*
295
319
296
320
/** The current scope during the tree traversal */
297
321
var currScopeType : MutStack [ScopeType ] = MutStack (ScopeType .Other )
298
322
323
+ var unusedAggregate : Option [UnusedResult ] = None
324
+
299
325
/* IMPORTS */
300
326
private val impInScope = MutStack (MutSet [tpd.Import ]())
301
327
/**
@@ -449,12 +475,13 @@ object CheckUnused:
449
475
*
450
476
* The given `List` is sorted by line and then column of the position
451
477
*/
478
+
452
479
def getUnused (using Context ): UnusedResult =
453
480
popScope()
454
481
455
482
val sortedImp =
456
483
if ctx.settings.WunusedHas .imports || ctx.settings.WunusedHas .strictNoImplicitWarn then
457
- unusedImport.map(d => d.srcPos -> WarnTypes .Imports ).toList
484
+ unusedImport.map(d => UnusedSymbol ( d.srcPos, d.name, WarnTypes .Imports ) ).toList
458
485
else
459
486
Nil
460
487
val sortedLocalDefs =
@@ -463,31 +490,31 @@ object CheckUnused:
463
490
.filterNot(d => d.symbol.usedDefContains)
464
491
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
465
492
.filterNot(d => containsSyntheticSuffix(d.symbol))
466
- .map(d => d.namePos -> WarnTypes .LocalDefs ).toList
493
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .LocalDefs ) ).toList
467
494
else
468
495
Nil
469
496
val sortedExplicitParams =
470
497
if ctx.settings.WunusedHas .explicits then
471
498
explicitParamInScope
472
499
.filterNot(d => d.symbol.usedDefContains)
473
500
.filterNot(d => containsSyntheticSuffix(d.symbol))
474
- .map(d => d.namePos -> WarnTypes .ExplicitParams ).toList
501
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ExplicitParams ) ).toList
475
502
else
476
503
Nil
477
504
val sortedImplicitParams =
478
505
if ctx.settings.WunusedHas .implicits then
479
506
implicitParamInScope
480
507
.filterNot(d => d.symbol.usedDefContains)
481
508
.filterNot(d => containsSyntheticSuffix(d.symbol))
482
- .map(d => d.namePos -> WarnTypes .ImplicitParams ).toList
509
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .ImplicitParams ) ).toList
483
510
else
484
511
Nil
485
512
val sortedPrivateDefs =
486
513
if ctx.settings.WunusedHas .privates then
487
514
privateDefInScope
488
515
.filterNot(d => d.symbol.usedDefContains)
489
516
.filterNot(d => containsSyntheticSuffix(d.symbol))
490
- .map(d => d.namePos -> WarnTypes .PrivateMembers ).toList
517
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .PrivateMembers ) ).toList
491
518
else
492
519
Nil
493
520
val sortedPatVars =
@@ -496,14 +523,14 @@ object CheckUnused:
496
523
.filterNot(d => d.symbol.usedDefContains)
497
524
.filterNot(d => containsSyntheticSuffix(d.symbol))
498
525
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
499
- .map(d => d.namePos -> WarnTypes .PatVars ).toList
526
+ .map(d => UnusedSymbol ( d.namePos, d.name, WarnTypes .PatVars ) ).toList
500
527
else
501
528
Nil
502
529
val warnings = List (sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s =>
503
- val pos = s._1 .sourcePos
530
+ val pos = s.pos .sourcePos
504
531
(pos.line, pos.column)
505
532
}
506
- UnusedResult (warnings, Nil )
533
+ UnusedResult (warnings)
507
534
end getUnused
508
535
// ============================ HELPERS ====================================
509
536
@@ -687,7 +714,11 @@ object CheckUnused:
687
714
case _:tpd.Block => Local
688
715
case _ => Other
689
716
717
+ case class UnusedSymbol (pos : SrcPos , name : Name , warnType : WarnTypes )
690
718
/** A container for the results of the used elements analysis */
691
- case class UnusedResult (warnings : List [(dotty.tools.dotc.util.SrcPos , WarnTypes )], usedImports : List [(tpd.Import , untpd.ImportSelector )])
719
+ case class UnusedResult (warnings : List [UnusedSymbol ])
720
+ object UnusedResult :
721
+ val Empty = UnusedResult (Nil )
722
+
692
723
end CheckUnused
693
724
0 commit comments