Skip to content

Commit e3cb2e3

Browse files
committed
Make CheckUnused run both after Typer and Inlining
1 parent 00bb3fe commit e3cb2e3

File tree

2 files changed

+65
-33
lines changed

2 files changed

+65
-33
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class Compiler {
3535
protected def frontendPhases: List[List[Phase]] =
3636
List(new Parser) :: // Compiler frontend: scanner, parser
3737
List(new TyperPhase) :: // Compiler frontend: namer, typer
38-
List(new CheckUnused) :: // Check for unused elements
38+
List(CheckUnused.PostTyper) :: // Check for unused elements
3939
List(new YCheckPositions) :: // YCheck positions
4040
List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks
4141
List(new semanticdb.ExtractSemanticDB) :: // Extract info into .semanticdb files
@@ -50,6 +50,7 @@ class Compiler {
5050
List(new Pickler) :: // Generate TASTY info
5151
List(new Inlining) :: // Inline and execute macros
5252
List(new PostInlining) :: // Add mirror support for inlined code
53+
List(CheckUnused.PostInlining) :: // Check for unused elements
5354
List(new Staging) :: // Check staging levels and heal staged types
5455
List(new Splicing) :: // Replace level 1 splices with holes
5556
List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

Lines changed: 63 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames
1313
import dotty.tools.dotc.report
1414
import dotty.tools.dotc.reporting.Message
1515
import dotty.tools.dotc.typer.ImportInfo
16-
import dotty.tools.dotc.util.{Property, SrcPos}
16+
import dotty.tools.dotc.util.{Property, SrcPos, Store}
1717
import dotty.tools.dotc.core.Mode
1818
import dotty.tools.dotc.core.Types.{AnnotatedType, ConstantType, NoType, TermRef, Type, TypeTraverser}
1919
import dotty.tools.dotc.core.Flags.flagsString
@@ -24,6 +24,7 @@ import dotty.tools.dotc.core.Annotations
2424
import dotty.tools.dotc.core.Definitions
2525
import dotty.tools.dotc.core.NameKinds.WildcardParamName
2626
import dotty.tools.dotc.core.Symbols.Symbol
27+
import dotty.tools.dotc.interfaces.CompilerCallback
2728

2829

2930

@@ -33,22 +34,15 @@ import dotty.tools.dotc.core.Symbols.Symbol
3334
* Basically, it gathers definition/imports and their usage. If a
3435
* definition/imports does not have any usage, then it is reported.
3536
*/
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.*
4440

4541
private def unusedDataApply[U](f: UnusedData => U)(using Context): Context =
4642
ctx.property(_key).foreach(f)
4743
ctx
48-
private def getUnusedData(using Context): Option[UnusedData] =
49-
ctx.property(_key)
5044

51-
override def phaseName: String = CheckUnused.phaseName
45+
override def phaseName: String = CheckUnused.phaseNamePrefix + suffix
5246

5347
override def description: String = CheckUnused.description
5448

@@ -60,13 +54,21 @@ class CheckUnused extends MiniPhase:
6054

6155
override def prepareForUnit(tree: tpd.Tree)(using Context): Context =
6256
val data = UnusedData()
57+
tree.getAttachment(_key).foreach(oldData =>
58+
data.unusedAggregate = oldData.unusedAggregate
59+
)
6360
val fresh = ctx.fresh.setProperty(_key, data)
61+
tree.putAttachment(_key, data)
6462
fresh
6563

6664
// ========== END + REPORTING ==========
6765

6866
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+
}
7072
tree
7173

7274
// ========== MiniPhase Prepare ==========
@@ -249,31 +251,45 @@ class CheckUnused extends MiniPhase:
249251
private def traverseAnnotations(sym: Symbol)(using Context): Unit =
250252
sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree))
251253

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+
252265
/** Do the actual reporting given the result of the anaylsis */
253266
private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit =
254-
import CheckUnused.WarnTypes
255267
res.warnings.foreach { s =>
256268
s match
257-
case (t, WarnTypes.Imports) =>
269+
case UnusedSymbol(t, _, WarnTypes.Imports) =>
258270
report.warning(s"unused import", t)
259-
case (t, WarnTypes.LocalDefs) =>
271+
case UnusedSymbol(t, _, WarnTypes.LocalDefs) =>
260272
report.warning(s"unused local definition", t)
261-
case (t, WarnTypes.ExplicitParams) =>
273+
case UnusedSymbol(t, _, WarnTypes.ExplicitParams) =>
262274
report.warning(s"unused explicit parameter", t)
263-
case (t, WarnTypes.ImplicitParams) =>
275+
case UnusedSymbol(t, _, WarnTypes.ImplicitParams) =>
264276
report.warning(s"unused implicit parameter", t)
265-
case (t, WarnTypes.PrivateMembers) =>
277+
case UnusedSymbol(t, _, WarnTypes.PrivateMembers) =>
266278
report.warning(s"unused private member", t)
267-
case (t, WarnTypes.PatVars) =>
279+
case UnusedSymbol(t, _, WarnTypes.PatVars) =>
268280
report.warning(s"unused pattern variable", t)
269281
}
270282

271283
end CheckUnused
272284

273285
object CheckUnused:
274-
val phaseName: String = "checkUnused"
286+
val phaseNamePrefix: String = "checkUnused"
275287
val description: String = "check for unused elements"
276288

289+
enum PhaseMode:
290+
case Aggregate
291+
case Report
292+
277293
private enum WarnTypes:
278294
case Imports
279295
case LocalDefs
@@ -282,20 +298,30 @@ object CheckUnused:
282298
case PrivateMembers
283299
case PatVars
284300

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+
285310
/**
286311
* A stateful class gathering the infos on :
287312
* - imports
288313
* - definitions
289314
* - usage
290315
*/
291316
private class UnusedData:
292-
import dotty.tools.dotc.transform.CheckUnused.UnusedData.UnusedResult
293317
import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack}
294-
import UnusedData.ScopeType
318+
import UnusedData.*
295319

296320
/** The current scope during the tree traversal */
297321
var currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other)
298322

323+
var unusedAggregate: Option[UnusedResult] = None
324+
299325
/* IMPORTS */
300326
private val impInScope = MutStack(MutSet[tpd.Import]())
301327
/**
@@ -449,12 +475,13 @@ object CheckUnused:
449475
*
450476
* The given `List` is sorted by line and then column of the position
451477
*/
478+
452479
def getUnused(using Context): UnusedResult =
453480
popScope()
454481

455482
val sortedImp =
456483
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
458485
else
459486
Nil
460487
val sortedLocalDefs =
@@ -463,31 +490,31 @@ object CheckUnused:
463490
.filterNot(d => d.symbol.usedDefContains)
464491
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
465492
.filterNot(d => containsSyntheticSuffix(d.symbol))
466-
.map(d => d.namePos -> WarnTypes.LocalDefs).toList
493+
.map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.LocalDefs)).toList
467494
else
468495
Nil
469496
val sortedExplicitParams =
470497
if ctx.settings.WunusedHas.explicits then
471498
explicitParamInScope
472499
.filterNot(d => d.symbol.usedDefContains)
473500
.filterNot(d => containsSyntheticSuffix(d.symbol))
474-
.map(d => d.namePos -> WarnTypes.ExplicitParams).toList
501+
.map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ExplicitParams)).toList
475502
else
476503
Nil
477504
val sortedImplicitParams =
478505
if ctx.settings.WunusedHas.implicits then
479506
implicitParamInScope
480507
.filterNot(d => d.symbol.usedDefContains)
481508
.filterNot(d => containsSyntheticSuffix(d.symbol))
482-
.map(d => d.namePos -> WarnTypes.ImplicitParams).toList
509+
.map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.ImplicitParams)).toList
483510
else
484511
Nil
485512
val sortedPrivateDefs =
486513
if ctx.settings.WunusedHas.privates then
487514
privateDefInScope
488515
.filterNot(d => d.symbol.usedDefContains)
489516
.filterNot(d => containsSyntheticSuffix(d.symbol))
490-
.map(d => d.namePos -> WarnTypes.PrivateMembers).toList
517+
.map(d => UnusedSymbol(d.namePos, d.name, WarnTypes.PrivateMembers)).toList
491518
else
492519
Nil
493520
val sortedPatVars =
@@ -496,14 +523,14 @@ object CheckUnused:
496523
.filterNot(d => d.symbol.usedDefContains)
497524
.filterNot(d => containsSyntheticSuffix(d.symbol))
498525
.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
500527
else
501528
Nil
502529
val warnings = List(sortedImp, sortedLocalDefs, sortedExplicitParams, sortedImplicitParams, sortedPrivateDefs, sortedPatVars).flatten.sortBy { s =>
503-
val pos = s._1.sourcePos
530+
val pos = s.pos.sourcePos
504531
(pos.line, pos.column)
505532
}
506-
UnusedResult(warnings, Nil)
533+
UnusedResult(warnings)
507534
end getUnused
508535
//============================ HELPERS ====================================
509536

@@ -687,7 +714,11 @@ object CheckUnused:
687714
case _:tpd.Block => Local
688715
case _ => Other
689716

717+
case class UnusedSymbol(pos: SrcPos, name: Name, warnType: WarnTypes)
690718
/** 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+
692723
end CheckUnused
693724

0 commit comments

Comments
 (0)