Skip to content

Commit b0790d1

Browse files
committed
Refactor CheckUnused into a MiniPhase
- Add prepare and tranform method from MiniPhase - Keep previous implementation for cases not covered by MiniPhase traversal (Mostly in the type tree)
1 parent e2b6b61 commit b0790d1

File tree

1 file changed

+106
-22
lines changed

1 file changed

+106
-22
lines changed

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

Lines changed: 106 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import dotty.tools.dotc.core.Types.AnnotatedType
2121
import dotty.tools.dotc.core.Flags.flagsString
2222
import dotty.tools.dotc.core.Flags
2323
import dotty.tools.dotc.core.Names.Name
24+
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
2425

2526

2627

@@ -30,7 +31,7 @@ import dotty.tools.dotc.core.Names.Name
3031
* Basically, it gathers definition/imports and their usage. If a
3132
* definition/imports does not have any usage, then it is reported.
3233
*/
33-
class CheckUnused extends Phase:
34+
class CheckUnused extends MiniPhase:
3435
import CheckUnused.UnusedData
3536

3637
/**
@@ -39,6 +40,13 @@ class CheckUnused extends Phase:
3940
*/
4041
private val _key = Property.Key[UnusedData]
4142

43+
extension (k: Property.Key[UnusedData])
44+
private def unusedDataApply[U](f: UnusedData => U)(using Context): Context =
45+
ctx.property(_key).foreach(f)
46+
ctx
47+
private def getUnusedData(using Context): Option[UnusedData] =
48+
ctx.property(_key)
49+
4250
override def phaseName: String = CheckUnused.phaseName
4351

4452
override def description: String = CheckUnused.description
@@ -47,12 +55,84 @@ class CheckUnused extends Phase:
4755
ctx.settings.Wunused.value.nonEmpty &&
4856
!ctx.isJava
4957

50-
override def run(using Context): Unit =
58+
// ========== SETUP ============
59+
60+
override def prepareForUnit(tree: tpd.Tree)(using Context): Context =
5161
val tree = ctx.compilationUnit.tpdTree
5262
val data = UnusedData()
5363
val fresh = ctx.fresh.setProperty(_key, data)
54-
traverser.traverse(tree)(using fresh)
55-
reportUnused(data.getUnused)
64+
fresh
65+
66+
// ========== END + REPORTING ==========
67+
68+
override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree =
69+
_key.unusedDataApply(ud => reportUnused(ud.getUnused))
70+
tree
71+
72+
// ========== MiniPhase Prepare ==========
73+
override def prepareForOther(tree: tpd.Tree)(using Context): Context =
74+
// A standard tree traverser covers cases not handled by the Mega/MiniPhase
75+
traverser.traverse(tree)
76+
ctx
77+
78+
override def prepareForIdent(tree: tpd.Ident)(using Context): Context =
79+
_key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name)))
80+
81+
override def prepareForSelect(tree: tpd.Select)(using Context): Context =
82+
_key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name)))
83+
84+
override def prepareForBlock(tree: tpd.Block)(using Context): Context =
85+
pushInBlockTemplatePackageDef(tree)
86+
87+
override def prepareForTemplate(tree: tpd.Template)(using Context): Context =
88+
pushInBlockTemplatePackageDef(tree)
89+
90+
override def prepareForPackageDef(tree: tpd.PackageDef)(using Context): Context =
91+
pushInBlockTemplatePackageDef(tree)
92+
93+
override def prepareForValDef(tree: tpd.ValDef)(using Context): Context =
94+
_key.unusedDataApply(_.registerDef(tree))
95+
96+
override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context =
97+
_key.unusedDataApply(_.registerDef(tree))
98+
99+
override def prepareForBind(tree: tpd.Bind)(using Context): Context =
100+
_key.unusedDataApply(_.registerPatVar(tree))
101+
102+
override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context =
103+
typeTraverser(_key.unusedDataApply).traverse(tree.tpe)
104+
ctx
105+
106+
// ========== MiniPhase Transform ==========
107+
108+
override def transformBlock(tree: tpd.Block)(using Context): tpd.Tree =
109+
popOutBlockTemplatePackageDef()
110+
tree
111+
112+
override def transformTemplate(tree: tpd.Template)(using Context): tpd.Tree =
113+
popOutBlockTemplatePackageDef()
114+
tree
115+
116+
override def transformPackageDef(tree: tpd.PackageDef)(using Context): tpd.Tree =
117+
popOutBlockTemplatePackageDef()
118+
tree
119+
120+
// ---------- MiniPhase HELPERS -----------
121+
122+
private def pushInBlockTemplatePackageDef(tree: tpd.Block | tpd.Template | tpd.PackageDef)(using Context): Context =
123+
_key.unusedDataApply { ud =>
124+
ud.pushScope(UnusedData.ScopeType.fromTree(tree))
125+
}
126+
ctx
127+
128+
private def popOutBlockTemplatePackageDef()(using Context): Context =
129+
_key.unusedDataApply { ud =>
130+
ud.popScope()
131+
}
132+
ctx
133+
134+
private def newCtx(tree: tpd.Tree)(using Context) =
135+
if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx
56136

57137
/**
58138
* This traverse is the **main** component of this phase
@@ -66,37 +146,37 @@ class CheckUnused extends Phase:
66146

67147
/* Register every imports, definition and usage */
68148
override def traverse(tree: tpd.Tree)(using Context): Unit =
69-
val unusedDataApply = ctx.property(_key).foreach
70149
val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx
71-
if tree.isDef then // register the annotations for usage
72-
unusedDataApply(_.registerUsedAnnotation(tree.symbol))
73150
tree match
74151
case imp:tpd.Import =>
75-
unusedDataApply(_.registerImport(imp))
152+
_key.unusedDataApply(_.registerImport(imp))
76153
traverseChildren(tree)(using newCtx)
77154
case ident: Ident =>
78-
unusedDataApply(_.registerUsed(ident.symbol, Some(ident.name)))
155+
prepareForIdent(ident)
79156
traverseChildren(tree)(using newCtx)
80157
case sel: Select =>
81-
unusedDataApply(_.registerUsed(sel.symbol, Some(sel.name)))
158+
prepareForSelect(sel)
82159
traverseChildren(tree)(using newCtx)
83160
case _: (tpd.Block | tpd.Template | tpd.PackageDef) =>
84-
unusedDataApply { ud =>
161+
//! DIFFERS FROM MINIPHASE
162+
_key.unusedDataApply { ud =>
85163
ud.inNewScope(ScopeType.fromTree(tree))(traverseChildren(tree)(using newCtx))
86164
}
87165
case t:tpd.ValDef =>
88-
unusedDataApply(_.registerDef(t))
166+
prepareForValDef(t)
89167
traverseChildren(tree)(using newCtx)
90168
case t:tpd.DefDef =>
91-
unusedDataApply(_.registerDef(t))
169+
prepareForDefDef(t)
92170
traverseChildren(tree)(using newCtx)
93171
case t: tpd.Bind =>
94-
unusedDataApply(_.registerPatVar(t))
172+
prepareForBind(t)
95173
traverseChildren(tree)(using newCtx)
96174
case t@tpd.TypeTree() =>
97-
typeTraverser(unusedDataApply).traverse(t.tpe)
175+
//! DIFFERS FROM MINIPHASE
176+
typeTraverser(_key.unusedDataApply).traverse(t.tpe)
98177
traverseChildren(tree)(using newCtx)
99178
case _ =>
179+
//! DIFFERS FROM MINIPHASE
100180
traverseChildren(tree)(using newCtx)
101181
end traverse
102182
end traverser
@@ -153,7 +233,7 @@ object CheckUnused:
153233
import UnusedData.ScopeType
154234

155235
/** The current scope during the tree traversal */
156-
var currScopeType: ScopeType = ScopeType.Other
236+
var currScopeType: MutStack[ScopeType] = MutStack(ScopeType.Other)
157237

158238
/* IMPORTS */
159239
private val impInScope = MutStack(MutSet[tpd.Import]())
@@ -190,11 +270,9 @@ object CheckUnused:
190270
*/
191271
def inNewScope(newScope: ScopeType)(execInNewScope: => Unit)(using Context): Unit =
192272
val prev = currScopeType
193-
currScopeType = newScope
194-
pushScope()
273+
pushScope(newScope)
195274
execInNewScope
196275
popScope()
197-
currScopeType = prev
198276

199277
/** Register all annotations of this symbol's denotation */
200278
def registerUsedAnnotation(sym: Symbol)(using Context): Unit =
@@ -227,23 +305,27 @@ object CheckUnused:
227305

228306
/** Register (or not) some `val` or `def` according to the context, scope and flags */
229307
def registerDef(valOrDef: tpd.ValOrDefDef)(using Context): Unit =
308+
// register the annotations for usage
309+
registerUsedAnnotation(valOrDef.symbol)
230310
if valOrDef.symbol.is(Param) && !isSyntheticMainParam(valOrDef.symbol) then
231311
if valOrDef.symbol.isOneOf(GivenOrImplicit) then
232312
implicitParamInScope += valOrDef
233313
else
234314
explicitParamInScope += valOrDef
235-
else if currScopeType == ScopeType.Local then
315+
else if currScopeType.top == ScopeType.Local then
236316
localDefInScope += valOrDef
237-
else if currScopeType == ScopeType.Template && valOrDef.symbol.is(Private, butNot = SelfName) then
317+
else if currScopeType.top == ScopeType.Template && valOrDef.symbol.is(Private, butNot = SelfName) then
238318
privateDefInScope += valOrDef
239319

240320
/** Register pattern variable */
241321
def registerPatVar(patvar: tpd.Bind)(using Context): Unit =
322+
registerUsedAnnotation(patvar.symbol)
242323
patVarsInScope += patvar
243324

244325
/** enter a new scope */
245-
def pushScope(): Unit =
326+
def pushScope(newScopeType: ScopeType): Unit =
246327
// unused imports :
328+
currScopeType.push(newScopeType)
247329
impInScope.push(MutSet())
248330
usedInScope.push(MutSet())
249331

@@ -275,6 +357,8 @@ object CheckUnused:
275357
usedInScope.top ++= kept
276358
// register usage in this scope for other warnings at the end of the phase
277359
usedDef ++= used.map(_._1)
360+
// retrieve previous scope type
361+
currScopeType.pop
278362
end popScope
279363

280364
/**

0 commit comments

Comments
 (0)