From 9ed667283e0bbe2791b44e62f050262c15040cca Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Mon, 20 Jun 2022 04:35:21 -0700 Subject: [PATCH] When reporting unused import, check deprecations --- .../tools/nsc/typechecker/Contexts.scala | 28 +++++++++++++-- test/files/neg/t11758.check | 24 +++++++++++++ test/files/neg/t11758.scala | 35 +++++++++++++++++++ 3 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 test/files/neg/t11758.check create mode 100644 test/files/neg/t11758.scala diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index b1b4beec2abd..703824097d96 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -70,8 +70,24 @@ trait Contexts { self: Analyzer => mutable.Map.empty[CompilationUnit, List[(ImportInfo, Symbol)]].withDefaultValue(Nil) def warnUnusedImports(unit: CompilationUnit) = if (!unit.isJava) { + def msg(sym: Symbol) = sym.deprecationMessage.map(": " + _).getOrElse("") + def checkDeprecatedElementInPath(selector: ImportSelector, info: ImportInfo): String = { + def badName(name: Name) = + info.qual.tpe.member(name) match { + case m if m.isDeprecated => Some(s" of deprecated $m${msg(m)}") + case _ => None + } + val badSelected = + if (!selector.isMask && selector.isSpecific) badName(selector.name).orElse(badName(selector.name.toTypeName)) + else None + def badFrom = { + val sym = info.qual.symbol + if (sym.isDeprecated) Some(s" from deprecated $sym${msg(sym)}") else None + } + badSelected.orElse(badFrom).getOrElse("") + } def warnUnusedSelections(infos0: List[(ImportInfo, Symbol)]): Unit = { - type Culled = (Position, Symbol, String) + type Culled = (ImportSelector, ImportInfo, Symbol) var unused = List.empty[Culled] @tailrec def loop(infos: List[(ImportInfo, Symbol)]): Unit = infos match { @@ -82,7 +98,7 @@ trait Contexts { self: Analyzer => case selector :: rest => checkSelectors(rest) if (!selector.isMask && !used(selector)) - unused ::= ((info.posOf(selector), owner, info.fullSelectorString(selector))) + unused ::= ((selector, info, owner)) case _ => } checkSelectors(info.tree.selectors) @@ -90,7 +106,13 @@ trait Contexts { self: Analyzer => case _ => } loop(infos0) - unused.foreach { case (pos, owner, origin) => runReporting.warning(pos, "Unused import", WarningCategory.UnusedImports, owner, origin) } + unused.foreach { + case (selector, info, owner) => + val pos = info.posOf(selector) + val origin = info.fullSelectorString(selector) + val addendum = checkDeprecatedElementInPath(selector, info) + runReporting.warning(pos, s"Unused import$addendum", WarningCategory.UnusedImports, owner, origin) + } } allImportInfos.remove(unit).foreach(warnUnusedSelections) } diff --git a/test/files/neg/t11758.check b/test/files/neg/t11758.check new file mode 100644 index 000000000000..8ddaee35d53a --- /dev/null +++ b/test/files/neg/t11758.check @@ -0,0 +1,24 @@ +t11758.scala:3: warning: Unused import of deprecated lazy value higherKinds: higherKinds no longer needs to be imported explicitly +import language.higherKinds + ^ +t11758.scala:19: warning: Unused import from deprecated object outer: no outer + import outer.other + ^ +t11758.scala:21: warning: Unused import of deprecated object inner: no inner + import outer.{inner => odder} + ^ +t11758.scala:25: warning: Unused import of deprecated class C: no see + import nest.C + ^ +t11758.scala:23: warning: object inner in object outer is deprecated (since 2.0): no inner + def f = inner + ^ +t11758.scala:23: warning: object outer is deprecated (since 1.0): no outer + def f = inner + ^ +t11758.scala:30: warning: class C in object nest is deprecated (since 3.0): no see + def g = new C + ^ +error: No warnings can be incurred under -Werror. +7 warnings +1 error diff --git a/test/files/neg/t11758.scala b/test/files/neg/t11758.scala new file mode 100644 index 000000000000..c6109ff684fc --- /dev/null +++ b/test/files/neg/t11758.scala @@ -0,0 +1,35 @@ +// scalac: -Xlint:deprecation -Wunused:imports -Werror + +import language.higherKinds + +@deprecated("no outer", "1.0") +object outer { + @deprecated("no inner", "2.0") + object inner + object other +} +object nest { + @deprecated("no see", "3.0") + class C + + val status = true +} + +trait T { + import outer.other + import outer.inner + import outer.{inner => odder} + + def f = inner + + import nest.C + def g = () +} +trait U { + import nest.C + def g = new C +} +trait OK { + import nest.{C => _, _} + def ok = status +}