Skip to content

Commit 00bb3fe

Browse files
authored
WUnused: Fix for symbols with synthetic names and unused transparent inlines (#17061)
Resolves #16863
2 parents 5c2efc5 + 1f9e869 commit 00bb3fe

File tree

6 files changed

+61
-8
lines changed

6 files changed

+61
-8
lines changed

compiler/src/dotty/tools/dotc/transform/CheckUnused.scala

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ object CheckUnused:
368368

369369
/** Register an import */
370370
def registerImport(imp: tpd.Import)(using Context): Unit =
371-
if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum then
371+
if !tpd.languageImport(imp.expr).nonEmpty && !imp.isGeneratedByEnum && !isTransparentAndInline(imp) then
372372
impInScope.top += imp
373373
unusedImport ++= imp.selectors.filter { s =>
374374
!shouldSelectorBeReported(imp, s) && !isImportExclusion(s)
@@ -432,6 +432,7 @@ object CheckUnused:
432432
else
433433
exists
434434
}
435+
435436
// if there's an outer scope
436437
if usedInScope.nonEmpty then
437438
// we keep the symbols not referencing an import in this scope
@@ -450,6 +451,7 @@ object CheckUnused:
450451
*/
451452
def getUnused(using Context): UnusedResult =
452453
popScope()
454+
453455
val sortedImp =
454456
if ctx.settings.WunusedHas.imports || ctx.settings.WunusedHas.strictNoImplicitWarn then
455457
unusedImport.map(d => d.srcPos -> WarnTypes.Imports).toList
@@ -460,34 +462,39 @@ object CheckUnused:
460462
localDefInScope
461463
.filterNot(d => d.symbol.usedDefContains)
462464
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
465+
.filterNot(d => containsSyntheticSuffix(d.symbol))
463466
.map(d => d.namePos -> WarnTypes.LocalDefs).toList
464467
else
465468
Nil
466469
val sortedExplicitParams =
467470
if ctx.settings.WunusedHas.explicits then
468471
explicitParamInScope
469472
.filterNot(d => d.symbol.usedDefContains)
473+
.filterNot(d => containsSyntheticSuffix(d.symbol))
470474
.map(d => d.namePos -> WarnTypes.ExplicitParams).toList
471475
else
472476
Nil
473477
val sortedImplicitParams =
474478
if ctx.settings.WunusedHas.implicits then
475479
implicitParamInScope
476480
.filterNot(d => d.symbol.usedDefContains)
481+
.filterNot(d => containsSyntheticSuffix(d.symbol))
477482
.map(d => d.namePos -> WarnTypes.ImplicitParams).toList
478483
else
479484
Nil
480485
val sortedPrivateDefs =
481486
if ctx.settings.WunusedHas.privates then
482487
privateDefInScope
483488
.filterNot(d => d.symbol.usedDefContains)
489+
.filterNot(d => containsSyntheticSuffix(d.symbol))
484490
.map(d => d.namePos -> WarnTypes.PrivateMembers).toList
485491
else
486492
Nil
487493
val sortedPatVars =
488494
if ctx.settings.WunusedHas.patvars then
489495
patVarsInScope
490496
.filterNot(d => d.symbol.usedDefContains)
497+
.filterNot(d => containsSyntheticSuffix(d.symbol))
491498
.filterNot(d => usedInPosition.exists { case (pos, name) => d.span.contains(pos.span) && name == d.symbol.name})
492499
.map(d => d.namePos -> WarnTypes.PatVars).toList
493500
else
@@ -500,6 +507,23 @@ object CheckUnused:
500507
end getUnused
501508
//============================ HELPERS ====================================
502509

510+
511+
/**
512+
* Checks if import selects a def that is transparent and inline
513+
*/
514+
private def isTransparentAndInline(imp: tpd.Import)(using Context): Boolean =
515+
imp.selectors.exists { sel =>
516+
val qual = imp.expr
517+
val importedMembers = qual.tpe.member(sel.name).alternatives.map(_.symbol)
518+
importedMembers.exists(s => s.is(Transparent) && s.is(Inline))
519+
}
520+
521+
/**
522+
* Heuristic to detect synthetic suffixes in names of symbols
523+
*/
524+
private def containsSyntheticSuffix(symbol: Symbol)(using Context): Boolean =
525+
symbol.name.mangledString.contains("$")
526+
503527
/**
504528
* Is the the constructor of synthetic package object
505529
* Should be ignored as it is always imported/used in package

tests/neg-custom-args/fatal-warnings/i15503-scala2/scala2-t11681.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,9 @@ trait Anonymous {
100100
trait Context[A]
101101
trait Implicits {
102102
def f[A](implicit ctx: Context[A]) = answer // error
103-
def g[A: Context] = answer // error
103+
def g[A: Context] = answer // OK
104104
}
105-
class Bound[A: Context] // error
105+
class Bound[A: Context] // OK
106106
object Answers {
107107
def answer: Int = 42
108108
}

tests/neg-custom-args/fatal-warnings/i15503b.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ package foo.scala2.tests:
7575

7676
object Types {
7777
def l1() = {
78-
object HiObject { def f = this } // error
78+
object HiObject { def f = this } // OK
7979
class Hi { // error
8080
def f1: Hi = new Hi
8181
def f2(x: Hi) = x

tests/neg-custom-args/fatal-warnings/i15503f.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ val default_int = 1
55

66
def f1(a: Int) = a // OK
77
def f2(a: Int) = 1 // OK
8-
def f3(a: Int)(using Int) = a // error
9-
def f4(a: Int)(using Int) = default_int // error
8+
def f3(a: Int)(using Int) = a // OK
9+
def f4(a: Int)(using Int) = default_int // OK
1010
def f6(a: Int)(using Int) = summon[Int] // OK
1111
def f7(a: Int)(using Int) = summon[Int] + a // OK
12+
def f8(a: Int)(using foo: Int) = a // error
1213

tests/neg-custom-args/fatal-warnings/i15503g.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ val default_int = 1
55

66
def f1(a: Int) = a // OK
77
def f2(a: Int) = default_int // error
8-
def f3(a: Int)(using Int) = a // error
9-
def f4(a: Int)(using Int) = default_int // error // error
8+
def f3(a: Int)(using Int) = a // OK
9+
def f4(a: Int)(using Int) = default_int // error
1010
def f6(a: Int)(using Int) = summon[Int] // error
1111
def f7(a: Int)(using Int) = summon[Int] + a // OK
1212

tests/neg-custom-args/fatal-warnings/i15503i.scala

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,34 @@ package foo.test.i16925:
209209
_ = println(i) // OK
210210
} yield ()
211211

212+
package foo.test.i16863a:
213+
import scala.quoted.*
214+
def fn(using Quotes) =
215+
val x = Expr(1)
216+
'{ $x + 2 } // OK
217+
218+
package foo.test.i16863b:
219+
import scala.quoted.*
220+
def fn[A](using Quotes, Type[A]) = // OK
221+
val numeric = Expr.summon[Numeric[A]].getOrElse(???)
222+
'{ $numeric.fromInt(3) } // OK
223+
224+
package foo.test.i16863c:
225+
import scala.quoted.*
226+
def fn[A](expr: Expr[Any])(using Quotes) =
227+
val imp = expr match
228+
case '{ ${ _ }: a } => Expr.summon[Numeric[a]] // OK
229+
println(imp)
230+
231+
package foo.test.i16863d:
232+
import scala.quoted.*
233+
import scala.compiletime.asMatchable // OK
234+
def fn[A](using Quotes, Type[A]) =
235+
import quotes.reflect.*
236+
val imp = TypeRepr.of[A].widen.asMatchable match
237+
case Refinement(_,_,_) => ()
238+
println(imp)
239+
212240
package foo.test.i16679a:
213241
object myPackage:
214242
trait CaseClassName[A]:

0 commit comments

Comments
 (0)