@@ -21,6 +21,7 @@ import dotty.tools.dotc.core.Types.AnnotatedType
21
21
import dotty .tools .dotc .core .Flags .flagsString
22
22
import dotty .tools .dotc .core .Flags
23
23
import dotty .tools .dotc .core .Names .Name
24
+ import dotty .tools .dotc .transform .MegaPhase .MiniPhase
24
25
25
26
26
27
@@ -30,7 +31,7 @@ import dotty.tools.dotc.core.Names.Name
30
31
* Basically, it gathers definition/imports and their usage. If a
31
32
* definition/imports does not have any usage, then it is reported.
32
33
*/
33
- class CheckUnused extends Phase :
34
+ class CheckUnused extends MiniPhase :
34
35
import CheckUnused .UnusedData
35
36
36
37
/**
@@ -39,6 +40,13 @@ class CheckUnused extends Phase:
39
40
*/
40
41
private val _key = Property .Key [UnusedData ]
41
42
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
+
42
50
override def phaseName : String = CheckUnused .phaseName
43
51
44
52
override def description : String = CheckUnused .description
@@ -47,12 +55,84 @@ class CheckUnused extends Phase:
47
55
ctx.settings.Wunused .value.nonEmpty &&
48
56
! ctx.isJava
49
57
50
- override def run (using Context ): Unit =
58
+ // ========== SETUP ============
59
+
60
+ override def prepareForUnit (tree : tpd.Tree )(using Context ): Context =
51
61
val tree = ctx.compilationUnit.tpdTree
52
62
val data = UnusedData ()
53
63
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
56
136
57
137
/**
58
138
* This traverse is the **main** component of this phase
@@ -66,37 +146,37 @@ class CheckUnused extends Phase:
66
146
67
147
/* Register every imports, definition and usage */
68
148
override def traverse (tree : tpd.Tree )(using Context ): Unit =
69
- val unusedDataApply = ctx.property(_key).foreach
70
149
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))
73
150
tree match
74
151
case imp: tpd.Import =>
75
- unusedDataApply(_.registerImport(imp))
152
+ _key. unusedDataApply(_.registerImport(imp))
76
153
traverseChildren(tree)(using newCtx)
77
154
case ident : Ident =>
78
- unusedDataApply(_.registerUsed( ident.symbol, Some (ident.name)) )
155
+ prepareForIdent( ident)
79
156
traverseChildren(tree)(using newCtx)
80
157
case sel : Select =>
81
- unusedDataApply(_.registerUsed( sel.symbol, Some (sel.name)) )
158
+ prepareForSelect( sel)
82
159
traverseChildren(tree)(using newCtx)
83
160
case _ : (tpd.Block | tpd.Template | tpd.PackageDef ) =>
84
- unusedDataApply { ud =>
161
+ // ! DIFFERS FROM MINIPHASE
162
+ _key.unusedDataApply { ud =>
85
163
ud.inNewScope(ScopeType .fromTree(tree))(traverseChildren(tree)(using newCtx))
86
164
}
87
165
case t: tpd.ValDef =>
88
- unusedDataApply(_.registerDef(t) )
166
+ prepareForValDef(t )
89
167
traverseChildren(tree)(using newCtx)
90
168
case t: tpd.DefDef =>
91
- unusedDataApply(_.registerDef(t) )
169
+ prepareForDefDef(t )
92
170
traverseChildren(tree)(using newCtx)
93
171
case t : tpd.Bind =>
94
- unusedDataApply(_.registerPatVar(t) )
172
+ prepareForBind(t )
95
173
traverseChildren(tree)(using newCtx)
96
174
case t@ tpd.TypeTree () =>
97
- typeTraverser(unusedDataApply).traverse(t.tpe)
175
+ // ! DIFFERS FROM MINIPHASE
176
+ typeTraverser(_key.unusedDataApply).traverse(t.tpe)
98
177
traverseChildren(tree)(using newCtx)
99
178
case _ =>
179
+ // ! DIFFERS FROM MINIPHASE
100
180
traverseChildren(tree)(using newCtx)
101
181
end traverse
102
182
end traverser
@@ -153,7 +233,7 @@ object CheckUnused:
153
233
import UnusedData .ScopeType
154
234
155
235
/** The current scope during the tree traversal */
156
- var currScopeType : ScopeType = ScopeType .Other
236
+ var currScopeType : MutStack [ ScopeType ] = MutStack ( ScopeType .Other )
157
237
158
238
/* IMPORTS */
159
239
private val impInScope = MutStack (MutSet [tpd.Import ]())
@@ -190,11 +270,9 @@ object CheckUnused:
190
270
*/
191
271
def inNewScope (newScope : ScopeType )(execInNewScope : => Unit )(using Context ): Unit =
192
272
val prev = currScopeType
193
- currScopeType = newScope
194
- pushScope()
273
+ pushScope(newScope)
195
274
execInNewScope
196
275
popScope()
197
- currScopeType = prev
198
276
199
277
/** Register all annotations of this symbol's denotation */
200
278
def registerUsedAnnotation (sym : Symbol )(using Context ): Unit =
@@ -227,23 +305,27 @@ object CheckUnused:
227
305
228
306
/** Register (or not) some `val` or `def` according to the context, scope and flags */
229
307
def registerDef (valOrDef : tpd.ValOrDefDef )(using Context ): Unit =
308
+ // register the annotations for usage
309
+ registerUsedAnnotation(valOrDef.symbol)
230
310
if valOrDef.symbol.is(Param ) && ! isSyntheticMainParam(valOrDef.symbol) then
231
311
if valOrDef.symbol.isOneOf(GivenOrImplicit ) then
232
312
implicitParamInScope += valOrDef
233
313
else
234
314
explicitParamInScope += valOrDef
235
- else if currScopeType == ScopeType .Local then
315
+ else if currScopeType.top == ScopeType .Local then
236
316
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
238
318
privateDefInScope += valOrDef
239
319
240
320
/** Register pattern variable */
241
321
def registerPatVar (patvar : tpd.Bind )(using Context ): Unit =
322
+ registerUsedAnnotation(patvar.symbol)
242
323
patVarsInScope += patvar
243
324
244
325
/** enter a new scope */
245
- def pushScope (): Unit =
326
+ def pushScope (newScopeType : ScopeType ): Unit =
246
327
// unused imports :
328
+ currScopeType.push(newScopeType)
247
329
impInScope.push(MutSet ())
248
330
usedInScope.push(MutSet ())
249
331
@@ -275,6 +357,8 @@ object CheckUnused:
275
357
usedInScope.top ++= kept
276
358
// register usage in this scope for other warnings at the end of the phase
277
359
usedDef ++= used.map(_._1)
360
+ // retrieve previous scope type
361
+ currScopeType.pop
278
362
end popScope
279
363
280
364
/**
0 commit comments