Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport "WUnused: Fix for symbols with synthetic names and unused transparent inlines" #17274

Merged
merged 8 commits into from
Apr 17, 2023
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