From 0ee804f5e5ad4e8712bb11c6a9f1bb172ef7586b Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 11 Apr 2024 17:37:57 +0200 Subject: [PATCH 1/2] Restore forgotten fatal-warnings tests In daeee3544a7933736240d1b79ebe81d699d74b0d, support for tests in the directory `tests/pos-special/fatal-warnings` was dropped in favor of magic `//>` comments in the regular `tests/pos` directory, but a few tests were forgotten in the original directory and were thus not run. This commit moves them to the appropriate directory with the correct magic comment. --- compiler/test/dotc/pos-test-pickling.blacklist | 1 + tests/{pos-special/fatal-warnings => pos}/i17735.scala | 4 ++-- tests/{pos-special/fatal-warnings => pos}/i17735a.scala | 2 +- tests/{pos-special/fatal-warnings => pos}/i17741.scala | 4 ++-- tests/{pos-special/fatal-warnings => pos}/nowarnannot.scala | 2 ++ 5 files changed, 8 insertions(+), 5 deletions(-) rename tests/{pos-special/fatal-warnings => pos}/i17735.scala (90%) rename tests/{pos-special/fatal-warnings => pos}/i17735a.scala (90%) rename tests/{pos-special/fatal-warnings => pos}/i17741.scala (90%) rename tests/{pos-special/fatal-warnings => pos}/nowarnannot.scala (66%) diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 81661e87b84e..3ea8b550f160 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -30,6 +30,7 @@ strict-pattern-bindings-3.0-migration.scala i17186b.scala i11982a.scala i17255 +i17735.scala # Tree is huge and blows stack for printing Text i7034.scala diff --git a/tests/pos-special/fatal-warnings/i17735.scala b/tests/pos/i17735.scala similarity index 90% rename from tests/pos-special/fatal-warnings/i17735.scala rename to tests/pos/i17735.scala index f171d4a028f7..17fb31010a8a 100644 --- a/tests/pos-special/fatal-warnings/i17735.scala +++ b/tests/pos/i17735.scala @@ -1,4 +1,4 @@ -//> using options -Wvalue-discard +//> using options -Xfatal-warnings -Wvalue-discard import scala.collection.mutable import scala.annotation.nowarn @@ -21,4 +21,4 @@ object Foo: // here @nowarn is effective without -Wfatal-warnings (i.e. no warning) // But with -Wfatal-warnings we get an error messageBuilder.append("\n").append(s): @nowarn("msg=discarded non-Unit value*") - messageBuilder.result() \ No newline at end of file + messageBuilder.result() diff --git a/tests/pos-special/fatal-warnings/i17735a.scala b/tests/pos/i17735a.scala similarity index 90% rename from tests/pos-special/fatal-warnings/i17735a.scala rename to tests/pos/i17735a.scala index fe0ea7e6bc45..b4d91f8d25fc 100644 --- a/tests/pos-special/fatal-warnings/i17735a.scala +++ b/tests/pos/i17735a.scala @@ -1,4 +1,4 @@ -//> using options -Wvalue-discard -Wconf:msg=non-Unit:s +//> using options -Xfatal-warnings -Wvalue-discard -Wconf:msg=non-Unit:s import scala.collection.mutable import scala.annotation.nowarn diff --git a/tests/pos-special/fatal-warnings/i17741.scala b/tests/pos/i17741.scala similarity index 90% rename from tests/pos-special/fatal-warnings/i17741.scala rename to tests/pos/i17741.scala index 7171aab83e4b..aa32e5a573d4 100644 --- a/tests/pos-special/fatal-warnings/i17741.scala +++ b/tests/pos/i17741.scala @@ -1,4 +1,4 @@ -//> using options -Wnonunit-statement +//> using options -Xfatal-warnings -Wnonunit-statement class Node() class Elem( @@ -29,4 +29,4 @@ object Main { ) } }: @annotation.nowarn() -} \ No newline at end of file +} diff --git a/tests/pos-special/fatal-warnings/nowarnannot.scala b/tests/pos/nowarnannot.scala similarity index 66% rename from tests/pos-special/fatal-warnings/nowarnannot.scala rename to tests/pos/nowarnannot.scala index 26e9713d0543..1710ae34b56f 100644 --- a/tests/pos-special/fatal-warnings/nowarnannot.scala +++ b/tests/pos/nowarnannot.scala @@ -1,3 +1,5 @@ +//> using options -Xfatal-warnings -Wvalue-discard + case class F(i: Int) object Main { From c7570c81c1eb524ce316360316be0e03fcffde9e Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 11 Apr 2024 17:41:28 +0200 Subject: [PATCH 2/2] Suppress "extension method will never be selected" for overrides 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. --- .../dotty/tools/dotc/typer/RefChecks.scala | 53 ++++++++++--------- tests/pos/ext-override.scala | 12 +++++ 2 files changed, 40 insertions(+), 25 deletions(-) create mode 100644 tests/pos/ext-override.scala diff --git a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala index e3d78e3c5707..1397b05ec3b5 100644 --- a/compiler/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/RefChecks.scala @@ -1108,6 +1108,8 @@ object RefChecks { * An extension method is hidden if it does not offer a parameter that is not subsumed * by the corresponding parameter of the member with the same name (or of all alternatives of an overload). * + * This check is suppressed if this method is an override. + * * For example, it is not possible to define a type-safe extension `contains` for `Set`, * since for any parameter type, the existing `contains` method will compile and would be used. * @@ -1125,31 +1127,32 @@ object RefChecks { * If the extension method is nullary, it is always hidden by a member of the same name. * (Either the member is nullary, or the reference is taken as the eta-expansion of the member.) */ - def checkExtensionMethods(sym: Symbol)(using Context): Unit = if sym.is(Extension) then - extension (tp: Type) - def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType - def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes - def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false } - val target = sym.info.firstExplicitParamTypes.head // required for extension method, the putative receiver - val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter - def hidden = - target.nonPrivateMember(sym.name) - .filterWithPredicate: - member => - val memberIsImplicit = member.info.hasImplicitParams - val paramTps = - if memberIsImplicit then methTp.stripPoly.firstParamTypes - else methTp.firstExplicitParamTypes - - paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || { - val memberParamTps = member.info.stripPoly.firstParamTypes - !memberParamTps.isEmpty - && memberParamTps.lengthCompare(paramTps) == 0 - && memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m) - } - .exists - if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden - then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos) + def checkExtensionMethods(sym: Symbol)(using Context): Unit = + if sym.is(Extension) && !sym.nextOverriddenSymbol.exists then + extension (tp: Type) + def strippedResultType = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).resultType + def firstExplicitParamTypes = Applications.stripImplicit(tp.stripPoly, wildcardOnly = true).firstParamTypes + def hasImplicitParams = tp.stripPoly match { case mt: MethodType => mt.isImplicitMethod case _ => false } + val target = sym.info.firstExplicitParamTypes.head // required for extension method, the putative receiver + val methTp = sym.info.strippedResultType // skip leading implicits and the "receiver" parameter + def hidden = + target.nonPrivateMember(sym.name) + .filterWithPredicate: + member => + val memberIsImplicit = member.info.hasImplicitParams + val paramTps = + if memberIsImplicit then methTp.stripPoly.firstParamTypes + else methTp.firstExplicitParamTypes + + paramTps.isEmpty || memberIsImplicit && !methTp.hasImplicitParams || { + val memberParamTps = member.info.stripPoly.firstParamTypes + !memberParamTps.isEmpty + && memberParamTps.lengthCompare(paramTps) == 0 + && memberParamTps.lazyZip(paramTps).forall((m, x) => x frozen_<:< m) + } + .exists + if !target.typeSymbol.denot.isAliasType && !target.typeSymbol.denot.isOpaqueAlias && hidden + then report.warning(ExtensionNullifiedByMember(sym, target.typeSymbol), sym.srcPos) end checkExtensionMethods /** Verify that references in the user-defined `@implicitNotFound` message are valid. diff --git a/tests/pos/ext-override.scala b/tests/pos/ext-override.scala new file mode 100644 index 000000000000..d08439e13c9a --- /dev/null +++ b/tests/pos/ext-override.scala @@ -0,0 +1,12 @@ +//> using options -Xfatal-warnings + +trait Foo[T]: + extension (x: T) + def hi: String + +class Bla: + def hi: String = "hi" +object Bla: + given Foo[Bla] with + extension (x: Bla) + def hi: String = x.hi