From 3240841c893131ff40cd1ce09bf3e6097d302d09 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 24 Jun 2020 19:14:34 -0400 Subject: [PATCH 1/3] Deprecate empty-paren (nilary) prefix unary operator Ref scala/bug 12055 > Now that auto-application is deprecated, a prefix with a nilary creates a warning that cannot be avoided while maintaining a prefix position (or permanently applying warning suppression). This adds the following warning ``` deprecation.scala:4: warning: empty-paren (nilary) prefix unary operator is deprecated: instead, remove () to declare as `def unary_~ : Foo` ``` I've opted to use the term "empty-paren" as defined in https://www.artima.com/pins1ed/composition-and-inheritance.html. --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 10 ++++++++++ src/reflect/scala/reflect/internal/StdNames.scala | 2 ++ test/files/neg/prefix-unary-nilary-deprecation.check | 11 +++++++++++ test/files/neg/prefix-unary-nilary-deprecation.scala | 11 +++++++++++ test/files/neg/prefix-unary-nilary-removal.check | 4 ++++ test/files/neg/prefix-unary-nilary-removal.scala | 11 +++++++++++ 6 files changed, 49 insertions(+) create mode 100644 test/files/neg/prefix-unary-nilary-deprecation.check create mode 100644 test/files/neg/prefix-unary-nilary-deprecation.scala create mode 100644 test/files/neg/prefix-unary-nilary-removal.check create mode 100644 test/files/neg/prefix-unary-nilary-removal.scala diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 78bc308079e3..ca796fc46050 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2815,6 +2815,16 @@ self => } expr() } + if (nme.isEncodedUnary(name) && vparamss.nonEmpty) { + val tpeStr = if (restype.isEmpty) "" else s" : $restype" + def unaryMsg(what: String) = s"empty-paren (nilary) prefix unary operator is $what: instead, remove () to declare as `def ${name.decodedName}$tpeStr`" + vparamss match { + case List(List()) => + if (currentRun.isScala3) syntaxError(nameOffset, unaryMsg("unsupported")) + else deprecationWarning(nameOffset, unaryMsg("deprecated"), "2.13.4") + case _ => // ok + } + } DefDef(newmods, name.toTermName, tparams, vparamss, restype, rhs) } signalParseProgress(result.pos) diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index e54fdd290932..15cdeea2a155 100644 --- a/src/reflect/scala/reflect/internal/StdNames.scala +++ b/src/reflect/scala/reflect/internal/StdNames.scala @@ -988,6 +988,8 @@ trait StdNames { val UNARY_- = encode("unary_-") val UNARY_! = encode("unary_!") + val isEncodedUnary = Set[Name](UNARY_~, UNARY_+, UNARY_-, UNARY_!) + // Grouped here so Cleanup knows what tests to perform. val CommonOpNames = Set[Name](OR, XOR, AND, EQ, NE) val BooleanOpNames = Set[Name](ZOR, ZAND, UNARY_!) ++ CommonOpNames diff --git a/test/files/neg/prefix-unary-nilary-deprecation.check b/test/files/neg/prefix-unary-nilary-deprecation.check new file mode 100644 index 000000000000..caf40a59225b --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-deprecation.check @@ -0,0 +1,11 @@ +prefix-unary-nilary-deprecation.scala:4: warning: empty-paren (nilary) prefix unary operator is deprecated: instead, remove () to declare as `def unary_~ : Foo` + def unary_~() : Foo = this + ^ +prefix-unary-nilary-deprecation.scala:10: warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method unary_~, +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. + val f2 = ~f + ^ +error: No warnings can be incurred under -Werror. +2 warnings +1 error diff --git a/test/files/neg/prefix-unary-nilary-deprecation.scala b/test/files/neg/prefix-unary-nilary-deprecation.scala new file mode 100644 index 000000000000..0dad1f65eb6b --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-deprecation.scala @@ -0,0 +1,11 @@ +// scalac: -Werror -Xlint:deprecation +// +class Foo { + def unary_~() : Foo = this + + def unary_! : Foo = this // ok +} +object Test { + val f = new Foo + val f2 = ~f +} diff --git a/test/files/neg/prefix-unary-nilary-removal.check b/test/files/neg/prefix-unary-nilary-removal.check new file mode 100644 index 000000000000..1bc3a44c5a23 --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-removal.check @@ -0,0 +1,4 @@ +prefix-unary-nilary-removal.scala:4: error: empty-paren (nilary) prefix unary operator is unsupported: instead, remove () to declare as `def unary_~ : Foo` + def unary_~() : Foo = this + ^ +1 error diff --git a/test/files/neg/prefix-unary-nilary-removal.scala b/test/files/neg/prefix-unary-nilary-removal.scala new file mode 100644 index 000000000000..fde5a1043caa --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-removal.scala @@ -0,0 +1,11 @@ +// scalac: -Xsource:3 +// +class Foo { + def unary_~() : Foo = this + + def unary_! : Foo = this // ok +} +object Test { + val f = new Foo + val f2 = ~f +} From 12f1c41406a528cb8b98a76f3142a037cf780b9d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 24 Jun 2020 21:49:48 -0400 Subject: [PATCH 2/3] Update warning message per review --- src/compiler/scala/tools/nsc/ast/parser/Parsers.scala | 2 +- test/files/neg/prefix-unary-nilary-deprecation.check | 2 +- test/files/neg/prefix-unary-nilary-removal.check | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index ca796fc46050..1961fceeb230 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2817,7 +2817,7 @@ self => } if (nme.isEncodedUnary(name) && vparamss.nonEmpty) { val tpeStr = if (restype.isEmpty) "" else s" : $restype" - def unaryMsg(what: String) = s"empty-paren (nilary) prefix unary operator is $what: instead, remove () to declare as `def ${name.decodedName}$tpeStr`" + def unaryMsg(what: String) = s"unary prefix operator definition with empty parameter list is $what: instead, remove () to declare as `def ${name.decodedName}$tpeStr`" vparamss match { case List(List()) => if (currentRun.isScala3) syntaxError(nameOffset, unaryMsg("unsupported")) diff --git a/test/files/neg/prefix-unary-nilary-deprecation.check b/test/files/neg/prefix-unary-nilary-deprecation.check index caf40a59225b..be1eddcbbb15 100644 --- a/test/files/neg/prefix-unary-nilary-deprecation.check +++ b/test/files/neg/prefix-unary-nilary-deprecation.check @@ -1,4 +1,4 @@ -prefix-unary-nilary-deprecation.scala:4: warning: empty-paren (nilary) prefix unary operator is deprecated: instead, remove () to declare as `def unary_~ : Foo` +prefix-unary-nilary-deprecation.scala:4: warning: unary prefix operator definition with empty parameter list is deprecated: instead, remove () to declare as `def unary_~ : Foo` def unary_~() : Foo = this ^ prefix-unary-nilary-deprecation.scala:10: warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method unary_~, diff --git a/test/files/neg/prefix-unary-nilary-removal.check b/test/files/neg/prefix-unary-nilary-removal.check index 1bc3a44c5a23..ac671f41875c 100644 --- a/test/files/neg/prefix-unary-nilary-removal.check +++ b/test/files/neg/prefix-unary-nilary-removal.check @@ -1,4 +1,4 @@ -prefix-unary-nilary-removal.scala:4: error: empty-paren (nilary) prefix unary operator is unsupported: instead, remove () to declare as `def unary_~ : Foo` +prefix-unary-nilary-removal.scala:4: error: unary prefix operator definition with empty parameter list is unsupported: instead, remove () to declare as `def unary_~ : Foo` def unary_~() : Foo = this ^ 1 error From 8cdf16ef9acc0fe4257169db66ca3a2b6934c542 Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Thu, 25 Jun 2020 12:26:34 -0400 Subject: [PATCH 3/3] Print warning for ()(implicit ...) prefix unary operators too --- .../scala/tools/nsc/ast/parser/Parsers.scala | 12 +++++++----- src/reflect/scala/reflect/internal/Printers.scala | 14 +++++++++----- .../neg/prefix-unary-nilary-deprecation.check | 9 ++++++--- .../neg/prefix-unary-nilary-deprecation.scala | 2 ++ test/files/neg/prefix-unary-nilary-removal.check | 7 +++++-- test/files/neg/prefix-unary-nilary-removal.scala | 2 ++ 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 1961fceeb230..d22279a5def2 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2816,12 +2816,14 @@ self => expr() } if (nme.isEncodedUnary(name) && vparamss.nonEmpty) { - val tpeStr = if (restype.isEmpty) "" else s" : $restype" - def unaryMsg(what: String) = s"unary prefix operator definition with empty parameter list is $what: instead, remove () to declare as `def ${name.decodedName}$tpeStr`" + def instead = DefDef(newmods, name.toTermName.decodedName, tparams, vparamss.drop(1), restype, rhs) + def unaryMsg(what: String) = s"unary prefix operator definition with empty parameter list is $what: instead, remove () to declare as `$instead`" + def warnNilary(): Unit = + if (currentRun.isScala3) syntaxError(nameOffset, unaryMsg("unsupported")) + else deprecationWarning(nameOffset, unaryMsg("deprecated"), "2.13.4") vparamss match { - case List(List()) => - if (currentRun.isScala3) syntaxError(nameOffset, unaryMsg("unsupported")) - else deprecationWarning(nameOffset, unaryMsg("deprecated"), "2.13.4") + case List(List()) => warnNilary() + case List(List(), x :: xs) if x.mods.isImplicit => warnNilary() case _ => // ok } } diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 765278b5745c..b31a8998287f 100644 --- a/src/reflect/scala/reflect/internal/Printers.scala +++ b/src/reflect/scala/reflect/internal/Printers.scala @@ -81,6 +81,10 @@ trait Printers extends api.Printers { self: SymbolTable => def indent() = indentMargin += indentStep def undent() = indentMargin -= indentStep + protected def checkForBlank(cond: Boolean) = if (cond) " " else "" + protected def blankForOperatorName(name: Name) = checkForBlank(name.isOperatorName) + protected def blankForName(name: Name) = checkForBlank(name.isOperatorName || name.endsWith("_")) + def printPosition(tree: Tree) = if (printPositions) comment(print(tree.pos.show)) @@ -356,7 +360,11 @@ trait Printers extends api.Printers { self: SymbolTable => } case dd @ DefDef(mods, name, tparams, vparamss, tp, rhs) => - printDefDef(dd, symName(tree, name))(printOpt(": ", tp))(printOpt(" = ", rhs)) + printDefDef(dd, symName(tree, name)) { + // place space after symbolic def name (def !: Unit does not compile) + if (tparams.isEmpty && vparamss.isEmpty) printOpt(blankForName(name.encodedName) + ": ", tp) + else printOpt(": ", tp) + } (printOpt(" = ", rhs)) case td @ TypeDef(mods, name, tparams, rhs) => printTypeDef(td, symName(tree, name)) @@ -593,10 +601,6 @@ trait Printers extends api.Printers { self: SymbolTable => } } - protected def checkForBlank(cond: Boolean) = if (cond) " " else "" - protected def blankForOperatorName(name: Name) = checkForBlank(name.isOperatorName) - protected def blankForName(name: Name) = checkForBlank(name.isOperatorName || name.endsWith("_")) - protected def resolveSelect(t: Tree): String = { t match { // case for: 1) (if (a) b else c).meth1.meth2 or 2) 1 + 5 should be represented as (1).+(5) diff --git a/test/files/neg/prefix-unary-nilary-deprecation.check b/test/files/neg/prefix-unary-nilary-deprecation.check index be1eddcbbb15..ed5ecc0a17ce 100644 --- a/test/files/neg/prefix-unary-nilary-deprecation.check +++ b/test/files/neg/prefix-unary-nilary-deprecation.check @@ -1,11 +1,14 @@ -prefix-unary-nilary-deprecation.scala:4: warning: unary prefix operator definition with empty parameter list is deprecated: instead, remove () to declare as `def unary_~ : Foo` +prefix-unary-nilary-deprecation.scala:4: warning: unary prefix operator definition with empty parameter list is deprecated: instead, remove () to declare as `def unary_~ : Foo = this` def unary_~() : Foo = this ^ -prefix-unary-nilary-deprecation.scala:10: warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method unary_~, +prefix-unary-nilary-deprecation.scala:5: warning: unary prefix operator definition with empty parameter list is deprecated: instead, remove () to declare as `def unary_-(implicit pos: Long) = this` + def unary_-()(implicit pos: Long) = this + ^ +prefix-unary-nilary-deprecation.scala:12: warning: Auto-application to `()` is deprecated. Supply the empty argument list `()` explicitly to invoke method unary_~, 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. val f2 = ~f ^ error: No warnings can be incurred under -Werror. -2 warnings +3 warnings 1 error diff --git a/test/files/neg/prefix-unary-nilary-deprecation.scala b/test/files/neg/prefix-unary-nilary-deprecation.scala index 0dad1f65eb6b..3060cda77f97 100644 --- a/test/files/neg/prefix-unary-nilary-deprecation.scala +++ b/test/files/neg/prefix-unary-nilary-deprecation.scala @@ -2,8 +2,10 @@ // class Foo { def unary_~() : Foo = this + def unary_-()(implicit pos: Long) = this def unary_! : Foo = this // ok + def unary_+(implicit pos: Long) = this // ok } object Test { val f = new Foo diff --git a/test/files/neg/prefix-unary-nilary-removal.check b/test/files/neg/prefix-unary-nilary-removal.check index ac671f41875c..83b23feca6fb 100644 --- a/test/files/neg/prefix-unary-nilary-removal.check +++ b/test/files/neg/prefix-unary-nilary-removal.check @@ -1,4 +1,7 @@ -prefix-unary-nilary-removal.scala:4: error: unary prefix operator definition with empty parameter list is unsupported: instead, remove () to declare as `def unary_~ : Foo` +prefix-unary-nilary-removal.scala:4: error: unary prefix operator definition with empty parameter list is unsupported: instead, remove () to declare as `def unary_~ : Foo = this` def unary_~() : Foo = this ^ -1 error +prefix-unary-nilary-removal.scala:5: error: unary prefix operator definition with empty parameter list is unsupported: instead, remove () to declare as `def unary_-(implicit pos: Long) = this` + def unary_-()(implicit pos: Long) = this + ^ +2 errors diff --git a/test/files/neg/prefix-unary-nilary-removal.scala b/test/files/neg/prefix-unary-nilary-removal.scala index fde5a1043caa..1b83f9d1df8a 100644 --- a/test/files/neg/prefix-unary-nilary-removal.scala +++ b/test/files/neg/prefix-unary-nilary-removal.scala @@ -2,8 +2,10 @@ // class Foo { def unary_~() : Foo = this + def unary_-()(implicit pos: Long) = this def unary_! : Foo = this // ok + def unary_+(implicit pos: Long) = this // ok } object Test { val f = new Foo