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

Deprecate auto-application of (non-Java-defined) methods with a single empty parameter list #8833

Merged
merged 1 commit into from Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion project/GenerateFunctionConverters.scala
Expand Up @@ -348,7 +348,7 @@ object GenerateFunctionConverters {

def sameText(f: java.io.File, text: String): Boolean = {
val x = scala.io.Source.fromFile(f)
val lines = try { x.getLines.toVector } finally { x.close }
val lines = try { x.getLines().toVector } finally { x.close }
// work around scala/bug#11125
lines.iterator.filter(_.nonBlank) == Predef.augmentString(text).lines.filter(_.nonBlank)
}
Expand Down
4 changes: 2 additions & 2 deletions spec/03-types.md
Expand Up @@ -685,7 +685,7 @@ An overloaded type consisting of type alternatives $T_1 \commadots T_n (n \geq 2

###### Example
```scala
def println: Unit
def println(): Unit
def println(s: String): Unit = $\ldots$
def println(x: Float): Unit = $\ldots$
def println(x: Float, width: Int): Unit = $\ldots$
Expand All @@ -694,7 +694,7 @@ def println[A](x: A)(tostring: A => String): Unit = $\ldots$
define a single function `println` which has an overloaded
type.
```
println: => Unit $\overload$
println: () Unit $\overload$
(String) Unit $\overload$
(Float) Unit $\overload$
(Float, Int) Unit $\overload$
Expand Down
26 changes: 11 additions & 15 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Expand Up @@ -974,27 +974,23 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
}

def matchNullaryLoosely: Boolean = {
def test(sym: Symbol) =
sym.isJavaDefined ||
sym.owner == AnyClass ||
sym == Object_clone
def test(sym: Symbol) = sym.isJavaDefined || sym.owner == AnyClass
test(meth) || meth.overrides.exists(test)
}
// (4.2) condition for auto-application by -Xsource level

// (4.2) condition for auto-application
//
// until 3.0: none (assuming condition for (4.3) was not met)
// in 3.0: `meth.isJavaDefined`
// (TODO decide -- currently the condition is more involved to give slack to Scala methods overriding Java-defined ones;
// I think we should resolve that by introducing slack in overriding e.g. a Java-defined `def toString()` by a Scala-defined `def toString`.
// This also works better for dealing with accessors overriding Java-defined methods. The current strategy in methodSig is problematic:
// > // Add a () parameter section if this overrides some method with () parameters
// > val vparamSymssOrEmptyParamsFromOverride =
// This means an accessor that overrides a Java-defined method gets a MethodType instead of a NullaryMethodType, which breaks lots of assumptions about accessors)
// Currently the condition is more involved to give slack to Scala methods overriding Java-defined ones;
// I (moors) think we should resolve that by introducing slack in overriding e.g. a Java-defined `def toString()` by a Scala-defined `def toString`.
// This also works better for dealing with accessors overriding Java-defined methods. The current strategy in methodSig is problematic:
// > // Add a () parameter section if this overrides some method with () parameters
// > val vparamSymssOrEmptyParamsFromOverride =
// This means an accessor that overrides a Java-defined method gets a MethodType instead of a NullaryMethodType, which breaks lots of assumptions about accessors)
def checkCanAutoApply(): Boolean = {
if (sourceLevel3 && !isPastTyper && !matchNullaryLoosely) {
if (!isPastTyper && !matchNullaryLoosely) {
context.deprecationWarning(tree.pos, NoSymbol, s"Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method ${meth.decodedName},\n" +
s"or remove the empty argument list from its definition (Java-defined methods are exempt).\n"+
s"In Scala 3, an unapplied method like this will be eta-expanded into a function.", "3.0.0")
s"In Scala 3, an unapplied method like this will be eta-expanded into a function.", "2.13.3")
}
true
}
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/collection/Iterator.scala
Expand Up @@ -40,7 +40,7 @@ import scala.runtime.Statics
* {{{
* def f[A](it: Iterator[A]) = {
* if (it.hasNext) { // Safe to reuse "it" after "hasNext"
* it.next // Safe to reuse "it" after "next"
* it.next() // Safe to reuse "it" after "next"
* val remainder = it.drop(2) // it is *not* safe to use "it" again after this line!
* remainder.take(2) // it is *not* safe to use "remainder" after this line!
* } else it
Expand Down
2 changes: 1 addition & 1 deletion src/library/scala/util/control/Exception.scala
Expand Up @@ -60,7 +60,7 @@ import scala.language.implicitConversions
* def printUrl(url: String) : Unit = {
* val con = new URL(url) openConnection()
* val source = scala.io.Source.fromInputStream(con.getInputStream())
* source.getLines.foreach(println)
* source.getLines().foreach(println)
* }
*
* val badUrl = "htt/xx"
Expand Down
6 changes: 3 additions & 3 deletions src/manual/scala/tools/docutil/EmitManPage.scala
Expand Up @@ -70,9 +70,9 @@ object EmitManPage {
for (d <- definitions) {
out println ".TP"
emitText(d.term)
out.println
out.println()
emitText(d.description)
if (n > 1) { out.println; n -= 1 }
if (n > 1) { out.println(); n -= 1 }
}

case Link(label, url) =>
Expand All @@ -88,7 +88,7 @@ object EmitManPage {
case TextParagraph(text) =>
out println ".PP"
emitText(text)
out.println
out.println()

case BlockQuote(text) =>
out println ".TP"
Expand Down
Expand Up @@ -31,7 +31,7 @@ class VectorIterationBenchmark {
var i = 0
val it = value.iterator
while (i < size) {
bh.consume(it.next)
bh.consume(it.next())
i += 1
}
}
Expand Down
Expand Up @@ -27,7 +27,7 @@ class BitSetIteratorBenchmark {
@Benchmark def iterateAll(): Unit = {
var sum = 0
val it = bs.iterator
while (it.hasNext) sum += it.next
while (it.hasNext) sum += it.next()
}

}
4 changes: 2 additions & 2 deletions test/files/jvm/annotations/Test_2.scala
Expand Up @@ -120,15 +120,15 @@ object Test4 {
anns foreach printSourceAnnotation
if (anns.length > 0) {
println(target)
println
println()
}
}
def printParamSourceAnnotations(target: { def getParameterAnnotations(): Array[Array[Annotation]] }): Unit = {
val anns = target.getParameterAnnotations().flatten
anns foreach printSourceAnnotation
if (anns.length > 0) {
println(target)
println
println()
}
}
printSourceAnnotations(classOf[Foo1])
Expand Down
2 changes: 1 addition & 1 deletion test/files/jvm/console.scala
Expand Up @@ -6,7 +6,7 @@ object Test extends App {
print(true)
print(1)
print(1.0)
flush
flush()
println("..")
println(1)
printf("Argument nr. %d has value %1.2f\n",
Expand Down
2 changes: 1 addition & 1 deletion test/files/jvm/future-spec/PromiseTests.scala
Expand Up @@ -104,7 +104,7 @@ class PromiseTests extends MinimalScalaTest {
"not be completable with a completed Promise" in {
{
val p = Promise[String]().failure(new RuntimeException("unbr0ken"))
p.tryCompleteWith(Promise[String].failure(new Exception("br0ken")).future)
p.tryCompleteWith(Promise[String]().failure(new Exception("br0ken")).future)
intercept[RuntimeException] {
Await.result(p.future, defaultTimeout)
}.getMessage mustBe ("unbr0ken")
Expand Down
8 changes: 4 additions & 4 deletions test/files/jvm/scala-concurrent-tck.scala
Expand Up @@ -117,15 +117,15 @@ class FutureCallbacks extends TestBase {
}

def testThatNestedCallbacksDoNotYieldStackOverflow(): Unit = {
val promise = Promise[Int]
val promise = Promise[Int]()
(0 to 10000).map(Future(_)).foldLeft(promise.future)((pf, f) => f.flatMap(i => pf))
promise.success(-1)
}

def stressTestNumberofCallbacks(): Unit = once {
done =>
val promise = Promise[Unit]
val otherPromise = Promise[Unit]
val promise = Promise[Unit]()
val otherPromise = Promise[Unit]()
def attachMeaninglessCallbacksTo[T](f: Future[T]): Future[T] = {
(1 to 20000).foreach(_ => f.onComplete(_ => ()))
f
Expand Down Expand Up @@ -359,7 +359,7 @@ def testTransformFailure(): Unit = once {
def testFlatMapDelayed(): Unit = once {
done =>
val f = Future { 5 }
val p = Promise[Int]
val p = Promise[Int]()
val g = f flatMap { _ => p.future }
g onComplete {
case Success(x) => done(x == 10)
Expand Down
2 changes: 1 addition & 1 deletion test/files/jvm/serialization.scala
Expand Up @@ -531,6 +531,6 @@ object Test10_util {
{
val random = new Random(345)
val random2: Random = read(write(random))
rep(5) { assert(random.nextInt == random2.nextInt) }
rep(5) { assert(random.nextInt() == random2.nextInt()) }
}
}
2 changes: 1 addition & 1 deletion test/files/jvm/throws-annot-from-java.check
Expand Up @@ -17,7 +17,7 @@ scala> :paste
println("atp.typeParams.isEmpty: " + atp.typeParams.isEmpty)
println(throwsAnn)
}
println
println()

{
val method = clazz.info.member(newTermName("bar"))
Expand Down
2 changes: 1 addition & 1 deletion test/files/jvm/throws-annot-from-java/Test_3.scala
Expand Up @@ -13,7 +13,7 @@ object Test extends ReplTest {
println("atp.typeParams.isEmpty: " + atp.typeParams.isEmpty)
println(throwsAnn)
}
println
println()
{
val method = clazz.info.member(newTermName("bar"))
Expand Down
16 changes: 8 additions & 8 deletions test/files/jvm/typerep.scala
Expand Up @@ -49,7 +49,7 @@ object testPrimitives {
println(getType("abc"))
println(getType(())) // Unit
println(getType(classOf[Int])) // Class
println
println()
}

object testOptions {
Expand All @@ -62,7 +62,7 @@ object testOptions {
println(getType(None: Option[Int]))
val y: Option[Int] = None
println(getType(y))
println
println()
}

object testLists {
Expand All @@ -71,7 +71,7 @@ object testLists {
println(getType(List(List(3))))
println(getType(Nil: List[Int]))
println(getType(List(1, "abc")))
println
println()
}

object testArrays {
Expand All @@ -81,7 +81,7 @@ object testArrays {
println(getType(List(1).toArray))
println(getType(List[Int]().toArray))
println(getType(Array(3).drop(1).toArray)) // empty
println
println()
}

object testTuples {
Expand All @@ -90,7 +90,7 @@ object testTuples {
println(getType(((3, "abc"), (4, "xyz"))))
println(getType(((Some('b'), 3), (Some('a'), 4))))
//println(getType(((Some('b'), 3), (None, 4))))
println
println()
}

object testFuncs {
Expand All @@ -109,7 +109,7 @@ object testFuncs {
def f5(f: Int => Int, x: Int) = f(x)
println(getType(f5 _))
println(getType(f5(f1, 1)))
println
println()
}

class Foo {
Expand All @@ -135,12 +135,12 @@ object testClasses {
val foo2 = new Foo
println(getType(foo2))
println(getType(new foo2.Bar(1)))
println
println()
println(getType(pkg1.c1))
val c1 = new pkg1.C1
println(getType(c1))
println
println()
*/
}

Expand Down
2 changes: 1 addition & 1 deletion test/files/jvm/unittest_io_Jvm.scala
Expand Up @@ -8,7 +8,7 @@ object Test {
|it is split on several lines.
|
|isn't it?
|""".stripMargin).getLines.toList
|""".stripMargin).getLines().toList
println("lines.size = " + lines.size)
lines.foreach(println)
}
Expand Down
Empty file removed test/files/jvm/unreachable.check
Empty file.
14 changes: 7 additions & 7 deletions test/files/jvm/unreachable/Foo_1.scala
Expand Up @@ -9,14 +9,14 @@ class Foo_1 {

def unreachableIf: Int = {
return 42
if (util.Random.nextInt % 2 == 0)
if (util.Random.nextInt() % 2 == 0)
0
else
1
}

def unreachableIfBranches: Int = {
if (util.Random.nextInt % 2 == 0)
if (util.Random.nextInt() % 2 == 0)
return 42
else
return 42
Expand All @@ -25,14 +25,14 @@ class Foo_1 {
}

def unreachableOneLegIf: Int = {
if (util.Random.nextInt % 2 == 0)
if (util.Random.nextInt() % 2 == 0)
return 42

return 42
}

def unreachableLeftBranch: Int = {
val result = if (util.Random.nextInt % 2 == 0)
val result = if (util.Random.nextInt() % 2 == 0)
return 42
else
42
Expand All @@ -41,7 +41,7 @@ class Foo_1 {
}

def unreachableRightBranch: Int = {
val result = if (util.Random.nextInt % 2 == 0)
val result = if (util.Random.nextInt() % 2 == 0)
42
else
return 42
Expand Down Expand Up @@ -92,7 +92,7 @@ class Foo_1 {

def unreachableSwitch: Int = {
return 42
val x = util.Random.nextInt % 2
val x = util.Random.nextInt() % 2
x match {
case 0 => return 0
case 1 => return 1
Expand All @@ -102,7 +102,7 @@ class Foo_1 {
}

def unreachableAfterSwitch: Int = {
val x = util.Random.nextInt % 2
val x = util.Random.nextInt() % 2
x match {
case 0 => return 42
case 1 => return 41 + x
Expand Down
12 changes: 9 additions & 3 deletions test/files/neg/auto-application.check
@@ -1,10 +1,16 @@
auto-application.scala:2: error: Int does not take parameters
auto-application.scala:4: error: Int does not take parameters
("": Any).##()
^
auto-application.scala:3: error: Int does not take parameters
auto-application.scala:5: error: Int does not take parameters
("": AnyRef).##()
^
auto-application.scala:4: error: Int does not take parameters
auto-application.scala:6: error: Int does not take parameters
("": Object).##()
^
auto-application.scala:9: warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method meth,
or remove the empty argument list from its definition (Java-defined methods are exempt).
In Scala 3, an unapplied method like this will be eta-expanded into a function.
meth // warn, auto-application (of nilary methods) is deprecated
^
1 warning
3 errors
5 changes: 5 additions & 0 deletions test/files/neg/auto-application.scala
@@ -1,5 +1,10 @@
// scalac: -deprecation -Werror

class Test {
("": Any).##()
("": AnyRef).##()
("": Object).##()

def meth() = ""
meth // warn, auto-application (of nilary methods) is deprecated
}