Skip to content

Commit b096764

Browse files
authored
Suppress "extension method will never be selected" for overrides (#20164)
When we're overriding an existing extension method, we don't have the liberty of renaming the method, so we shouldn't get warnings we can't do anything about.
2 parents a14c79e + c7570c8 commit b096764

File tree

7 files changed

+48
-30
lines changed

7 files changed

+48
-30
lines changed

compiler/src/dotty/tools/dotc/typer/RefChecks.scala

+28-25
Original file line numberDiff line numberDiff line change
@@ -1108,6 +1108,8 @@ object RefChecks {
11081108
* An extension method is hidden if it does not offer a parameter that is not subsumed
11091109
* by the corresponding parameter of the member with the same name (or of all alternatives of an overload).
11101110
*
1111+
* This check is suppressed if this method is an override.
1112+
*
11111113
* For example, it is not possible to define a type-safe extension `contains` for `Set`,
11121114
* since for any parameter type, the existing `contains` method will compile and would be used.
11131115
*
@@ -1125,31 +1127,32 @@ object RefChecks {
11251127
* If the extension method is nullary, it is always hidden by a member of the same name.
11261128
* (Either the member is nullary, or the reference is taken as the eta-expansion of the member.)
11271129
*/
1128-
def checkExtensionMethods(sym: Symbol)(using Context): Unit = if sym.is(Extension) then
1129-
extension (tp: Type)
1130-
def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType
1131-
def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes
1132-
def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false }
1133-
val target = sym.info.firstExplicitParamTypes.head // required for extension method, the putative receiver
1134-
val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter
1135-
def hidden =
1136-
target.nonPrivateMember(sym.name)
1137-
.filterWithPredicate:
1138-
member =>
1139-
val memberIsImplicit = member.info.hasImplicitParams
1140-
val paramTps =
1141-
if memberIsImplicit then methTp.stripPoly.firstParamTypes
1142-
else methTp.firstExplicitParamTypes
1143-
1144-
paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || {
1145-
val memberParamTps = member.info.stripPoly.firstParamTypes
1146-
!memberParamTps.isEmpty
1147-
&& memberParamTps.lengthCompare(paramTps) == 0
1148-
&& memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m)
1149-
}
1150-
.exists
1151-
if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden
1152-
then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos)
1130+
def checkExtensionMethods(sym: Symbol)(using Context): Unit =
1131+
if sym.is(Extension) && !sym.nextOverriddenSymbol.exists then
1132+
extension (tp: Type)
1133+
def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType
1134+
def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes
1135+
def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false }
1136+
val target = sym.info.firstExplicitParamTypes.head // required for extension method, the putative receiver
1137+
val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter
1138+
def hidden =
1139+
target.nonPrivateMember(sym.name)
1140+
.filterWithPredicate:
1141+
member =>
1142+
val memberIsImplicit = member.info.hasImplicitParams
1143+
val paramTps =
1144+
if memberIsImplicit then methTp.stripPoly.firstParamTypes
1145+
else methTp.firstExplicitParamTypes
1146+
1147+
paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || {
1148+
val memberParamTps = member.info.stripPoly.firstParamTypes
1149+
!memberParamTps.isEmpty
1150+
&& memberParamTps.lengthCompare(paramTps) == 0
1151+
&& memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m)
1152+
}
1153+
.exists
1154+
if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden
1155+
then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos)
11531156
end checkExtensionMethods
11541157

11551158
/** Verify that references in the user-defined `@implicitNotFound` message are valid.

compiler/test/dotc/pos-test-pickling.blacklist

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ strict-pattern-bindings-3.0-migration.scala
3030
i17186b.scala
3131
i11982a.scala
3232
i17255
33+
i17735.scala
3334

3435
# Tree is huge and blows stack for printing Text
3536
i7034.scala

tests/pos/ext-override.scala

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//> using options -Xfatal-warnings
2+
3+
trait Foo[T]:
4+
extension (x: T)
5+
def hi: String
6+
7+
class Bla:
8+
def hi: String = "hi"
9+
object Bla:
10+
given Foo[Bla] with
11+
extension (x: Bla)
12+
def hi: String = x.hi

tests/pos-special/fatal-warnings/i17735.scala renamed to tests/pos/i17735.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//> using options -Wvalue-discard
1+
//> using options -Xfatal-warnings -Wvalue-discard
22

33
import scala.collection.mutable
44
import scala.annotation.nowarn
@@ -21,4 +21,4 @@ object Foo:
2121
// here @nowarn is effective without -Wfatal-warnings (i.e. no warning)
2222
// But with -Wfatal-warnings we get an error
2323
messageBuilder.append("\n").append(s): @nowarn("msg=discarded non-Unit value*")
24-
messageBuilder.result()
24+
messageBuilder.result()

tests/pos-special/fatal-warnings/i17735a.scala renamed to tests/pos/i17735a.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//> using options -Wvalue-discard -Wconf:msg=non-Unit:s
1+
//> using options -Xfatal-warnings -Wvalue-discard -Wconf:msg=non-Unit:s
22

33
import scala.collection.mutable
44
import scala.annotation.nowarn

tests/pos-special/fatal-warnings/i17741.scala renamed to tests/pos/i17741.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//> using options -Wnonunit-statement
1+
//> using options -Xfatal-warnings -Wnonunit-statement
22

33
class Node()
44
class Elem(
@@ -29,4 +29,4 @@ object Main {
2929
)
3030
}
3131
}: @annotation.nowarn()
32-
}
32+
}

tests/pos-special/fatal-warnings/nowarnannot.scala renamed to tests/pos/nowarnannot.scala

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//> using options -Xfatal-warnings -Wvalue-discard
2+
13
case class F(i: Int)
24

35
object Main {

0 commit comments

Comments
 (0)