Skip to content

Commit 8f20db0

Browse files
authored
Merge pull request #466 from scala/backport-lts-3.3-23351
Backport "Nowarn receiver of extension taking params" to 3.3 LTS
2 parents e28c0bf + 52ee0c3 commit 8f20db0

File tree

3 files changed

+56
-9
lines changed

3 files changed

+56
-9
lines changed

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

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -568,12 +568,15 @@ object CheckUnused:
568568
// A class param is unused if its param accessor is unused.
569569
// (The class param is not assigned to a field until constructors.)
570570
// A local param accessor warns as a param; a private accessor as a private member.
571-
// Avoid warning for case class elements because they are aliased via unapply.
571+
// Avoid warning for case class elements because they are aliased via unapply (i.e. may be extracted).
572572
if m.isPrimaryConstructor then
573573
val alias = m.owner.info.member(sym.name)
574574
if alias.exists then
575575
val aliasSym = alias.symbol
576-
if aliasSym.isAllOf(PrivateParamAccessor, butNot = CaseAccessor) && !infos.refs(alias.symbol) then
576+
if aliasSym.isAllOf(PrivateParamAccessor, butNot = CaseAccessor)
577+
&& !infos.refs(alias.symbol)
578+
&& !usedByDefaultGetter(sym, m)
579+
then
577580
if aliasSym.is(Local) then
578581
if ctx.settings.WunusedHas.explicits then
579582
warnAt(pos)(UnusedSymbol.explicitParams(aliasSym))
@@ -582,13 +585,14 @@ object CheckUnused:
582585
warnAt(pos)(UnusedSymbol.privateMembers)
583586
else if ctx.settings.WunusedHas.explicits
584587
&& !sym.is(Synthetic) // param to setter is unused bc there is no field yet
585-
&& !(sym.owner.is(ExtensionMethod) && {
586-
m.paramSymss.dropWhile(_.exists(_.isTypeParam)) match
587-
case (h :: Nil) :: Nil => h == sym // param is the extended receiver
588+
&& !(sym.owner.is(ExtensionMethod) &&
589+
m.paramSymss.dropWhile(_.exists(_.isTypeParam)).match
590+
case (h :: Nil) :: _ => h == sym // param is the extended receiver
588591
case _ => false
589-
})
592+
)
590593
&& !sym.name.isInstanceOf[DerivedName]
591594
&& !ctx.platform.isMainMethod(m)
595+
&& !usedByDefaultGetter(sym, m)
592596
then
593597
warnAt(pos)(UnusedSymbol.explicitParams(sym))
594598
end checkExplicit
@@ -600,6 +604,16 @@ object CheckUnused:
600604
checkExplicit()
601605
end checkParam
602606

607+
// does the param have an alias in a default arg method that is used?
608+
def usedByDefaultGetter(param: Symbol, meth: Symbol): Boolean =
609+
val cls = if meth.isConstructor then meth.enclosingClass.companionModule else meth.enclosingClass
610+
val MethName = meth.name
611+
cls.info.decls.exists: d =>
612+
d.name match
613+
case DefaultGetterName(MethName, _) =>
614+
d.paramSymss.exists(_.exists(p => p.name == param.name && infos.refs(p)))
615+
case _ => false
616+
603617
def checkImplicit(sym: Symbol, pos: SrcPos) =
604618
val m = sym.owner
605619
def allowed =
@@ -629,9 +643,12 @@ object CheckUnused:
629643
val checking =
630644
aliasSym.isAllOf(PrivateParamAccessor, butNot = CaseAccessor)
631645
|| aliasSym.isAllOf(Protected | ParamAccessor, butNot = CaseAccessor) && m.owner.is(Given)
632-
if checking && !infos.refs(alias.symbol) then
646+
if checking
647+
&& !infos.refs(alias.symbol)
648+
&& !usedByDefaultGetter(sym, m)
649+
then
633650
warnAt(pos)(UnusedSymbol.implicitParams(aliasSym))
634-
else
651+
else if !usedByDefaultGetter(sym, m) then
635652
warnAt(pos)(UnusedSymbol.implicitParams(sym))
636653

637654
def checkLocal(sym: Symbol, pos: SrcPos) =

tests/warn/i15503e.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,4 @@ object UnwrapTyped:
9292
error("Compiler bug: `codeOf` was not evaluated by the compiler")
9393

9494
object `default usage`:
95-
def f(i: Int)(j: Int = i * 2) = j // warn I guess
95+
def f(i: Int)(j: Int = i * 2) = j // ~warn~ I guess (see tests/warn/i23349.scala)

tests/warn/i23349.scala

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//> using options -Wunused:explicits,implicits
2+
3+
// An external class that doesn't get its own `copy` method.
4+
class Foo(val a: String, val b: Int)
5+
6+
//
7+
// Example 1: add `copy` method via an extension method.
8+
//
9+
extension (self: Foo)
10+
def copy(a: String = self.a, b: Int = self.b): Foo = Foo(a, b) // nowarn
11+
12+
//
13+
// Example 2: implement `copyFoo` with parameter groups.
14+
//
15+
def copyFoo(foo: Foo)(a: String = foo.a, b: Int = foo.b): Foo = Foo(a, b)
16+
17+
class C:
18+
def copyFoo(foo: Foo, bar: String)(a: String = foo.a, b: Int = foo.b)(c: String = bar): Foo = Foo(a, b) // warn c
19+
def copyUsing(using foo: Foo, bar: String)(a: String = foo.a, b: Int = foo.b)(c: String = bar): Foo = // warn c
20+
Foo(a, b)
21+
22+
class K(k: Int)(s: String = "*"*k):
23+
override val toString = s
24+
25+
class KU(using k: Int)(s: String = "*"*k):
26+
override val toString = s
27+
28+
class KK(s: String):
29+
def this(k: Int)(s: String = "*"*k) = this(s)
30+
override val toString = s

0 commit comments

Comments
 (0)