Skip to content

Commit a8bf3a4

Browse files
committed
Restore resolving prefixes of implicit Ident
1 parent 977232c commit a8bf3a4

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

Diff for: compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

+18-12
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
5858
|| tree.srcPos.isZeroExtentSynthetic
5959
|| refInfos.inlined.exists(_.sourcePos.contains(tree.srcPos.sourcePos))
6060
if resolving && !ignoreTree(tree) then
61+
def loopOverPrefixes(prefix: Type, depth: Int): Unit =
62+
if depth < 10 && prefix.exists && !prefix.classSymbol.isEffectiveRoot then
63+
resolveUsage(prefix.classSymbol, nme.NO_NAME, NoPrefix)
64+
loopOverPrefixes(prefix.normalizedPrefix, depth + 1)
65+
if tree.srcPos.isZeroExtentSynthetic then
66+
loopOverPrefixes(tree.typeOpt.normalizedPrefix, depth = 0)
6167
resolveUsage(tree.symbol, tree.name, tree.typeOpt.importPrefix.skipPackageObject)
6268
else if tree.hasType then
6369
resolveUsage(tree.tpe.classSymbol, tree.name, tree.tpe.importPrefix.skipPackageObject)
@@ -116,7 +122,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
116122
tree
117123

118124
override def prepareForMatch(tree: Match)(using Context): Context =
119-
// exonerate case.pat against tree.selector (simple var pat only for now)
125+
// allow case.pat against tree.selector (simple var pat only for now)
120126
tree.selector match
121127
case Ident(nm) => tree.cases.foreach(k => allowVariableBindings(List(nm), List(k.pat)))
122128
case _ =>
@@ -138,9 +144,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
138144
refInfos.inlined.push(tree.call.srcPos)
139145
ctx
140146
override def transformInlined(tree: Inlined)(using Context): tree.type =
147+
transformAllDeep(tree.expansion) // traverse expansion with nonempty inlined stack to avoid registering defs
141148
val _ = refInfos.inlined.pop()
142-
if !tree.call.isEmpty && phaseMode.eq(PhaseMode.Aggregate) then
143-
transformAllDeep(tree.call)
149+
transformAllDeep(tree.call)
144150
tree
145151

146152
override def prepareForBind(tree: Bind)(using Context): Context =
@@ -155,11 +161,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
155161
traverseAnnotations(tree.symbol)
156162
if tree.name.startsWith("derived$") && tree.hasType then
157163
def loop(t: Tree): Unit = t match
158-
case Ident(name) =>
159-
val target =
160-
val ts0 = t.tpe.typeSymbol
161-
if ts0.is(ModuleClass) then ts0.companionModule else ts0
162-
resolveUsage(target, name, t.tpe.underlyingPrefix.skipPackageObject)
164+
case Ident(name) => resolveUsage(t.tpe.typeSymbol, name, t.tpe.underlyingPrefix.skipPackageObject)
163165
case Select(t, _) => loop(t)
164166
case _ =>
165167
tree.getAttachment(OriginalTypeClass).foreach(loop)
@@ -274,8 +276,9 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
274276
* For Select, lint does not look up `<empty>.scala` (so top-level syms look like magic) but records `scala.Int`.
275277
* For Ident, look-up finds the root import as usual. A competing import is OK because higher precedence.
276278
*/
277-
def resolveUsage(sym: Symbol, name: Name, prefix: Type)(using Context): Unit =
279+
def resolveUsage(sym0: Symbol, name: Name, prefix: Type)(using Context): Unit =
278280
import PrecedenceLevels.*
281+
val sym = sym0.userSymbol
279282

280283
def matchingSelector(info: ImportInfo): ImportSelector | Null =
281284
val qtpe = info.site
@@ -321,7 +324,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
321324

322325
// Avoid spurious NoSymbol and also primary ctors which are never warned about.
323326
// Selections C.this.toString should be already excluded, but backtopped here for eq, etc.
324-
if !sym.exists || sym.isPrimaryConstructor || defn.topClasses(sym.owner) then return
327+
if !sym.exists || sym.isPrimaryConstructor || sym.isEffectiveRoot || defn.topClasses(sym.owner) then return
325328

326329
// Find the innermost, highest precedence. Contexts have no nesting levels but assume correctness.
327330
// If the sym is an enclosing definition (the owner of a context), it does not count toward usages.
@@ -457,10 +460,10 @@ object CheckUnused:
457460
pats.addOne((tree.symbol, tree.namePos))
458461
case tree: NamedDefTree =>
459462
if (tree.symbol ne NoSymbol) && !tree.name.isWildcard then
460-
defs.addOne((tree.symbol, tree.namePos))
463+
defs.addOne((tree.symbol.userSymbol, tree.namePos))
461464
case _ =>
462465
if tree.symbol ne NoSymbol then
463-
defs.addOne((tree.symbol, tree.srcPos))
466+
defs.addOne((tree.symbol, tree.srcPos)) // TODO is this a code path
464467

465468
val inlined = Stack.empty[SrcPos] // enclosing call.srcPos of inlined code (expansions)
466469
var inliners = 0 // depth of inline def (not inlined yet)
@@ -885,6 +888,9 @@ object CheckUnused:
885888
sym.isClass && sym.info.allMembers.forall: d =>
886889
val m = d.symbol
887890
!m.isTerm || m.isSelfSym || m.is(Method) && (m.owner == defn.AnyClass || m.owner == defn.ObjectClass)
891+
// pick the symbol the user wrote for purposes of tracking
892+
inline def userSymbol(using Context): Symbol=
893+
if sym.denot.is(ModuleClass) then sym.denot.companionModule else sym
888894

889895
extension (sel: ImportSelector)
890896
def boundTpe: Type = sel.bound match

Diff for: tests/warn/i22744.scala

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
2+
//> using options -Wunused:privates -Werror
3+
4+
object test {
5+
private trait Foo[A] { val value: A }
6+
7+
private object Foo { // no warn prefix of implicit value
8+
given int: Foo[Int] = new Foo[Int] { val value = 1 }
9+
}
10+
11+
val i = summon[Foo[Int]].value
12+
}
13+
14+
object supplement {
15+
private trait Foo[A] { val value: A }
16+
17+
private object Foo { // no warn prefix of implicit value
18+
given int: Foo[Int] = new Foo[Int] { val value = 1 }
19+
}
20+
21+
private def fooValue[A](using f: Foo[A]): A = f.value
22+
23+
val i = fooValue[Int]
24+
}
25+
26+
package p:
27+
private trait Foo[A] { val value: A }
28+
29+
private object Foo { // no warn prefix of implicit value
30+
given int: Foo[Int] = new Foo[Int] { val value = 1 }
31+
}
32+
33+
private def fooValue[A](using f: Foo[A]): A = f.value
34+
35+
val i = fooValue[Int]

0 commit comments

Comments
 (0)