Skip to content

Commit

Permalink
WUnused: Fix for symbols with synthetic names and unused transparent …
Browse files Browse the repository at this point in the history
…inlines (scala#17061)

Resolves scala#16863
  • Loading branch information
szymon-rd authored Mar 30, 2023
2 parents 5c2efc5 + 1f9e869 commit 00bb3fe
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 8 deletions.
26 changes: 25 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/CheckUnused.scala
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,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)
Expand Down Expand Up @@ -432,6 +432,7 @@ object CheckUnused:
else
exists
}

// if there's an outer scope
if usedInScope.nonEmpty then
// we keep the symbols not referencing an import in this scope
Expand All @@ -450,6 +451,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
Expand All @@ -460,34 +462,39 @@ 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
val sortedExplicitParams =
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
val sortedImplicitParams =
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
val sortedPrivateDefs =
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
val sortedPatVars =
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
Expand All @@ -500,6 +507,23 @@ 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 =
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))
}

/**
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-custom-args/fatal-warnings/i15503b.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions tests/neg-custom-args/fatal-warnings/i15503f.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

4 changes: 2 additions & 2 deletions tests/neg-custom-args/fatal-warnings/i15503g.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
28 changes: 28 additions & 0 deletions tests/neg-custom-args/fatal-warnings/i15503i.scala
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,34 @@ package foo.test.i16925:
_ = 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]:
Expand Down

0 comments on commit 00bb3fe

Please sign in to comment.