Skip to content

Commit

Permalink
Fix wunused false positive on CanEqual (#18641)
Browse files Browse the repository at this point in the history
Fixes #17762
  • Loading branch information
szymon-rd authored Oct 10, 2023
2 parents e1b5451 + 88eed71 commit a133b41
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 5 deletions.
22 changes: 17 additions & 5 deletions compiler/src/dotty/tools/dotc/transform/CheckUnused.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dotty.tools.dotc.transform

import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Symbols.*
import dotty.tools.dotc.ast.tpd.{Inlined, TreeTraverser}
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.ast.untpd.ImportSelector
Expand Down Expand Up @@ -423,12 +424,12 @@ object CheckUnused:
if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then
impInScope.top += imp
unusedImport ++= imp.selectors.filter { s =>
!shouldSelectorBeReported(imp, s) && !isImportExclusion(s)
!shouldSelectorBeReported(imp, s) && !isImportExclusion(s) && !isImportIgnored(imp, s)
}

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

def getUnused(using Context): UnusedResult =
popScope()

val sortedImp =
if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then
unusedImport.map(d => UnusedSymbol(d.srcPos, d.name, WarnTypes.Imports)).toList
Expand Down Expand Up @@ -643,9 +643,21 @@ object CheckUnused:
sel.isWildcard ||
imp.expr.tpe.member(sel.name.toTermName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit)) ||
imp.expr.tpe.member(sel.name.toTypeName).alternatives.exists(_.symbol.isOneOf(GivenOrImplicit))
)

)

/**
* Ignore CanEqual imports
*/
private def isImportIgnored(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean =
(sel.isWildcard && sel.isGiven && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)) && p.symbol.isOneOf(GivenOrImplicit))) ||
(imp.expr.tpe.member(sel.name.toTermName).alternatives
.exists(p => p.symbol.isOneOf(GivenOrImplicit) && p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass))))

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

extension (tree: ImportSelector)
def boundTpe: Type = tree.bound match {
Expand Down
21 changes: 21 additions & 0 deletions tests/pos/i17762.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//> using options -Xfatal-warnings -Wunused:all

class SomeType

def testIt(st1: SomeType, st2: SomeType): Boolean =
given CanEqual[SomeType, SomeType] = CanEqual.derived
st1 == st2

object HasCanEqual:
given f: CanEqual[SomeType, SomeType] = CanEqual.derived

object UsesCanEqual:
import HasCanEqual.given
def testIt(st1: SomeType, st2: SomeType): Boolean =
st1 == st2

object UsesCanEqual2:
import HasCanEqual.f

def testIt(st1: SomeType, st2: SomeType): Boolean =
st1 == st2

0 comments on commit a133b41

Please sign in to comment.