From 82aab283a94f88b5440a68152692cca93ce84848 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 7 Sep 2024 08:20:22 -0700 Subject: [PATCH 1/3] Deprecate infix named args --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 9 +++++++-- tests/warn/infix-named-args.scala | 7 +++++++ 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 tests/warn/infix-named-args.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6390d8d32d3f..818834359f05 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1104,9 +1104,14 @@ object Parsers { if (prec < opPrec || leftAssoc && prec == opPrec) { opStack = opStack.tail recur { - atSpan(opInfo.operator.span union opInfo.operand.span union top.span) { + atSpan(opInfo.operator.span union opInfo.operand.span union top.span): + def deprecateInfixNamedArg(t: Tree): Unit = t match + case Tuple(ts) => ts.foreach(deprecateInfixNamedArg) + case Parens(t) => deprecateInfixNamedArg(t) + case t: Assign => report.deprecationWarning(em"named argument is deprecated for infix syntax", t.srcPos) + case _ => + deprecateInfixNamedArg(top) InfixOp(opInfo.operand, opInfo.operator, top) - } } } else top diff --git a/tests/warn/infix-named-args.scala b/tests/warn/infix-named-args.scala new file mode 100644 index 000000000000..e6953810e567 --- /dev/null +++ b/tests/warn/infix-named-args.scala @@ -0,0 +1,7 @@ +//> using options -deprecation + +class C { + def f = 42 + (x = 1) // warn + def multi(x: Int, y: Int): Int = x + y + def g = new C() `multi` (x = 42, y = 27) // warn // warn +} From 81291f1d6956613fa21f8615eb64893436f2d0df Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 22 Oct 2024 13:04:25 -0700 Subject: [PATCH 2/3] Make message more obvious in the myopic case --- .../dotty/tools/dotc/reporting/ErrorMessageID.scala | 1 + .../src/dotty/tools/dotc/typer/ErrorReporting.scala | 8 +++++++- .../reference/other-new-features/named-tuples.md | 5 +++-- tests/neg/infix-named-args.check | 13 +++++++++++++ tests/neg/infix-named-args.scala | 7 +++++++ tests/warn/infix-named-args.scala | 7 ------- 6 files changed, 31 insertions(+), 10 deletions(-) create mode 100644 tests/neg/infix-named-args.check create mode 100644 tests/neg/infix-named-args.scala delete mode 100644 tests/warn/infix-named-args.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index 6d0a85b3ef0f..fc18f472a864 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -217,6 +217,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case NonNamedArgumentInJavaAnnotationID // errorNumber: 201 case QuotedTypeMissingID // errorNumber: 202 case AmbiguousNamedTupleAssignmentID // errorNumber: 203 + case DeprecatedNamedInfixArgID // errorNumber: 204 - used ONLY in LTS def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 68143dfd2ba0..3bcfba47aac6 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -110,7 +110,13 @@ object ErrorReporting { case tp => i" and expected result type $tp" } i"(${tp.typedArgs().tpes}%, %)$result" - s"arguments ${argStr(tp)}" + def hasNames = tp.args.exists: + case tree: untpd.Tuple => tree.trees.exists: + case NamedArg(_, _) => true + case _ => false + case _ => false + val addendum = if hasNames then " (a named tuple)" else "" + s"arguments ${argStr(tp)}$addendum" case _ => i"expected type $tp" } diff --git a/docs/_docs/reference/other-new-features/named-tuples.md b/docs/_docs/reference/other-new-features/named-tuples.md index df96a91fe182..5483c5cc255b 100644 --- a/docs/_docs/reference/other-new-features/named-tuples.md +++ b/docs/_docs/reference/other-new-features/named-tuples.md @@ -17,8 +17,9 @@ val persons: List[Person] = ... val minors = persons.filter: p => p.age < 18 ``` -Named bindings in tuples are similar to function parameters and arguments. We use `name: Type` for element types and `name = value` for element values. It is illegal to mix named and unnamed elements in a tuple, or to use the same same -name for two different elements. +Named bindings in tuples are similar to function parameters and arguments. +We use `name: Type` for element types and `name = value` for element values. +It is illegal to mix named and unnamed elements in a tuple, or to use the same name for two different elements. Fields of named tuples can be selected by their name, as in the line `p.age < 18` above. diff --git a/tests/neg/infix-named-args.check b/tests/neg/infix-named-args.check new file mode 100644 index 000000000000..df967f083992 --- /dev/null +++ b/tests/neg/infix-named-args.check @@ -0,0 +1,13 @@ +-- [E134] Type Error: tests/neg/infix-named-args.scala:2:13 ------------------------------------------------------------ +2 | def f = 42 + (x = 1) // error // a named tuple! + | ^^^^ + | None of the overloaded alternatives of method + in class Int with types + | (x: Double): Double + | (x: Float): Float + | (x: Long): Long + | (x: Int): Int + | (x: Char): Int + | (x: Short): Int + | (x: Byte): Int + | (x: String): String + | match arguments ((x : Int)) (a named tuple) diff --git a/tests/neg/infix-named-args.scala b/tests/neg/infix-named-args.scala new file mode 100644 index 000000000000..c8ceee547877 --- /dev/null +++ b/tests/neg/infix-named-args.scala @@ -0,0 +1,7 @@ +class C { + def f = 42 + (x = 1) // error // a named tuple! + def multi(x: Int, y: Int): Int = x + y + def **(x: Int, y: Int): Int = x + y + def g = new C() `multi` (x = 42, y = 27) // werror // werror // not actually a tuple! appearances to the contrary + def h = new C() ** (x = 42, y = 27) // werror // werror +} diff --git a/tests/warn/infix-named-args.scala b/tests/warn/infix-named-args.scala deleted file mode 100644 index e6953810e567..000000000000 --- a/tests/warn/infix-named-args.scala +++ /dev/null @@ -1,7 +0,0 @@ -//> using options -deprecation - -class C { - def f = 42 + (x = 1) // warn - def multi(x: Int, y: Int): Int = x + y - def g = new C() `multi` (x = 42, y = 27) // warn // warn -} From 4448c8b09a12c21ccdad8c1e7cdb5e623ed36105 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 22 Oct 2024 13:56:45 -0700 Subject: [PATCH 3/3] Fix named arg deprecation --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../dotty/tools/dotc/reporting/ErrorMessageID.scala | 2 +- compiler/src/dotty/tools/dotc/reporting/messages.scala | 7 +++++++ tests/neg/infix-named-args.check | 8 ++++++++ tests/neg/infix-named-args.scala | 10 ++++++++-- tests/neg/named-tuples.check | 1 + tests/warn/infix-named-args.scala | 7 +++++++ 7 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 tests/warn/infix-named-args.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 818834359f05..84bc4fec169f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1108,7 +1108,7 @@ object Parsers { def deprecateInfixNamedArg(t: Tree): Unit = t match case Tuple(ts) => ts.foreach(deprecateInfixNamedArg) case Parens(t) => deprecateInfixNamedArg(t) - case t: Assign => report.deprecationWarning(em"named argument is deprecated for infix syntax", t.srcPos) + case t: NamedArg => report.deprecationWarning(InfixNamedArgDeprecation(), t.srcPos) case _ => deprecateInfixNamedArg(top) InfixOp(opInfo.operand, opInfo.operator, top) diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index fc18f472a864..c959028a880f 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -217,7 +217,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case NonNamedArgumentInJavaAnnotationID // errorNumber: 201 case QuotedTypeMissingID // errorNumber: 202 case AmbiguousNamedTupleAssignmentID // errorNumber: 203 - case DeprecatedNamedInfixArgID // errorNumber: 204 - used ONLY in LTS + case DeprecatedNamedInfixArgID // errorNumber: 204 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 3b7fba1cb52d..c678dd95beaf 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3352,3 +3352,10 @@ final class AmbiguousNamedTupleAssignment(key: Name, value: untpd.Tree)(using Co |To assign a value, use curly braces: `{${key} = ${value}}`.""" override protected def explain(using Context): String = "" + +class InfixNamedArgDeprecation()(using Context) extends SyntaxMsg(DeprecatedNamedInfixArgID): + def msg(using Context) = "Named argument syntax is deprecated for infix application" + def explain(using Context) = + i"""The argument will be parsed as a named tuple in future. + | + |To avoid this warning, either remove the argument names or use dotted selection.""" diff --git a/tests/neg/infix-named-args.check b/tests/neg/infix-named-args.check index df967f083992..86a98bf9b3d6 100644 --- a/tests/neg/infix-named-args.check +++ b/tests/neg/infix-named-args.check @@ -11,3 +11,11 @@ | (x: Byte): Int | (x: String): String | match arguments ((x : Int)) (a named tuple) +-- [E007] Type Mismatch Error: tests/neg/infix-named-args.scala:13:18 -------------------------------------------------- +13 | def g = this ** 2 // error + | ^ + | Found: (2 : Int) + | Required: X + | + | longer explanation available when compiling with `-explain` +there were 6 deprecation warnings; re-run with -deprecation for details diff --git a/tests/neg/infix-named-args.scala b/tests/neg/infix-named-args.scala index c8ceee547877..2cec30a5d0ff 100644 --- a/tests/neg/infix-named-args.scala +++ b/tests/neg/infix-named-args.scala @@ -1,7 +1,13 @@ -class C { +class C: def f = 42 + (x = 1) // error // a named tuple! def multi(x: Int, y: Int): Int = x + y def **(x: Int, y: Int): Int = x + y def g = new C() `multi` (x = 42, y = 27) // werror // werror // not actually a tuple! appearances to the contrary def h = new C() ** (x = 42, y = 27) // werror // werror -} + +type X = (x: Int) + +class D(d: Int): + def **(x: X): Int = d * x.x + def f = this ** (x = 2) + def g = this ** 2 // error diff --git a/tests/neg/named-tuples.check b/tests/neg/named-tuples.check index 8ec958b6a75d..a66bd3e52039 100644 --- a/tests/neg/named-tuples.check +++ b/tests/neg/named-tuples.check @@ -101,3 +101,4 @@ | Required: (name : ?, age : ?) | | longer explanation available when compiling with `-explain` +there were 2 deprecation warnings; re-run with -deprecation for details diff --git a/tests/warn/infix-named-args.scala b/tests/warn/infix-named-args.scala new file mode 100644 index 000000000000..2e7803cf2720 --- /dev/null +++ b/tests/warn/infix-named-args.scala @@ -0,0 +1,7 @@ +//> using options -deprecation +type X = (x: Int) + +class E(e: Int): + def **(x: Int): Int = e * x + def **(x: X): Int = e * x.x + def f = this ** (x = 2) // warn