diff --git a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala index 78bc308079e..d22279a5def 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Parsers.scala @@ -2815,6 +2815,18 @@ self => } expr() } + if (nme.isEncodedUnary(name) && vparamss.nonEmpty) { + 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()) => warnNilary() + case List(List(), x :: xs) if x.mods.isImplicit => warnNilary() + case _ => // ok + } + } DefDef(newmods, name.toTermName, tparams, vparamss, restype, rhs) } signalParseProgress(result.pos) diff --git a/src/reflect/scala/reflect/internal/Printers.scala b/src/reflect/scala/reflect/internal/Printers.scala index 765278b5745..b31a8998287 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/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala index e54fdd29093..15cdeea2a15 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 00000000000..ed5ecc0a17c --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-deprecation.check @@ -0,0 +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 = this` + def unary_~() : Foo = this + ^ +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. +3 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 00000000000..3060cda77f9 --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-deprecation.scala @@ -0,0 +1,13 @@ +// scalac: -Werror -Xlint:deprecation +// +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 + 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 00000000000..83b23feca6f --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-removal.check @@ -0,0 +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 = this` + def unary_~() : Foo = this + ^ +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 new file mode 100644 index 00000000000..1b83f9d1df8 --- /dev/null +++ b/test/files/neg/prefix-unary-nilary-removal.scala @@ -0,0 +1,13 @@ +// scalac: -Xsource:3 +// +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 + val f2 = ~f +}