Skip to content

Commit da0a934

Browse files
authored
Check for synthetic case methods in unused lint (#24239)
Fixes #24235 References to a case class from synthetic methods in its companion are not usages for `-Wunused`.
1 parent 55f235c commit da0a934

File tree

2 files changed

+32
-7
lines changed

2 files changed

+32
-7
lines changed

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -303,10 +303,23 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
303303
for annot <- sym.denot.annotations do
304304
transformAllDeep(annot.tree)
305305

306-
// if sym is not an enclosing element, record the reference
306+
/** If sym is not an enclosing element with respect to the give context, record the reference
307+
*
308+
* Also check that every enclosing element is not a synthetic member
309+
* of the sym's case class companion module.
310+
*/
307311
def refUsage(sym: Symbol)(using Context): Unit =
308-
if !ctx.outersIterator.exists(cur => cur.owner eq sym) then
309-
refInfos.addRef(sym)
312+
if !refInfos.hasRef(sym) then
313+
val isCase = sym.is(Case) && sym.isClass
314+
if !ctx.outersIterator.exists: outer =>
315+
val owner = outer.owner
316+
owner.eq(sym)
317+
|| isCase
318+
&& owner.exists
319+
&& owner.is(Synthetic)
320+
&& owner.owner.eq(sym.companionModule.moduleClass)
321+
then
322+
refInfos.addRef(sym)
310323

311324
/** Look up a reference in enclosing contexts to determine whether it was introduced by a definition or import.
312325
* The binding of highest precedence must then be correct.
@@ -422,7 +435,7 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
422435
end while
423436
// record usage and possibly an import
424437
if !enclosed then
425-
refInfos.addRef(sym)
438+
refUsage(sym)
426439
if imports && candidate != NoContext && candidate.isImportContext && importer != null then
427440
refInfos.sels.put(importer, ())
428441
end resolveUsage
@@ -442,15 +455,15 @@ class CheckUnused private (phaseMode: PhaseMode, suffix: String) extends MiniPha
442455
else Nil
443456
implicitRefs.find(ref => ref.underlyingRef.widen <:< tp) match
444457
case Some(found: TermRef) =>
445-
refInfos.addRef(found.denot.symbol)
458+
refUsage(found.denot.symbol)
446459
if cur.isImportContext then
447460
cur.importInfo.nn.selectors.find(sel => sel.isGiven || sel.rename == found.name) match
448461
case Some(sel) =>
449462
refInfos.sels.put(sel, ())
450463
case _ =>
451464
return
452465
case Some(found: RenamedImplicitRef) if cur.isImportContext =>
453-
refInfos.addRef(found.underlyingRef.denot.symbol)
466+
refUsage(found.underlyingRef.denot.symbol)
454467
cur.importInfo.nn.selectors.find(sel => sel.rename == found.implicitName) match
455468
case Some(sel) =>
456469
refInfos.sels.put(sel, ())
@@ -529,13 +542,18 @@ object CheckUnused:
529542

530543
var inliners = 0 // depth of inline def (not inlined yet)
531544

532-
// instead of refs.addOne, use addRef to distinguish a read from a write to var
545+
// instead of refs.addOne, use refUsage -> addRef to distinguish a read from a write to var
533546
var isAssignment = false
534547
def addRef(sym: Symbol): Unit =
535548
if isAssignment then
536549
asss.addOne(sym)
537550
else
538551
refs.addOne(sym)
552+
def hasRef(sym: Symbol): Boolean =
553+
if isAssignment then
554+
asss(sym)
555+
else
556+
refs(sym)
539557

540558
// currently compiletime.testing is completely erased, so ignore the unit
541559
var isNullified = false

tests/warn/i15503c.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,10 @@ object LazyVals:
7070

7171
final class Waiting extends CountDownLatch(1), LazyValControlState:
7272
private def writeReplace(): Any = null
73+
74+
package i24235:
75+
object Test:
76+
private case class Unused() // warn
77+
private class Regular // nowarn
78+
private object Regular: // warn
79+
def r = Regular() // usage

0 commit comments

Comments
 (0)