From fa72e70cba3763119af151dc7a565b5606268689 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 3 Oct 2023 17:53:49 +0200 Subject: [PATCH 1/2] Fix wunused false positive on CanEqual --- .../tools/dotc/transform/CheckUnused.scala | 22 ++++++++++++++----- tests/pos/i17762.scala | 21 ++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i17762.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 3b397d8be746..736b9fbc43bb 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -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 @@ -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 @@ -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 @@ -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 && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) || + (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 { diff --git a/tests/pos/i17762.scala b/tests/pos/i17762.scala new file mode 100644 index 000000000000..65275c4619db --- /dev/null +++ b/tests/pos/i17762.scala @@ -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 \ No newline at end of file From 88eed71b4d3fd70e4bbbf7b8f4234e59069d30e0 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 9 Oct 2023 14:25:22 +0200 Subject: [PATCH 2/2] Apply Jamie's suggestion --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 736b9fbc43bb..073626b4b5c6 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -649,7 +649,7 @@ object CheckUnused: * Ignore CanEqual imports */ private def isImportIgnored(imp: tpd.Import, sel: ImportSelector)(using Context): Boolean = - (sel.isWildcard && imp.expr.tpe.allMembers.exists(p => p.symbol.typeRef.baseClasses.exists(_.derivesFrom(defn.CanEqualClass)))) || + (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))))