Skip to content

Commit 3a07f34

Browse files
committed
Consider superclass context
1 parent 8ddf038 commit 3a07f34

File tree

2 files changed

+34
-19
lines changed

2 files changed

+34
-19
lines changed

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

Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,19 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
7272
def loopOnNormalizedPrefixes(prefix: Type, depth: Int): Unit =
7373
// limit to 10 as failsafe for the odd case where there is an infinite cycle
7474
if depth < 10 && prefix.exists then
75-
ud.registerUsed(prefix.classSymbol, name = None, prefix)
75+
ud.registerUsed(prefix.classSymbol, name = None, prefix, tree = tree)
7676
loopOnNormalizedPrefixes(prefix.normalizedPrefix, depth + 1)
7777
val prefix = tree.typeOpt.normalizedPrefix
7878
loopOnNormalizedPrefixes(prefix, depth = 0)
79-
ud.registerUsed(tree.symbol, Some(tree.name), tree.typeOpt.importPrefix.skipPackageObject)
79+
ud.registerUsed(tree.symbol, Some(tree.name), tree.typeOpt.importPrefix.skipPackageObject, tree = tree)
8080
else if tree.hasType then
81-
ud.registerUsed(tree.tpe.classSymbol, Some(tree.name), tree.tpe.importPrefix.skipPackageObject)
81+
ud.registerUsed(tree.tpe.classSymbol, Some(tree.name), tree.tpe.importPrefix.skipPackageObject, tree = tree)
8282
tree
8383

8484
override def transformSelect(tree: Select)(using Context): tree.type =
8585
preparing:
8686
val name = tree.removeAttachment(OriginalName)
87-
ud.registerUsed(tree.symbol, name, tree.qualifier.tpe, includeForImport = tree.qualifier.span.isSynthetic)
87+
ud.registerUsed(tree.symbol, name, tree.qualifier.tpe, includeForImport = tree.qualifier.span.isSynthetic, tree = tree)
8888
tree
8989

9090
override def transformApply(tree: Apply)(using Context): Tree =
@@ -144,7 +144,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
144144
tree.getAttachment(OriginalTypeClass) match
145145
case Some(orig) =>
146146
val (typsym, name, prefix) = core(orig)
147-
ud.registerUsed(typsym, name, prefix.skipPackageObject)
147+
ud.registerUsed(typsym, name, prefix.skipPackageObject, tree = EmptyTree)
148148
case _ =>
149149
ud.removeIgnoredUsage(tree.symbol)
150150
tree
@@ -176,7 +176,7 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
176176
tree
177177

178178
override def prepareForTemplate(tree: Template)(using Context): Context =
179-
pushScope(tree)
179+
pushScope(tree, tree.parents)
180180

181181
override def transformTemplate(tree: Template)(using Context): Tree =
182182
popScope(tree)
@@ -229,9 +229,9 @@ class CheckUnused private (phaseMode: CheckUnused.PhaseMode, suffix: String, _ke
229229
//println(s"OTHER ${tree.getClass} ${tree.show}")
230230
tree
231231

232-
private def pushScope(tree: Block | Template | PackageDef)(using Context): Context =
232+
private def pushScope(tree: Block | Template | PackageDef, parents: List[Tree] = Nil)(using Context): Context =
233233
preparing:
234-
ud.pushScope(UnusedData.ScopeType.fromTree(tree))
234+
ud.pushScope(UnusedData.ScopeType.fromTree(tree), parents)
235235

236236
private def popScope(tree: Block | Template | PackageDef)(using Context): Context =
237237
preparing:
@@ -307,14 +307,17 @@ object CheckUnused:
307307

308308
var unusedAggregate: Option[UnusedResult] = None
309309

310-
/* IMPORTS */
310+
// Trees of superclass constructors, i.e., template.parents when currScopeType is Template.
311+
// Ideally, Context would supply correct context and scope; instead, trees in superclass context
312+
// are promoted to "enclosing scope" by popScope. (This is just for import usage, so class params are ignored.)
313+
private val parents = Stack(List.empty[Tree])
314+
311315
private val impInScope = Stack(ListBuffer.empty[ImportSelectorData])
312316
private val usedInScope = Stack(mut.Map.empty[Symbol, ListBuffer[Usage]])
313317
private val usedInPosition = mut.Map.empty[Name, mut.Set[Symbol]]
314318
/* unused import collected during traversal */
315319
private val unusedImport = ListBuffer.empty[ImportSelectorData]
316320

317-
/* LOCAL DEF OR VAL / Private Def or Val / Pattern variables */
318321
private val localDefInScope = ListBuffer.empty[MemberDef]
319322
private val privateDefInScope = ListBuffer.empty[MemberDef]
320323
private val explicitParamInScope = ListBuffer.empty[MemberDef]
@@ -355,11 +358,11 @@ object CheckUnused:
355358
* The optional name will be used to target the right import
356359
* as the same element can be imported with different renaming.
357360
*/
358-
def registerUsed(sym: Symbol, name: Option[Name], prefix: Type = NoType, includeForImport: Boolean = true)(using Context): Unit =
361+
def registerUsed(sym: Symbol, name: Option[Name], prefix: Type = NoType, includeForImport: Boolean = true, tree: Tree)(using Context): Unit =
359362
if sym.exists && !isConstructorOfSynth(sym) && !doNotRegister(sym) && !doNotRegisterPrefix(prefix.typeSymbol) then
360363
if sym.isConstructor then
361364
// constructors are "implicitly" imported with the class
362-
registerUsed(sym.owner, name = None, prefix, includeForImport = includeForImport)
365+
registerUsed(sym.owner, name = None, prefix, includeForImport = includeForImport, tree = tree)
363366
else
364367
// If the symbol is accessible in this scope without an import, do not register it for unused import analysis
365368
val includeForImport1 =
@@ -369,7 +372,7 @@ object CheckUnused:
369372
if sym.exists then
370373
usedDef += sym
371374
if includeForImport1 then
372-
addUsage(Usage(sym, name, prefix))
375+
addUsage(Usage(sym, name, prefix, isSuper = !tree.isEmpty && parents.top.exists(t => t.find(_ eq tree).isDefined)))
373376
addIfExists(sym)
374377
addIfExists(sym.companionModule)
375378
addIfExists(sym.companionClass)
@@ -379,7 +382,7 @@ object CheckUnused:
379382

380383
def addUsage(usage: Usage)(using Context): Unit =
381384
val usages = usedInScope.top.getOrElseUpdate(usage.symbol, ListBuffer.empty)
382-
if !usages.exists(x => x.name == usage.name && x.prefix =:= usage.prefix)
385+
if !usages.exists(x => x.name == usage.name && x.prefix =:= usage.prefix && x.isSuper == usage.isSuper)
383386
then usages += usage
384387

385388
/** Register a symbol that should be ignored */
@@ -437,10 +440,11 @@ object CheckUnused:
437440
patVarsInScope += patvar
438441

439442
/** enter a new scope */
440-
def pushScope(newScopeType: ScopeType): Unit =
443+
def pushScope(newScopeType: ScopeType, parents: List[Tree]): Unit =
441444
currScopeType.push(newScopeType)
442445
impInScope.push(ListBuffer.empty)
443446
usedInScope.push(mut.Map.empty)
447+
this.parents.push(parents)
444448

445449
def registerSetVar(sym: Symbol): Unit =
446450
setVars += sym
@@ -453,7 +457,9 @@ object CheckUnused:
453457

454458
for usedInfos <- usedInScope.pop().valuesIterator; usedInfo <- usedInfos do
455459
import usedInfo.*
456-
selDatas.find(symbol.isInImport(_, name, prefix)) match
460+
if isSuper then
461+
addUsage(Usage(symbol, name, prefix, isSuper = false)) // approximate superclass context
462+
else selDatas.find(symbol.isInImport(_, name, prefix)) match
457463
case Some(sel) =>
458464
sel.markUsed()
459465
case None =>
@@ -465,6 +471,8 @@ object CheckUnused:
465471
for selData <- selDatas do
466472
if !selData.isUsed then
467473
unusedImport += selData
474+
475+
this.parents.pop()
468476
end popScope
469477

470478
/** Leave the scope and return a result set of warnings.
@@ -746,7 +754,7 @@ object CheckUnused:
746754
/** A symbol usage includes the name under which it was observed,
747755
* and the prefix from which it was selected.
748756
*/
749-
case class Usage(val symbol: Symbol, val name: Option[Name], val prefix: Type)
757+
class Usage(val symbol: Symbol, val name: Option[Name], val prefix: Type, val isSuper: Boolean)
750758
end UnusedData
751759
extension (sym: Symbol)
752760
/** is accessible without import in current context */

tests/warn/i19657.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,17 @@ def `old-style constants are usages`: Unit =
6969
import Constants.i
7070
println(i + Local.j)
7171

72+
object Constantinople:
73+
val k = 42
7274
class `scope of super`:
73-
import Constants.i // bad warn
75+
import Constants.i // was bad warn
7476
class C(x: Int):
7577
def y = x
76-
class D extends C(i):
78+
class D(j: Int) extends C(i + j):
7779
import Constants.* // does not resolve i in C(i)
7880
def m = i
81+
def f =
82+
import Constantinople.*
83+
class E(e: Int) extends C(i + k):
84+
def g = e + y + k + 1
85+
E(0).g

0 commit comments

Comments
 (0)