Skip to content

Commit 34838d5

Browse files
szymon-rdWojciechMazur
authored andcommitted
Fix wunused false positive on CanEqual
[Cherry-picked fa72e70]
1 parent 8ced7d8 commit 34838d5

File tree

2 files changed

+38
-5
lines changed

2 files changed

+38
-5
lines changed

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

+17-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package dotty.tools.dotc.transform
22

33
import dotty.tools.dotc.ast.tpd
4+
import dotty.tools.dotc.core.Symbols.*
45
import dotty.tools.dotc.ast.tpd.{Inlined, TreeTraverser}
56
import dotty.tools.dotc.ast.untpd
67
import dotty.tools.dotc.ast.untpd.ImportSelector
@@ -423,12 +424,12 @@ object CheckUnused:
423424
if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then
424425
impInScope.top += imp
425426
unusedImport ++= imp.selectors.filter { s =>
426-
!shouldSelectorBeReported(imp, s) && !isImportExclusion(s)
427+
!shouldSelectorBeReported(imp, s) && !isImportExclusion(s) && !isImportIgnored(imp, s)
427428
}
428429

429430
/** Register (or not) some `val` or `def` according to the context, scope and flags */
430431
def registerDef(memDef: tpd.MemberDef)(using Context): Unit =
431-
if memDef.isValidMemberDef then
432+
if memDef.isValidMemberDef && !isDefIgnored(memDef) then
432433
if memDef.isValidParam then
433434
if memDef.symbol.isOneOf(GivenOrImplicit) then
434435
if !paramsToSkip.contains(memDef.symbol) then
@@ -507,7 +508,6 @@ object CheckUnused:
507508

508509
def getUnused(using Context): UnusedResult =
509510
popScope()
510-
511511
val sortedImp =
512512
if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then
513513
unusedImport.map(d => UnusedSymbol(d.srcPos, d.name, WarnTypes.Imports)).toList
@@ -643,9 +643,21 @@ object CheckUnused:
643643
sel.isWildcard ||
644644
imp.expr.tpe.member(sel.name.toTermName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) ||
645645
imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit))
646-
)
647-
646+
)
647+
648+
/**
649+
* Ignore CanEqual imports
650+
*/
651+
private def isImportIgnored(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean =
652+
(sel.isWildcard && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) ||
653+
(imp.expr.tpe.member(sel.name.toTermName).alternatives
654+
.exists(p => p.symbol.isOneOf(GivenOrImplicit) && p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass))))
648655

656+
/**
657+
* Ignore definitions of CanEqual given
658+
*/
659+
private def isDefIgnored(memDef: tpd.MemberDef)(using Context): Boolean =
660+
memDef.symbol.isOneOf(GivenOrImplicit) && memDef.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass))
649661

650662
extension (tree: ImportSelector)
651663
def boundTpe: Type = tree.bound match {

tests/pos/i17762.scala

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//> using options -Xfatal-warnings -Wunused:all
2+
3+
class SomeType
4+
5+
def testIt(st1: SomeType, st2: SomeType): Boolean =
6+
given CanEqual[SomeType, SomeType] = CanEqual.derived
7+
st1 == st2
8+
9+
object HasCanEqual:
10+
given f: CanEqual[SomeType, SomeType] = CanEqual.derived
11+
12+
object UsesCanEqual:
13+
import HasCanEqual.given
14+
def testIt(st1: SomeType, st2: SomeType): Boolean =
15+
st1 == st2
16+
17+
object UsesCanEqual2:
18+
import HasCanEqual.f
19+
20+
def testIt(st1: SomeType, st2: SomeType): Boolean =
21+
st1 == st2

0 commit comments

Comments
 (0)