From 0f85c64e64cb74ad7a89c7639923c424fc7d538e Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Wed, 15 Feb 2023 16:44:55 +0100 Subject: [PATCH 01/27] Fix #16822 - Ignore synthetic local private - Update test suit --- .../tools/dotc/transform/CheckUnused.scala | 19 +++++++-------- .../fatal-warnings/i15503i.scala | 23 ++++++++++++++----- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 59878757c39b..6c47c12ac07c 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -81,7 +81,7 @@ class CheckUnused extends MiniPhase: ctx override def prepareForIdent(tree: tpd.Ident)(using Context): Context = - if tree.symbol.exists then + if tree.symbol.exists then _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then _key.unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -103,7 +103,7 @@ class CheckUnused extends MiniPhase: override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = _key.unusedDataApply{ud => // do not register the ValDef generated for `object` - if !tree.symbol.is(Module) then + if !tree.symbol.is(Module) then ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } @@ -335,7 +335,7 @@ object CheckUnused: * The optional name will be used to target the right import * as the same element can be imported with different renaming */ - def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = + def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = if !isConstructorOfSynth(sym) && !doNotRegister(sym) then if sym.isConstructor && sym.exists then registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class @@ -371,7 +371,7 @@ object CheckUnused: implicitParamInScope += memDef else explicitParamInScope += memDef - else if currScopeType.top == ScopeType.Local then + else if currScopeType.top == ScopeType.Local then localDefInScope += memDef else if memDef.shouldReportPrivateDef then privateDefInScope += memDef @@ -578,10 +578,10 @@ object CheckUnused: else false - private def usedDefContains(using Context): Boolean = + private def usedDefContains(using Context): Boolean = sym.everySymbol.exists(usedDef.apply) - private def everySymbol(using Context): List[Symbol] = + private def everySymbol(using Context): List[Symbol] = List(sym, sym.companionClass, sym.companionModule, sym.moduleClass).filter(_.exists) end extension @@ -614,10 +614,11 @@ object CheckUnused: private def isValidParam(using Context): Boolean = val sym = memDef.symbol (sym.is(Param) || sym.isAllOf(PrivateParamAccessor | Local, butNot = CaseAccessor)) && - !isSyntheticMainParam(sym) && - !sym.shouldNotReportParamOwner + !isSyntheticMainParam(sym) && + !sym.shouldNotReportParamOwner && + (!sym.exists || !sym.owner.isAllOf(Synthetic | PrivateLocal)) - private def shouldReportPrivateDef(using Context): Boolean = + private def shouldReportPrivateDef(using Context): Boolean = currScopeType.top == ScopeType.Template && !memDef.symbol.isConstructor && memDef.symbol.is(Private, butNot = SelfName | Synthetic | CaseAccessor) extension (imp: tpd.Import) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 33e04f34daa8..7eae207d952d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -78,13 +78,13 @@ package foo.test.companionprivate: package foo.test.i16678: def foo(func: Int => String, value: Int): String = func(value) // OK - def run = + def run = println(foo(number => number.toString, value = 5)) // OK println(foo(number => "", value = 5)) // error println(foo(func = number => "", value = 5)) // error println(foo(func = number => number.toString, value = 5)) // OK println(foo(func = _.toString, value = 5)) // OK - + package foo.test.possibleclasses: case class AllCaseClass( k: Int, // OK @@ -93,7 +93,7 @@ package foo.test.possibleclasses: s: Int, // error /* But not these */ val t: Int, // OK private val z: Int // error - ) + ) case class AllCaseUsed( k: Int, // OK @@ -113,7 +113,7 @@ package foo.test.possibleclasses: s: Int, // error val t: Int, // OK private val z: Int // error - ) + ) class AllUsed( k: Int, // OK @@ -124,10 +124,21 @@ package foo.test.possibleclasses: private val z: Int // OK ) { def a = k + y + s + t + z - } + } package foo.test.from.i16675: case class PositiveNumber private (i: Int) // OK object PositiveNumber: - def make(i: Int): Option[PositiveNumber] = //OK + def make(i: Int): Option[PositiveNumber] = //OK Option.when(i >= 0)(PositiveNumber(i)) // OK + +package foo.test.i16822: + enum ExampleEnum { + case Build(context: String) // OK + case List // OK + } + + def demo = { + val x = ExampleEnum.List // OK + println(x) // OK + } From 609bc59900169c32708347aee33136bf26f6668f Mon Sep 17 00:00:00 2001 From: Paul Coral Date: Sat, 18 Feb 2023 16:41:59 +0100 Subject: [PATCH 02/27] Traverse annotations instead of just registering - Traverse the tree of annotations - Update test suits --- .../tools/dotc/transform/CheckUnused.scala | 18 +++++++++--------- .../fatal-warnings/i15503i.scala | 9 +++++++++ 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6c47c12ac07c..f66412e16d36 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -28,6 +28,7 @@ import dotty.tools.dotc.core.Types.ConstantType import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Types.TermRef import dotty.tools.dotc.core.Types.NameFilter +import dotty.tools.dotc.core.Symbols.Symbol @@ -103,6 +104,7 @@ class CheckUnused extends MiniPhase: override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = _key.unusedDataApply{ud => // do not register the ValDef generated for `object` + traverseAnnotations(tree.symbol) if !tree.symbol.is(Module) then ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) @@ -112,6 +114,7 @@ class CheckUnused extends MiniPhase: _key.unusedDataApply{ ud => import ud.registerTrivial tree.registerTrivial + traverseAnnotations(tree.symbol) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } @@ -119,11 +122,13 @@ class CheckUnused extends MiniPhase: override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = _key.unusedDataApply{ ud => if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2) + traverseAnnotations(tree.symbol) ud.registerDef(tree) ud.addIgnoredUsage(tree.symbol) } override def prepareForBind(tree: tpd.Bind)(using Context): Context = + traverseAnnotations(tree.symbol) _key.unusedDataApply(_.registerPatVar(tree)) override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context = @@ -232,6 +237,10 @@ class CheckUnused extends MiniPhase: case AnnotatedType(_, annot) => dt(_.registerUsed(annot.symbol, None)) case _ => traverseChildren(tp) + /** This traverse the annotations of the symbol */ + private def traverseAnnotations(sym: Symbol)(using Context): Unit = + sym.denot.annotations.foreach(annot => traverser.traverse(annot.tree)) + /** Do the actual reporting given the result of the anaylsis */ private def reportUnused(res: UnusedData.UnusedResult)(using Context): Unit = import CheckUnused.WarnTypes @@ -274,7 +283,6 @@ object CheckUnused: private class UnusedData: import dotty.tools.dotc.transform.CheckUnused.UnusedData.UnusedResult import collection.mutable.{Set => MutSet, Map => MutMap, Stack => MutStack} - import dotty.tools.dotc.core.Symbols.Symbol import UnusedData.ScopeType /** The current scope during the tree traversal */ @@ -324,11 +332,6 @@ object CheckUnused: execInNewScope popScope() - /** Register all annotations of this symbol's denotation */ - def registerUsedAnnotation(sym: Symbol)(using Context): Unit = - val annotSym = sym.denot.annotations.map(_.symbol) - annotSym.foreach(s => registerUsed(s, None)) - /** * Register a found (used) symbol along with its name * @@ -363,8 +366,6 @@ object CheckUnused: /** Register (or not) some `val` or `def` according to the context, scope and flags */ def registerDef(memDef: tpd.MemberDef)(using Context): Unit = - // register the annotations for usage - registerUsedAnnotation(memDef.symbol) if memDef.isValidMemberDef then if memDef.isValidParam then if memDef.symbol.isOneOf(GivenOrImplicit) then @@ -378,7 +379,6 @@ object CheckUnused: /** Register pattern variable */ def registerPatVar(patvar: tpd.Bind)(using Context): Unit = - registerUsedAnnotation(patvar.symbol) if !patvar.symbol.isUnusedAnnot then patVarsInScope += patvar diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 7eae207d952d..ccf9344319d2 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -142,3 +142,12 @@ package foo.test.i16822: val x = ExampleEnum.List // OK println(x) // OK } + +package foo.test.i16877: + import scala.collection.immutable.HashMap // OK + import scala.annotation.StaticAnnotation // OK + + class ExampleAnnotation(val a: Object) extends StaticAnnotation // OK + + @ExampleAnnotation(new HashMap()) // OK + class Test //OK From 557640e8490f1c5d2736e5267b01b5cbf56641ec Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Feb 2023 16:03:10 +0100 Subject: [PATCH 03/27] WUnused: Fix unused warnining in synthetic symbols --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 ++++++-- tests/neg-custom-args/fatal-warnings/i16925.scala | 8 ++++++++ tests/neg-custom-args/fatal-warnings/i16926.scala | 7 +++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16925.scala create mode 100644 tests/neg-custom-args/fatal-warnings/i16926.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index f66412e16d36..1a39e162d5ed 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -7,13 +7,13 @@ import dotty.tools.dotc.ast.untpd.ImportSelector import dotty.tools.dotc.config.ScalaSettings import dotty.tools.dotc.core.Contexts.* import dotty.tools.dotc.core.Decorators.{em, i} -import dotty.tools.dotc.core.Flags._ +import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo -import dotty.tools.dotc.util.Property +import dotty.tools.dotc.util.{Property, SourcePosition, SrcPos} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Types.TypeTraverser import dotty.tools.dotc.core.Types.Type @@ -297,6 +297,7 @@ object CheckUnused: * See the `isAccessibleAsIdent` extension method below in the file */ private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]()) + private val usedInPosition = MutSet[(SrcPos, Name)]() /* unused import collected during traversal */ private val unusedImport = MutSet[ImportSelector]() @@ -346,6 +347,7 @@ object CheckUnused: usedInScope.top += ((sym, sym.isAccessibleAsIdent, name)) usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name)) usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name)) + name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ def addIgnoredUsage(sym: Symbol)(using Context): Unit = @@ -450,6 +452,7 @@ object CheckUnused: if ctx.settings.WunusedHas.locals then localDefInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.LocalDefs).toList else Nil @@ -478,6 +481,7 @@ object CheckUnused: if ctx.settings.WunusedHas.patvars then patVarsInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.PatVars).toList else Nil diff --git a/tests/neg-custom-args/fatal-warnings/i16925.scala b/tests/neg-custom-args/fatal-warnings/i16925.scala new file mode 100644 index 000000000000..5cc94f53cdd4 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16925.scala @@ -0,0 +1,8 @@ +// scalac: -Wunused:all + +def hello = + for { + i <- 1 to 2 if true + _ = println(i) // OK + } yield () + diff --git a/tests/neg-custom-args/fatal-warnings/i16926.scala b/tests/neg-custom-args/fatal-warnings/i16926.scala new file mode 100644 index 000000000000..23f167f4ce30 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16926.scala @@ -0,0 +1,7 @@ +// scalac: -Wunused:all + +def hello(): Unit = + for { + i <- (0 to 10).toList + (a, b) = "hello" -> "world" // OK + } yield println(s"$a $b") From b009fd500297ac3c1c126e2d777279f5c9e07c74 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Feb 2023 12:55:33 +0100 Subject: [PATCH 04/27] Move tests --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 14 ++++++++++++++ tests/neg-custom-args/fatal-warnings/i16925.scala | 8 -------- tests/neg-custom-args/fatal-warnings/i16926.scala | 7 ------- 3 files changed, 14 insertions(+), 15 deletions(-) delete mode 100644 tests/neg-custom-args/fatal-warnings/i16925.scala delete mode 100644 tests/neg-custom-args/fatal-warnings/i16926.scala diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index ccf9344319d2..e0675a060fdb 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -151,3 +151,17 @@ package foo.test.i16877: @ExampleAnnotation(new HashMap()) // OK class Test //OK + +package foo.test.i16926: + def hello(): Unit = + for { + i <- (0 to 10).toList + (a, b) = "hello" -> "world" // OK + } yield println(s"$a $b") + +package foo.test.i16925: + def hello = + for { + i <- 1 to 2 if true + _ = println(i) // OK + } yield () diff --git a/tests/neg-custom-args/fatal-warnings/i16925.scala b/tests/neg-custom-args/fatal-warnings/i16925.scala deleted file mode 100644 index 5cc94f53cdd4..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i16925.scala +++ /dev/null @@ -1,8 +0,0 @@ -// scalac: -Wunused:all - -def hello = - for { - i <- 1 to 2 if true - _ = println(i) // OK - } yield () - diff --git a/tests/neg-custom-args/fatal-warnings/i16926.scala b/tests/neg-custom-args/fatal-warnings/i16926.scala deleted file mode 100644 index 23f167f4ce30..000000000000 --- a/tests/neg-custom-args/fatal-warnings/i16926.scala +++ /dev/null @@ -1,7 +0,0 @@ -// scalac: -Wunused:all - -def hello(): Unit = - for { - i <- (0 to 10).toList - (a, b) = "hello" -> "world" // OK - } yield println(s"$a $b") From 5abf61f349c7f5dd761553b71b254b9774b86fb2 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Feb 2023 17:54:21 +0100 Subject: [PATCH 05/27] Remove unused import --- 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 1a39e162d5ed..a5c4a7722106 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -13,7 +13,7 @@ import dotty.tools.dotc.core.StdNames import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo -import dotty.tools.dotc.util.{Property, SourcePosition, SrcPos} +import dotty.tools.dotc.util.{Property, SrcPos} import dotty.tools.dotc.core.Mode import dotty.tools.dotc.core.Types.TypeTraverser import dotty.tools.dotc.core.Types.Type From efa502a1c6330205849e653d6adc69ee914f178d Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 7 Mar 2023 15:18:52 +0100 Subject: [PATCH 06/27] WUnused: Fix for symbols with synthetic names and unused transparent inlines --- .../tools/dotc/transform/CheckUnused.scala | 27 +++++++- .../fatal-warnings/i15503i.scala | 65 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a5c4a7722106..00f7363865e5 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Decorators.{em, i} import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.report +import dotty.tools.dotc.{ast, report} import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} @@ -425,6 +425,20 @@ object CheckUnused: else exists } + + // not report unused transparent inline imports + for { + imp <- imports + sel <- imp.selectors + } { + if unusedImport.contains(sel) then + val tpd.Import(qual, _) = imp + val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) + val isTransparentAndInline = importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) + if isTransparentAndInline then + unusedImport -= sel + } + // if there's an outer scope if usedInScope.nonEmpty then // we keep the symbols not referencing an import in this scope @@ -443,6 +457,7 @@ object CheckUnused: */ def getUnused(using Context): UnusedResult = popScope() + val sortedImp = if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then unusedImport.map(d => d.srcPos -> WarnTypes.Imports).toList @@ -453,6 +468,7 @@ object CheckUnused: localDefInScope .filterNot(d => d.symbol.usedDefContains) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.LocalDefs).toList else Nil @@ -460,6 +476,7 @@ object CheckUnused: if ctx.settings.WunusedHas.explicits then explicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ExplicitParams).toList else Nil @@ -467,6 +484,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -474,6 +492,7 @@ object CheckUnused: if ctx.settings.WunusedHas.privates then privateDefInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.PrivateMembers).toList else Nil @@ -481,6 +500,7 @@ object CheckUnused: if ctx.settings.WunusedHas.patvars then patVarsInScope .filterNot(d => d.symbol.usedDefContains) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name}) .map(d => d.namePos -> WarnTypes.PatVars).toList else @@ -493,6 +513,11 @@ object CheckUnused: end getUnused //============================ HELPERS ==================================== + /** + * Heuristic to detect synthetic suffixes in names of symbols + */ + private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = + symbol.name.mangledString.contains("$") /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index e0675a060fdb..fd3b373625a9 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -165,3 +165,68 @@ package foo.test.i16925: i <- 1 to 2 if true _ = println(i) // OK } yield () + +package foo.test.i16863a: + import scala.quoted.* + def fn(using Quotes) = + val x = Expr(1) + '{ $x + 2 } // OK + +package foo.test.i16863b: + import scala.quoted.* + def fn[A](using Quotes, Type[A]) = // OK + val numeric = Expr.summon[Numeric[A]].getOrElse(???) + '{ $numeric.fromInt(3) } // OK + +package foo.test.i16863c: + import scala.quoted.* + def fn[A](expr: Expr[Any])(using Quotes) = + val imp = expr match + case '{ ${ _ }: a } => Expr.summon[Numeric[a]] // OK + println(imp) + +package foo.test.i16863d: + import scala.quoted.* + import scala.compiletime.asMatchable // OK + def fn[A](using Quotes, Type[A]) = + import quotes.reflect.* + val imp = TypeRepr.of[A].widen.asMatchable match + case Refinement(_,_,_) => () + println(imp) + +package foo.test.i16679a: + object myPackage: + trait CaseClassName[A]: + def name: String + object CaseClassName: + trait CaseClassByStringName[A] extends CaseClassName[A] + import scala.deriving.Mirror + object CaseClassByStringName: + inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassByStringName[A] = + new CaseClassByStringName[A]: + def name: String = A.toString + + object secondPackage: + import myPackage.CaseClassName // OK + case class CoolClass(i: Int) derives CaseClassName.CaseClassByStringName + println(summon[CaseClassName[CoolClass]].name) + +package foo.test.i16679b: + object myPackage: + trait CaseClassName[A]: + def name: String + + object CaseClassName: + import scala.deriving.Mirror + inline final def derived[A](using inline A: Mirror.Of[A]): CaseClassName[A] = + new CaseClassName[A]: + def name: String = A.toString + + object Foo: + given x: myPackage.CaseClassName[secondPackage.CoolClass] = null + + object secondPackage: + import myPackage.CaseClassName // OK + import Foo.x + case class CoolClass(i: Int) + println(summon[myPackage.CaseClassName[CoolClass]]) From 3969ba949dce8b097456a50769f427c2642f8564 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 8 Mar 2023 13:24:54 +0100 Subject: [PATCH 07/27] Adjust assertions in test --- .../fatal-warnings/i15503-scala2/scala2-t11681.scala | 4 ++-- tests/neg-custom-args/fatal-warnings/i15503b.scala | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala index f04129a19e48..18aa6879eeba 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala @@ -100,9 +100,9 @@ trait Anonymous { trait Context[A] trait Implicits { def f[A](implicit ctx: Context[A]) = answer // error - def g[A: Context] = answer // error + def g[A: Context] = answer // OK } -class Bound[A: Context] // error +class Bound[A: Context] // OK object Answers { def answer: Int = 42 } diff --git a/tests/neg-custom-args/fatal-warnings/i15503b.scala b/tests/neg-custom-args/fatal-warnings/i15503b.scala index 19bcd01a8dde..8a4a055150f9 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503b.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503b.scala @@ -75,7 +75,7 @@ package foo.scala2.tests: object Types { def l1() = { - object HiObject { def f = this } // error + object HiObject { def f = this } // OK class Hi { // error def f1: Hi = new Hi def f2(x: Hi) = x From 4cdc72fa16577de340b33657409dcfd08d0a51a1 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Fri, 10 Mar 2023 17:36:15 +0100 Subject: [PATCH 08/27] Check if import contains transparent inline in registerImport --- .../tools/dotc/transform/CheckUnused.scala | 29 +++++++++---------- .../fatal-warnings/i15503f.scala | 2 +- .../fatal-warnings/i15503g.scala | 4 +-- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 00f7363865e5..4962d43b4eb1 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Decorators.{em, i} import dotty.tools.dotc.core.Flags.* import dotty.tools.dotc.core.Phases.Phase import dotty.tools.dotc.core.StdNames -import dotty.tools.dotc.{ast, report} +import dotty.tools.dotc.report import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} @@ -360,7 +360,7 @@ object CheckUnused: /** Register an import */ def registerImport(imp: tpd.Import)(using Context): Unit = - if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum then + if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then impInScope.top += imp unusedImport ++= imp.selectors.filter { s => !shouldSelectorBeReported(imp, s) && !isImportExclusion(s) @@ -426,19 +426,6 @@ object CheckUnused: exists } - // not report unused transparent inline imports - for { - imp <- imports - sel <- imp.selectors - } { - if unusedImport.contains(sel) then - val tpd.Import(qual, _) = imp - val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) - val isTransparentAndInline = importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) - if isTransparentAndInline then - unusedImport -= sel - } - // if there's an outer scope if usedInScope.nonEmpty then // we keep the symbols not referencing an import in this scope @@ -513,6 +500,18 @@ object CheckUnused: end getUnused //============================ HELPERS ==================================== + + /** + * Checks if import selects a def that is transparent and inline + */ + private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean = + (for { + sel <- imp.selectors + } yield { + val qual = imp.expr + val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) + importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) + }).exists(identity) /** * Heuristic to detect synthetic suffixes in names of symbols */ diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index db695da3490b..d36cd01be74e 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,7 +5,7 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // error +def f3(a: Int)(using Int) = a // OK def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK diff --git a/tests/neg-custom-args/fatal-warnings/i15503g.scala b/tests/neg-custom-args/fatal-warnings/i15503g.scala index d4daea944184..a0822e7e1611 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503g.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503g.scala @@ -5,8 +5,8 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = default_int // error -def f3(a: Int)(using Int) = a // error -def f4(a: Int)(using Int) = default_int // error // error +def f3(a: Int)(using Int) = a // OK +def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // error def f7(a: Int)(using Int) = summon[Int] + a // OK From 0da834e6f5cd86f693d0123b610a02163f8f05a4 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Mar 2023 17:32:00 +0200 Subject: [PATCH 09/27] Warn for synthetic using/givens with wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503f.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 4962d43b4eb1..3337b4a5a5c6 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -471,7 +471,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol)) + .filterNot(d => containsSyntheticSuffix(d.symbol) && !d.rawMods.is(Given)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index d36cd01be74e..db695da3490b 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,7 +5,7 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // OK +def f3(a: Int)(using Int) = a // error def f4(a: Int)(using Int) = default_int // error def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK From 87e35109ad251a1677962ec06192ee081e7e57f5 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 28 Mar 2023 19:29:09 +0200 Subject: [PATCH 10/27] Wunused: only filter out non-zero span-length givens --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 3337b4a5a5c6..fbf7ea1346c3 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -471,7 +471,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol) && !d.rawMods.is(Given)) + .filterNot(d => containsSyntheticSuffix(d.symbol) && (!d.rawMods.is(Given) || hasZeroLengthSpan(d.symbol))) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -512,11 +512,18 @@ object CheckUnused: val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) }).exists(identity) + /** * Heuristic to detect synthetic suffixes in names of symbols */ private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = symbol.name.mangledString.contains("$") + + /** + * Heuristic to detect generated symbols by checking if symbol has zero length span in source + */ + private def hasZeroLengthSpan(symbol: Symbol)(using Context): Boolean = + symbol.span.end - symbol.span.start == 0 /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package From e8e56561dc8c3388155a44d5e842c74e2580d806 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 14:52:46 +0200 Subject: [PATCH 11/27] Skip all symbols with $ in name in Wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index fbf7ea1346c3..a14c25e664c1 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -471,7 +471,7 @@ object CheckUnused: if ctx.settings.WunusedHas.implicits then implicitParamInScope .filterNot(d => d.symbol.usedDefContains) - .filterNot(d => containsSyntheticSuffix(d.symbol) && (!d.rawMods.is(Given) || hasZeroLengthSpan(d.symbol))) + .filterNot(d => containsSyntheticSuffix(d.symbol)) .map(d => d.namePos -> WarnTypes.ImplicitParams).toList else Nil @@ -519,11 +519,6 @@ object CheckUnused: private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean = symbol.name.mangledString.contains("$") - /** - * Heuristic to detect generated symbols by checking if symbol has zero length span in source - */ - private def hasZeroLengthSpan(symbol: Symbol)(using Context): Boolean = - symbol.span.end - symbol.span.start == 0 /** * Is the the constructor of synthetic package object * Should be ignored as it is always imported/used in package From fd49eb6d9bea2d097ec3fcb29c0e3a80acdcc945 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 15:51:50 +0200 Subject: [PATCH 12/27] Add a failing case with named using to test Wunused:implicits --- tests/neg-custom-args/fatal-warnings/i15503f.scala | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503f.scala b/tests/neg-custom-args/fatal-warnings/i15503f.scala index db695da3490b..67c595d74f40 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503f.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503f.scala @@ -5,8 +5,9 @@ val default_int = 1 def f1(a: Int) = a // OK def f2(a: Int) = 1 // OK -def f3(a: Int)(using Int) = a // error -def f4(a: Int)(using Int) = default_int // error +def f3(a: Int)(using Int) = a // OK +def f4(a: Int)(using Int) = default_int // OK def f6(a: Int)(using Int) = summon[Int] // OK def f7(a: Int)(using Int) = summon[Int] + a // OK +def f8(a: Int)(using foo: Int) = a // error From b3c6071dd4333b4e6eca6fc0f35c814f71153af0 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 16:10:21 +0200 Subject: [PATCH 13/27] Replace for with exists in isTransparentInline in WUNused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index a14c25e664c1..e67e94e8d354 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -505,13 +505,11 @@ object CheckUnused: * Checks if import selects a def that is transparent and inline */ private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean = - (for { - sel <- imp.selectors - } yield { + imp.selectors.exists { sel => val qual = imp.expr val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol) importedMembers.exists(s => s.is(Transparent) && s.is(Inline)) - }).exists(identity) + } /** * Heuristic to detect synthetic suffixes in names of symbols From 631acaa888028a72ed1e07eb7d618833b6e383a5 Mon Sep 17 00:00:00 2001 From: Kacper Korban Date: Thu, 16 Feb 2023 15:53:07 +0100 Subject: [PATCH 14/27] Register usage of symbols in non-inferred type trees in CheckUnused fixes lampepfl#16930 --- .../tools/dotc/transform/CheckUnused.scala | 57 ++++++++++--------- .../fatal-warnings/i16930.scala | 22 +++++++ 2 files changed, 53 insertions(+), 26 deletions(-) create mode 100644 tests/neg-custom-args/fatal-warnings/i16930.scala diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index e67e94e8d354..6350471a3663 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -47,12 +47,11 @@ class CheckUnused extends MiniPhase: */ private val _key = Property.Key[UnusedData] - extension (k: Property.Key[UnusedData]) - private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = - ctx.property(_key).foreach(f) - ctx - private def getUnusedData(using Context): Option[UnusedData] = - ctx.property(_key) + private def unusedDataApply[U](f: UnusedData => U)(using Context): Context = + ctx.property(_key).foreach(f) + ctx + private def getUnusedData(using Context): Option[UnusedData] = + ctx.property(_key) override def phaseName: String = CheckUnused.phaseName @@ -72,7 +71,7 @@ class CheckUnused extends MiniPhase: // ========== END + REPORTING ========== override def transformUnit(tree: tpd.Tree)(using Context): tpd.Tree = - _key.unusedDataApply(ud => reportUnused(ud.getUnused)) + unusedDataApply(ud => reportUnused(ud.getUnused)) tree // ========== MiniPhase Prepare ========== @@ -83,14 +82,14 @@ class CheckUnused extends MiniPhase: override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then - _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) + unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then - _key.unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) + unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) else ctx override def prepareForSelect(tree: tpd.Select)(using Context): Context = - _key.unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) + unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) override def prepareForBlock(tree: tpd.Block)(using Context): Context = pushInBlockTemplatePackageDef(tree) @@ -102,7 +101,7 @@ class CheckUnused extends MiniPhase: pushInBlockTemplatePackageDef(tree) override def prepareForValDef(tree: tpd.ValDef)(using Context): Context = - _key.unusedDataApply{ud => + unusedDataApply{ud => // do not register the ValDef generated for `object` traverseAnnotations(tree.symbol) if !tree.symbol.is(Module) then @@ -111,7 +110,7 @@ class CheckUnused extends MiniPhase: } override def prepareForDefDef(tree: tpd.DefDef)(using Context): Context = - _key.unusedDataApply{ ud => + unusedDataApply{ ud => import ud.registerTrivial tree.registerTrivial traverseAnnotations(tree.symbol) @@ -120,7 +119,7 @@ class CheckUnused extends MiniPhase: } override def prepareForTypeDef(tree: tpd.TypeDef)(using Context): Context = - _key.unusedDataApply{ ud => + unusedDataApply{ ud => if !tree.symbol.is(Param) then // Ignore type parameter (as Scala 2) traverseAnnotations(tree.symbol) ud.registerDef(tree) @@ -129,10 +128,10 @@ class CheckUnused extends MiniPhase: override def prepareForBind(tree: tpd.Bind)(using Context): Context = traverseAnnotations(tree.symbol) - _key.unusedDataApply(_.registerPatVar(tree)) + unusedDataApply(_.registerPatVar(tree)) override def prepareForTypeTree(tree: tpd.TypeTree)(using Context): Context = - typeTraverser(_key.unusedDataApply).traverse(tree.tpe) + if !tree.isInstanceOf[tpd.InferredTypeTree] then typeTraverser(unusedDataApply).traverse(tree.tpe) ctx // ========== MiniPhase Transform ========== @@ -150,27 +149,27 @@ class CheckUnused extends MiniPhase: tree override def transformValDef(tree: tpd.ValDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree override def transformDefDef(tree: tpd.DefDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree override def transformTypeDef(tree: tpd.TypeDef)(using Context): tpd.Tree = - _key.unusedDataApply(_.removeIgnoredUsage(tree.symbol)) + unusedDataApply(_.removeIgnoredUsage(tree.symbol)) tree // ---------- MiniPhase HELPERS ----------- private def pushInBlockTemplatePackageDef(tree: tpd.Block | tpd.Template | tpd.PackageDef)(using Context): Context = - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.pushScope(UnusedData.ScopeType.fromTree(tree)) } ctx private def popOutBlockTemplatePackageDef()(using Context): Context = - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.popScope() } ctx @@ -193,7 +192,7 @@ class CheckUnused extends MiniPhase: val newCtx = if tree.symbol.exists then ctx.withOwner(tree.symbol) else ctx tree match case imp:tpd.Import => - _key.unusedDataApply(_.registerImport(imp)) + unusedDataApply(_.registerImport(imp)) traverseChildren(tree)(using newCtx) case ident: Ident => prepareForIdent(ident) @@ -203,7 +202,7 @@ class CheckUnused extends MiniPhase: traverseChildren(tree)(using newCtx) case _: (tpd.Block | tpd.Template | tpd.PackageDef) => //! DIFFERS FROM MINIPHASE - _key.unusedDataApply { ud => + unusedDataApply { ud => ud.inNewScope(ScopeType.fromTree(tree))(traverseChildren(tree)(using newCtx)) } case t:tpd.ValDef => @@ -221,9 +220,10 @@ class CheckUnused extends MiniPhase: case t: tpd.Bind => prepareForBind(t) traverseChildren(tree)(using newCtx) + case _: tpd.InferredTypeTree => case t@tpd.TypeTree() => //! DIFFERS FROM MINIPHASE - typeTraverser(_key.unusedDataApply).traverse(t.tpe) + typeTraverser(unusedDataApply).traverse(t.tpe) traverseChildren(tree)(using newCtx) case _ => //! DIFFERS FROM MINIPHASE @@ -233,9 +233,14 @@ class CheckUnused extends MiniPhase: /** This is a type traverser which catch some special Types not traversed by the term traverser above */ private def typeTraverser(dt: (UnusedData => Any) => Unit)(using Context) = new TypeTraverser: - override def traverse(tp: Type): Unit = tp match - case AnnotatedType(_, annot) => dt(_.registerUsed(annot.symbol, None)) - case _ => traverseChildren(tp) + override def traverse(tp: Type): Unit = + if tp.typeSymbol.exists then dt(_.registerUsed(tp.typeSymbol, Some(tp.typeSymbol.name))) + tp match + case AnnotatedType(_, annot) => + dt(_.registerUsed(annot.symbol, None)) + traverseChildren(tp) + case _ => + traverseChildren(tp) /** This traverse the annotations of the symbol */ private def traverseAnnotations(sym: Symbol)(using Context): Unit = diff --git a/tests/neg-custom-args/fatal-warnings/i16930.scala b/tests/neg-custom-args/fatal-warnings/i16930.scala new file mode 100644 index 000000000000..1f6c5bf1a09f --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i16930.scala @@ -0,0 +1,22 @@ +// scalac: -Wunused:imports + +trait Outer: + trait Used + trait Unused + +object Test { + val outer: Outer = ??? + import outer.{Used, Unused} // error + def foo(x: Any): Used = x.asInstanceOf[Used] +} + +trait Outer1: + trait UnusedToo1 + trait Unused1 + def unusedToo1: UnusedToo1 + +object Test1 { + val outer1: Outer1 = ??? + import outer1.{Unused1, UnusedToo1} // error // error + def foo() = outer1.unusedToo1 // in this case UnusedToo1 is not used explicitly, only inferred +} From a2476795e3bd5bf828c287c065ffeb6c767df225 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 13 Mar 2023 15:11:53 +0100 Subject: [PATCH 15/27] Fix WUnused with indents in derived code --- .../dotty/tools/dotc/transform/CheckUnused.scala | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6350471a3663..36069c314fcf 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -15,19 +15,14 @@ import dotty.tools.dotc.reporting.Message import dotty.tools.dotc.typer.ImportInfo import dotty.tools.dotc.util.{Property, SrcPos} import dotty.tools.dotc.core.Mode -import dotty.tools.dotc.core.Types.TypeTraverser -import dotty.tools.dotc.core.Types.Type -import dotty.tools.dotc.core.Types.AnnotatedType +import dotty.tools.dotc.core.Types.{AnnotatedType, ConstantType, NameFilter, NoType, TermRef, Type, TypeTraverser} import dotty.tools.dotc.core.Flags.flagsString import dotty.tools.dotc.core.Flags import dotty.tools.dotc.core.Names.Name import dotty.tools.dotc.transform.MegaPhase.MiniPhase import dotty.tools.dotc.core.Annotations import dotty.tools.dotc.core.Definitions -import dotty.tools.dotc.core.Types.ConstantType import dotty.tools.dotc.core.NameKinds.WildcardParamName -import dotty.tools.dotc.core.Types.TermRef -import dotty.tools.dotc.core.Types.NameFilter import dotty.tools.dotc.core.Symbols.Symbol @@ -82,6 +77,12 @@ class CheckUnused extends MiniPhase: override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then + val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) + for { + prefix <- prefixes + } { + unusedDataApply(_.registerUsed(prefix.classSymbol, None)) + } unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) @@ -409,7 +410,6 @@ object CheckUnused: val kept = used.filterNot { t => val (sym, isAccessible, optName) = t // keep the symbol for outer scope, if it matches **no** import - // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None From 27325a05cd8ccb913b1c0dfbd1f8eca35ff9fa9c Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 13 Mar 2023 15:38:12 +0100 Subject: [PATCH 16/27] Add failsafe for a case where prefixes in CheckUnused/prepareIndent formed an infinite cycle --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 36069c314fcf..7c2cebfdc18e 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -78,6 +78,7 @@ class CheckUnused extends MiniPhase: override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) + .take(10) // Failsafe for the odd case if there was an infinite cycle for { prefix <- prefixes } { From f39ff925a46bd3966914b44367f82e9237364111 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 14 Mar 2023 15:53:10 +0100 Subject: [PATCH 17/27] Fix for formatting and traverse call of inlined tree in wunused --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 7c2cebfdc18e..856272e9ba53 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -1,7 +1,7 @@ package dotty.tools.dotc.transform import dotty.tools.dotc.ast.tpd -import dotty.tools.dotc.ast.tpd.TreeTraverser +import dotty.tools.dotc.ast.tpd.{Inlined, TreeTraverser} import dotty.tools.dotc.ast.untpd import dotty.tools.dotc.ast.untpd.ImportSelector import dotty.tools.dotc.config.ScalaSettings @@ -59,6 +59,7 @@ class CheckUnused extends MiniPhase: // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = + println(tree) val data = UnusedData() val fresh = ctx.fresh.setProperty(_key, data) fresh @@ -75,15 +76,16 @@ class CheckUnused extends MiniPhase: traverser.traverse(tree) ctx + def prepareForInlined(tree: Inlined)(using Context): Context = + traverser.traverse(tree.call) + ctx + override def prepareForIdent(tree: tpd.Ident)(using Context): Context = if tree.symbol.exists then val prefixes = LazyList.iterate(tree.typeOpt.normalizedPrefix)(_.normalizedPrefix).takeWhile(_ != NoType) .take(10) // Failsafe for the odd case if there was an infinite cycle - for { - prefix <- prefixes - } { + for prefix <- prefixes do unusedDataApply(_.registerUsed(prefix.classSymbol, None)) - } unusedDataApply(_.registerUsed(tree.symbol, Some(tree.name))) else if tree.hasType then unusedDataApply(_.registerUsed(tree.tpe.classSymbol, Some(tree.name))) From b5e69a1e3c2b949c2d2c9977a3ccd71724d2ad19 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 14 Mar 2023 16:11:07 +0100 Subject: [PATCH 18/27] Add test for wunused Inlined call --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 856272e9ba53..fd1aaba1a881 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -59,7 +59,6 @@ class CheckUnused extends MiniPhase: // ========== SETUP ============ override def prepareForUnit(tree: tpd.Tree)(using Context): Context = - println(tree) val data = UnusedData() val fresh = ctx.fresh.setProperty(_key, data) fresh @@ -76,7 +75,7 @@ class CheckUnused extends MiniPhase: traverser.traverse(tree) ctx - def prepareForInlined(tree: Inlined)(using Context): Context = + override def prepareForInlined(tree: tpd.Inlined)(using Context): Context = traverser.traverse(tree.call) ctx From 36de6da76d920fdb307596830a57ec79b866f4aa Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 27 Mar 2023 16:49:09 +0200 Subject: [PATCH 19/27] Fix wunused false positive when deriving alias type --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 12 ++++++++++-- libste | 0 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 libste diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index fd1aaba1a881..7667f262b167 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -589,14 +589,22 @@ object CheckUnused: /** Given an import and accessibility, return an option of selector that match import<->symbol */ private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp - val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).contains(sym) + val dealiasedSym = dealias(sym) + val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) + def dealiasedSelector = sels.flatMap(sel => qual.tpe.member(sym.name).alternatives.map(m => (sel, m.symbol))).collect { + case (sel, sym) if dealias(sym) == dealiasedSym => sel + }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) if qualHasSymbol && !isAccessible && sym.exists then - selector.orElse(wildcard) // selector with name or wildcard (or given) + selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None + private def dealias(symbol: Symbol)(using Context): Symbol = + if(symbol.isType && symbol.asType.denot.isAliasType) then + symbol.asType.typeRef.dealias.typeSymbol + else symbol /** Annotated with @unused */ private def isUnusedAnnot(using Context): Boolean = sym.annotations.exists(a => a.symbol == ctx.definitions.UnusedAnnot) diff --git a/libste b/libste new file mode 100644 index 000000000000..e69de29bb2d1 From adede7326a1237aec37d67391d5c5cd37af94f55 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 3 Apr 2023 15:53:05 +0200 Subject: [PATCH 20/27] Fix wunused for deriving alias type that has a different name --- .../src/dotty/tools/dotc/transform/CheckUnused.scala | 7 +++++-- libste | 0 tests/neg-custom-args/fatal-warnings/i15503i.scala | 11 +++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) delete mode 100644 libste diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 7667f262b167..6f25ee137d07 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -590,9 +590,12 @@ object CheckUnused: private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) - val qualHasSymbol = qual.tpe.member(sym.name).alternatives.map(_.symbol).map(dealias).contains(dealiasedSym) + val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) + val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) + val allSelections = typeSelections ::: termSelections :::qual.tpe.member(sym.name).alternatives + val qualHasSymbol = allSelections.map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = sels.flatMap(sel => qual.tpe.member(sym.name).alternatives.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = sels.flatMap(sel => allSelections.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) diff --git a/libste b/libste deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index fd3b373625a9..74dee1ce732d 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -230,3 +230,14 @@ package foo.test.i16679b: import Foo.x case class CoolClass(i: Int) println(summon[myPackage.CaseClassName[CoolClass]]) + +package foo.test.i17156: + package a: + trait Foo[A] + + package b: + type Xd = Foo + + package c: + import b.Xd + trait Z derives Xd From 11c7a8c2b50c8d7f00d0a6bfa84fc97908d02425 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 3 Apr 2023 19:45:58 +0200 Subject: [PATCH 21/27] Fix test for wunused alias deriving --- tests/neg-custom-args/fatal-warnings/i15503i.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index 74dee1ce732d..c4291801c1c1 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -234,9 +234,12 @@ package foo.test.i16679b: package foo.test.i17156: package a: trait Foo[A] + object Foo: + inline def derived[T]: Foo[T] = new Foo{} package b: - type Xd = Foo + import a.Foo + type Xd[A] = Foo[A] package c: import b.Xd From 4acb776e533ddbbe08eae653b0e5a5bf6a25fbc0 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Tue, 4 Apr 2023 14:43:12 +0200 Subject: [PATCH 22/27] Fix selecting unaliased selector in wunused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 6f25ee137d07..948a837a7034 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -590,12 +590,13 @@ object CheckUnused: private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) + val simpleSelections = qual.tpe.member(sym.name).alternatives val typeSelections = sels.flatMap(n => qual.tpe.member(n.name.toTypeName).alternatives) val termSelections = sels.flatMap(n => qual.tpe.member(n.name.toTermName).alternatives) - val allSelections = typeSelections ::: termSelections :::qual.tpe.member(sym.name).alternatives - val qualHasSymbol = allSelections.map(_.symbol).map(dealias).contains(dealiasedSym) + val selectionsToDealias = typeSelections ::: termSelections + val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = sels.flatMap(sel => allSelections.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) @@ -604,6 +605,7 @@ object CheckUnused: else None + private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then symbol.asType.typeRef.dealias.typeSymbol From 95d2fa1024f91492d5208ac1b3c16b285c4e8fbb Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Mon, 10 Apr 2023 19:41:15 +0200 Subject: [PATCH 23/27] Dealias only conditionally when symbol is derived val type in wunused --- .../tools/dotc/transform/CheckUnused.scala | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 948a837a7034..b6f2afcfcc24 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -24,7 +24,7 @@ import dotty.tools.dotc.core.Annotations import dotty.tools.dotc.core.Definitions import dotty.tools.dotc.core.NameKinds.WildcardParamName import dotty.tools.dotc.core.Symbols.Symbol - +import dotty.tools.dotc.core.StdNames.nme /** @@ -109,6 +109,9 @@ class CheckUnused extends MiniPhase: traverseAnnotations(tree.symbol) if !tree.symbol.is(Module) then ud.registerDef(tree) + if tree.name.mangledString.startsWith(nme.derived.mangledString + "$") + && tree.typeOpt != NoType then + ud.registerUsed(tree.typeOpt.typeSymbol, None, true) ud.addIgnoredUsage(tree.symbol) } @@ -304,7 +307,7 @@ object CheckUnused: * * See the `isAccessibleAsIdent` extension method below in the file */ - private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name])]()) + private val usedInScope = MutStack(MutSet[(Symbol,Boolean, Option[Name], Boolean)]()) private val usedInPosition = MutSet[(SrcPos, Name)]() /* unused import collected during traversal */ private val unusedImport = MutSet[ImportSelector]() @@ -347,14 +350,14 @@ object CheckUnused: * The optional name will be used to target the right import * as the same element can be imported with different renaming */ - def registerUsed(sym: Symbol, name: Option[Name])(using Context): Unit = + def registerUsed(sym: Symbol, name: Option[Name], isDerived: Boolean = false)(using Context): Unit = if !isConstructorOfSynth(sym) && !doNotRegister(sym) then if sym.isConstructor && sym.exists then registerUsed(sym.owner, None) // constructor are "implicitly" imported with the class else - usedInScope.top += ((sym, sym.isAccessibleAsIdent, name)) - usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name)) - usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name)) + usedInScope.top += ((sym, sym.isAccessibleAsIdent, name, isDerived)) + usedInScope.top += ((sym.companionModule, sym.isAccessibleAsIdent, name, isDerived)) + usedInScope.top += ((sym.companionClass, sym.isAccessibleAsIdent, name, isDerived)) name.map(n => usedInPosition += ((sym.sourcePos, n))) /** Register a symbol that should be ignored */ @@ -408,15 +411,15 @@ object CheckUnused: // used symbol in this scope val used = usedInScope.pop().toSet // used imports in this scope - val imports = impInScope.pop().toSet + val imports = impInScope.pop() val kept = used.filterNot { t => - val (sym, isAccessible, optName) = t + val (sym, isAccessible, optName, isDerived) = t // keep the symbol for outer scope, if it matches **no** import // This is the first matching wildcard selector var selWildCard: Option[ImportSelector] = None val exists = imports.exists { imp => - sym.isInImport(imp, isAccessible, optName) match + sym.isInImport(imp, isAccessible, optName, isDerived) match case None => false case optSel@Some(sel) if sel.isWildcard => if selWildCard.isEmpty then selWildCard = optSel @@ -587,7 +590,7 @@ object CheckUnused: } /** Given an import and accessibility, return an option of selector that match import<->symbol */ - private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name])(using Context): Option[ImportSelector] = + private def isInImport(imp: tpd.Import, isAccessible: Boolean, symName: Option[Name], isDerived: Boolean)(using Context): Option[ImportSelector] = val tpd.Import(qual, sels) = imp val dealiasedSym = dealias(sym) val simpleSelections = qual.tpe.member(sym.name).alternatives @@ -596,9 +599,9 @@ object CheckUnused: val selectionsToDealias = typeSelections ::: termSelections val qualHasSymbol = simpleSelections.map(_.symbol).contains(sym) || (simpleSelections ::: selectionsToDealias).map(_.symbol).map(dealias).contains(dealiasedSym) def selector = sels.find(sel => (sel.name.toTermName == sym.name || sel.name.toTypeName == sym.name) && symName.map(n => n.toTermName == sel.rename).getOrElse(true)) - def dealiasedSelector = sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { + def dealiasedSelector = if(isDerived) sels.flatMap(sel => selectionsToDealias.map(m => (sel, m.symbol))).collect { case (sel, sym) if dealias(sym) == dealiasedSym => sel - }.headOption + }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) if qualHasSymbol && !isAccessible && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) From 44bfddb0c803c41ff0c1c10c4de045d0197fed18 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 29 Mar 2023 19:09:47 +0200 Subject: [PATCH 24/27] Fix WUnused for accessible symbols that are renamed --- .../dotty/tools/dotc/transform/CheckUnused.scala | 2 +- tests/neg-custom-args/fatal-warnings/i15503i.scala | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index b6f2afcfcc24..daf82d57cf1b 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,7 +603,7 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && !isAccessible && sym.exists then + if qualHasSymbol && (!isAccessible || symName.exists(_ != sym.name)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None diff --git a/tests/neg-custom-args/fatal-warnings/i15503i.scala b/tests/neg-custom-args/fatal-warnings/i15503i.scala index c4291801c1c1..9d752e44cf92 100644 --- a/tests/neg-custom-args/fatal-warnings/i15503i.scala +++ b/tests/neg-custom-args/fatal-warnings/i15503i.scala @@ -244,3 +244,17 @@ package foo.test.i17156: package c: import b.Xd trait Z derives Xd + +package foo.test.i17117: + package example { + object test1 { + val test = "test" + } + + object test2 { + + import example.test1 as t1 + + val test = t1.test + } + } \ No newline at end of file From 5fc9e97d1e464a595cab1f6b81c71fcf0b933f11 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 14:35:53 +0200 Subject: [PATCH 25/27] Compare simple name and handle NO_NAME case in WUnused --- 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 daf82d57cf1b..5cd5ebf447ba 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,7 +603,7 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || symName.exists(_ != sym.name)) && sym.exists then + if qualHasSymbol && (!isAccessible || (sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName))) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None From 42b6e97ce744986768c5057807b364b51f6d4f55 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 17:00:18 +0200 Subject: [PATCH 26/27] Extracted isRenamedSymbol def --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 5cd5ebf447ba..245b2cd18ce4 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,11 +603,13 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || (sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName))) && sym.exists then + if qualHasSymbol && (!isAccessible || isRenamedSymbol(sym, symName)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None + private def isRenamedSymbol(sym: Symbol, symNameInScope: Option[Name]) = + sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName) private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then From 6bd53a8d57ce3114873b03f8586b6db44b86f9f4 Mon Sep 17 00:00:00 2001 From: Szymon Rodziewicz Date: Wed, 12 Apr 2023 17:16:39 +0200 Subject: [PATCH 27/27] Fix isRenamedSymbol method in WUnused --- compiler/src/dotty/tools/dotc/transform/CheckUnused.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala index 245b2cd18ce4..7ccf1a62bc50 100644 --- a/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala +++ b/compiler/src/dotty/tools/dotc/transform/CheckUnused.scala @@ -603,13 +603,13 @@ object CheckUnused: case (sel, sym) if dealias(sym) == dealiasedSym => sel }.headOption else None def wildcard = sels.find(sel => sel.isWildcard && ((sym.is(Given) == sel.isGiven) || sym.is(Implicit))) - if qualHasSymbol && (!isAccessible || isRenamedSymbol(sym, symName)) && sym.exists then + if qualHasSymbol && (!isAccessible || sym.isRenamedSymbol(symName)) && sym.exists then selector.orElse(dealiasedSelector).orElse(wildcard) // selector with name or wildcard (or given) else None - private def isRenamedSymbol(sym: Symbol, symNameInScope: Option[Name]) = - sym.name != nme.NO_NAME && symName.exists(_.toSimpleName != sym.name.toSimpleName) + private def isRenamedSymbol(symNameInScope: Option[Name])(using Context) = + sym.name != nme.NO_NAME && symNameInScope.exists(_.toSimpleName != sym.name.toSimpleName) private def dealias(symbol: Symbol)(using Context): Symbol = if(symbol.isType && symbol.asType.denot.isAliasType) then