Skip to content

Commit 5e866e3

Browse files
authored
Backport "Fix wunused false positive when deriving alias type" (#17276)
Backports #17157
2 parents ae37e68 + ec298fa commit 5e866e3

File tree

2 files changed

+42
-12
lines changed

2 files changed

+42
-12
lines changed

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

+28-12
Original file line numberDiff line numberDiff line change
@@ -24,7 +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-
27+
import dotty.tools.dotc.core.StdNames.nme
2828

2929

3030
/**
@@ -109,6 +109,9 @@ class CheckUnused extends MiniPhase:
109109
traverseAnnotations(tree.symbol)
110110
if !tree.symbol.is(Module) then
111111
ud.registerDef(tree)
112+
if tree.name.mangledString.startsWith(nme.derived.mangledString + "$")
113+
&& tree.typeOpt != NoType then
114+
ud.registerUsed(tree.typeOpt.typeSymbol, None, true)
112115
ud.addIgnoredUsage(tree.symbol)
113116
}
114117

@@ -304,7 +307,7 @@ object CheckUnused:
304307
*
305308
* See the `isAccessibleAsIdent` extension method below in the file
306309
*/
307-
private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]())
310+
private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name], Boolean)]())
308311
private val usedInPosition = MutSet[(SrcPos, Name)]()
309312
/* unused import collected during traversal */
310313
private val unusedImport = MutSet[ImportSelector]()
@@ -347,14 +350,14 @@ object CheckUnused:
347350
* The optional name will be used to target the right import
348351
* as the same element can be imported with different renaming
349352
*/
350-
def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit =
353+
def registerUsed(sym: Symbol, name: Option[Name], isDerived: Boolean = false)(using Context): Unit =
351354
if !isConstructorOfSynth(sym) && !doNotRegister(sym) then
352355
if sym.isConstructor && sym.exists then
353356
registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class
354357
else
355-
usedInScope.top += ((sym, sym.isAccessibleAsIdent, name))
356-
usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name))
357-
usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name))
358+
usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived))
359+
usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived))
360+
usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived))
358361
name.map(n => usedInPosition += ((sym.sourcePos, n)))
359362

360363
/** Register a symbol that should be ignored */
@@ -408,15 +411,15 @@ object CheckUnused:
408411
// used symbol in this scope
409412
val used = usedInScope.pop().toSet
410413
// used imports in this scope
411-
val imports = impInScope.pop().toSet
414+
val imports = impInScope.pop()
412415
val kept = used.filterNot { t =>
413-
val (sym, isAccessible, optName) = t
416+
val (sym, isAccessible, optName, isDerived) = t
414417
// keep the symbol for outer scope, if it matches **no** import
415418
// This is the first matching wildcard selector
416419
var selWildCard: Option[ImportSelector] = None
417420

418421
val exists = imports.exists { imp =>
419-
sym.isInImport(imp, isAccessible, optName) match
422+
sym.isInImport(imp, isAccessible, optName, isDerived) match
420423
case None => false
421424
case optSel@Some(sel) if sel.isWildcard =>
422425
if selWildCard.isEmpty then selWildCard = optSel
@@ -587,16 +590,29 @@ object CheckUnused:
587590
}
588591

589592
/** Given an import and accessibility, return an option of selector that match import<->symbol */
590-
private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] =
593+
private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] =
591594
val tpd.Import(qual, sels) = imp
592-
val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).contains(sym)
595+
val dealiasedSym = dealias(sym)
596+
val simpleSelections = qual.tpe.member(sym.name).alternatives
597+
val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives)
598+
val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives)
599+
val selectionsToDealias = typeSelections ::: termSelections
600+
val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym)
593601
def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true))
602+
def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect {
603+
case (sel, sym) if dealias(sym) == dealiasedSym => sel
604+
}.headOption else None
594605
def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit)))
595606
if qualHasSymbol && !isAccessible && sym.exists then
596-
selector.orElse(wildcard) // selector with name or wildcard (or given)
607+
selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given)
597608
else
598609
None
599610

611+
612+
private def dealias(symbol: Symbol)(using Context): Symbol =
613+
if(symbol.isType && symbol.asType.denot.isAliasType) then
614+
symbol.asType.typeRef.dealias.typeSymbol
615+
else symbol
600616
/** Annotated with @unused */
601617
private def isUnusedAnnot(using Context): Boolean =
602618
sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot)

tests/neg-custom-args/fatal-warnings/i15503i.scala

+14
Original file line numberDiff line numberDiff line change
@@ -273,3 +273,17 @@ package foo.test.i16679b:
273273
import Foo.x
274274
case class CoolClass(i: Int)
275275
println(summon[myPackage.CaseClassName[CoolClass]])
276+
277+
package foo.test.i17156:
278+
package a:
279+
trait Foo[A]
280+
object Foo:
281+
inline def derived[T]: Foo[T] = new Foo{}
282+
283+
package b:
284+
import a.Foo
285+
type Xd[A] = Foo[A]
286+
287+
package c:
288+
import b.Xd
289+
trait Z derives Xd

0 commit comments

Comments
 (0)