From 8cc355c45c90d1670027b7d6be485deeb7f7813f Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 7 Sep 2024 08:20:22 -0700 Subject: [PATCH 1/6] 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 cd727ba8ac72..1e9f83a88efa 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1120,9 +1120,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 988132400f69e1fcef760b18101fa71380f10a89 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 22 Oct 2024 13:04:25 -0700 Subject: [PATCH 2/6] 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 ad3f38d4cab98f6126ff73017e31a7a7bea8c953 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 22 Oct 2024 13:56:45 -0700 Subject: [PATCH 3/6] 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 1e9f83a88efa..87aa27c120c8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1124,7 +1124,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 From f085145e56b77e9bc2fde7dfa4c42a44cadad66a Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 14 Nov 2024 16:47:56 +0100 Subject: [PATCH 4/6] Implement automatic rewrites for named infix arguments interpreted as named tuples --- .../tools/dotc/config/MigrationVersion.scala | 1 + .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../dotty/tools/dotc/parsing/Parsers.scala | 27 ++++++++++----- .../tools/dotc/reporting/ErrorMessageID.scala | 2 +- .../dotty/tools/dotc/reporting/messages.scala | 9 +++-- .../dotty/tools/dotc/CompilationTests.scala | 1 + tests/neg/infix-named-args.check | 34 +++++++++++++++---- tests/neg/infix-named-args.scala | 11 +++--- tests/neg/named-tuples.check | 1 - tests/rewrites/infix-named-args.check | 15 ++++++++ tests/rewrites/infix-named-args.scala | 15 ++++++++ tests/warn/infix-named-args-migration.scala | 14 ++++++++ tests/warn/infix-named-args.scala | 7 ---- 13 files changed, 106 insertions(+), 32 deletions(-) create mode 100644 tests/rewrites/infix-named-args.check create mode 100644 tests/rewrites/infix-named-args.scala create mode 100644 tests/warn/infix-named-args-migration.scala delete mode 100644 tests/warn/infix-named-args.scala diff --git a/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala b/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala index 3da716abbc40..247e3f62a98d 100644 --- a/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala +++ b/compiler/src/dotty/tools/dotc/config/MigrationVersion.scala @@ -26,6 +26,7 @@ enum MigrationVersion(val warnFrom: SourceVersion, val errorFrom: SourceVersion) case WithOperator extends MigrationVersion(`3.4`, future) case FunctionUnderscore extends MigrationVersion(`3.4`, future) case NonNamedArgumentInJavaAnnotation extends MigrationVersion(`3.6`, `3.6`) + case AmbiguousNamedTupleInfixApply extends MigrationVersion(`3.6`, never) case ImportWildcard extends MigrationVersion(future, future) case ImportRename extends MigrationVersion(future, future) case ParameterEnclosedByParenthesis extends MigrationVersion(future, future) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index d3e198a7e7a7..56d71c7fb57e 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -668,6 +668,7 @@ object StdNames { val readResolve: N = "readResolve" val zero: N = "zero" val zip: N = "zip" + val `++` : N = "++" val nothingRuntimeClass: N = "scala.runtime.Nothing$" val nullRuntimeClass: N = "scala.runtime.Null$" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 87aa27c120c8..5541663c1722 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1120,14 +1120,9 @@ object Parsers { if (prec < opPrec || leftAssoc && prec == opPrec) { opStack = opStack.tail recur { - 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: NamedArg => report.deprecationWarning(InfixNamedArgDeprecation(), t.srcPos) - case _ => - deprecateInfixNamedArg(top) - InfixOp(opInfo.operand, opInfo.operator, top) + migrateInfixOp(opInfo, isType): + atSpan(opInfo.operator.span union opInfo.operand.span union top.span): + InfixOp(opInfo.operand, opInfo.operator, top) } } else top @@ -1135,6 +1130,22 @@ object Parsers { recur(top) } + private def migrateInfixOp(opInfo: OpInfo, isType: Boolean)(infixOp: InfixOp): Tree = { + def isNamedTupleOperator = opInfo.operator.name match + case nme.EQ | nme.NE | nme.eq | nme.ne | nme.`++` | nme.zip => true + case _ => false + if isType then infixOp + else infixOp.right match + case Tuple(args) if args.exists(_.isInstanceOf[NamedArg]) && !isNamedTupleOperator => + report.errorOrMigrationWarning(AmbiguousNamedTupleInfixApply(), infixOp.right.srcPos, MigrationVersion.AmbiguousNamedTupleInfixApply) + if MigrationVersion.AmbiguousNamedTupleInfixApply.needsPatch then + val asApply = cpy.Apply(infixOp)(Select(opInfo.operand, opInfo.operator.name), args) + patch(source, infixOp.span, asApply.show) + asApply // allow to use pre-3.6 syntax in migration mode + else infixOp + case _ => infixOp + } + /** True if we are seeing a lambda argument after a colon of the form: * : (params) => * body diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index c959028a880f..35c170858bbf 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 + case AmbiguousNamedTupleInfixApplyID // 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 c678dd95beaf..328ec122f848 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3353,9 +3353,12 @@ final class AmbiguousNamedTupleAssignment(key: Name, value: untpd.Tree)(using Co 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" +class AmbiguousNamedTupleInfixApply()(using Context) extends SyntaxMsg(AmbiguousNamedTupleInfixApplyID): + def msg(using Context) = + "Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list." + + Message.rewriteNotice("This", version = SourceVersion.`3.6-migration`) + def explain(using Context) = - i"""The argument will be parsed as a named tuple in future. + i"""Starting with Scala 3.6 infix named arguments are interpretted as Named Tuple. | |To avoid this warning, either remove the argument names or use dotted selection.""" diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index de00faa86406..3bd3b5138fad 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -79,6 +79,7 @@ class CompilationTests { compileFile("tests/rewrites/i20002.scala", defaultOptions.and("-indent", "-rewrite")), compileDir("tests/rewrites/annotation-named-pararamters", defaultOptions.and("-rewrite", "-source:3.6-migration")), compileFile("tests/rewrites/i21418.scala", unindentOptions.and("-rewrite", "-source:3.5-migration")), + compileFile("tests/rewrites/infix-named-args.scala", defaultOptions.and("-rewrite", "-source:3.6-migration")), ).checkRewrites() } diff --git a/tests/neg/infix-named-args.check b/tests/neg/infix-named-args.check index 86a98bf9b3d6..0cfbbaef73a3 100644 --- a/tests/neg/infix-named-args.check +++ b/tests/neg/infix-named-args.check @@ -1,5 +1,5 @@ -- [E134] Type Error: tests/neg/infix-named-args.scala:2:13 ------------------------------------------------------------ -2 | def f = 42 + (x = 1) // error // a named tuple! +2 | def f = 42 + (x = 1) // error // werror | ^^^^ | None of the overloaded alternatives of method + in class Int with types | (x: Double): Double @@ -11,11 +11,31 @@ | (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 +-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:2:15 -------------------------------------------------------- +2 | def f = 42 + (x = 1) // error // werror + | ^^^^^^^ + |Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list. + |This can be rewritten automatically under -rewrite -source 3.6-migration. + | + | longer explanation available when compiling with `-explain` +-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:5:26 -------------------------------------------------------- +5 | def g = new C() `multi` (x = 42, y = 27) // werror + | ^^^^^^^^^^^^^^^^ + |Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list. + |This can be rewritten automatically under -rewrite -source 3.6-migration. + | + | longer explanation available when compiling with `-explain` +-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:6:21 -------------------------------------------------------- +6 | def h = new C() ** (x = 42, y = 27) // werror + | ^^^^^^^^^^^^^^^^ + |Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list. + |This can be rewritten automatically under -rewrite -source 3.6-migration. + | + | longer explanation available when compiling with `-explain` +-- [E204] Syntax Warning: tests/neg/infix-named-args.scala:13:18 ------------------------------------------------------- +13 | def f = this ** (x = 2) // werror + | ^^^^^^^ + |Ambigious syntax: this infix call argument list is interpreted as single named tuple argument, not as an named arguments list. + |This can be rewritten automatically under -rewrite -source 3.6-migration. | | 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 2cec30a5d0ff..d8616899540c 100644 --- a/tests/neg/infix-named-args.scala +++ b/tests/neg/infix-named-args.scala @@ -1,13 +1,14 @@ class C: - def f = 42 + (x = 1) // error // a named tuple! + def f = 42 + (x = 1) // error // werror 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 + def g = new C() `multi` (x = 42, y = 27) // werror + def h = new C() ** (x = 42, y = 27) // werror type X = (x: Int) class D(d: Int): + def **(x: Int): Int = d * x def **(x: X): Int = d * x.x - def f = this ** (x = 2) - def g = this ** 2 // error + def f = this ** (x = 2) // werror + def g = this ** 2 diff --git a/tests/neg/named-tuples.check b/tests/neg/named-tuples.check index a66bd3e52039..8ec958b6a75d 100644 --- a/tests/neg/named-tuples.check +++ b/tests/neg/named-tuples.check @@ -101,4 +101,3 @@ | 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/rewrites/infix-named-args.check b/tests/rewrites/infix-named-args.check new file mode 100644 index 000000000000..a50593ef18a8 --- /dev/null +++ b/tests/rewrites/infix-named-args.check @@ -0,0 +1,15 @@ +class C: + 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) + def h = new C().**(x = 42, y = 27) + +type X = (x: Int) + +class D(d: Int): + def **(x: Int): Int = d * x + def **(x: X): Int = d * x.x + def f = this.**(x = 2) + def g = this ** 2 + def h = this ** ((x = 2)) + def i = this.**(x = (1 + 1)) \ No newline at end of file diff --git a/tests/rewrites/infix-named-args.scala b/tests/rewrites/infix-named-args.scala new file mode 100644 index 000000000000..bcdf4a21a9d2 --- /dev/null +++ b/tests/rewrites/infix-named-args.scala @@ -0,0 +1,15 @@ +class C: + 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) + def h = new C() ** (x = 42, y = 27) + +type X = (x: Int) + +class D(d: Int): + def **(x: Int): Int = d * x + def **(x: X): Int = d * x.x + def f = this ** (x = 2) + def g = this ** 2 + def h = this ** ((x = 2)) + def i = this ** (x = (1 + 1)) diff --git a/tests/warn/infix-named-args-migration.scala b/tests/warn/infix-named-args-migration.scala new file mode 100644 index 000000000000..df4bfb50271c --- /dev/null +++ b/tests/warn/infix-named-args-migration.scala @@ -0,0 +1,14 @@ +//> using options -source:3.6-migration +class C: + def f = 42 + (x = 1) // warn // interpreted as 42.+(x = 1) under migration, x is a valid synthetic parameter name + 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) // warn + def h = new C() ** (x = 42, y = 27) // warn + +type X = (x: Int) + +class D(d: Int): + def **(x: Int): Int = d * x + def f = this ** (x = 2) // warn + def g = this ** 2 diff --git a/tests/warn/infix-named-args.scala b/tests/warn/infix-named-args.scala deleted file mode 100644 index 2e7803cf2720..000000000000 --- a/tests/warn/infix-named-args.scala +++ /dev/null @@ -1,7 +0,0 @@ -//> 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 From e99aeeed1a8cc68148812c1b445939d59323b91e Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Thu, 14 Nov 2024 17:30:19 +0100 Subject: [PATCH 5/6] Ensure to use no colors printer when preparing rewrites --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 5541663c1722..11017848c8fe 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1140,7 +1140,7 @@ object Parsers { report.errorOrMigrationWarning(AmbiguousNamedTupleInfixApply(), infixOp.right.srcPos, MigrationVersion.AmbiguousNamedTupleInfixApply) if MigrationVersion.AmbiguousNamedTupleInfixApply.needsPatch then val asApply = cpy.Apply(infixOp)(Select(opInfo.operand, opInfo.operator.name), args) - patch(source, infixOp.span, asApply.show) + patch(source, infixOp.span, asApply.show(using ctx.withoutColors)) asApply // allow to use pre-3.6 syntax in migration mode else infixOp case _ => infixOp From 65ef9605f88c908f3bce61d1de3673ac5ebcb38b Mon Sep 17 00:00:00 2001 From: Wojciech Mazur Date: Fri, 15 Nov 2024 14:57:00 +0100 Subject: [PATCH 6/6] Apply review suggstion Co-authored-by: Matt Bovel --- compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala index 3bcfba47aac6..13e75be75838 100644 --- a/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala +++ b/compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala @@ -111,9 +111,7 @@ object ErrorReporting { } i"(${tp.typedArgs().tpes}%, %)$result" def hasNames = tp.args.exists: - case tree: untpd.Tuple => tree.trees.exists: - case NamedArg(_, _) => true - case _ => false + case tree: untpd.Tuple => tree.trees.exists(_.isInstanceOf[NamedArg]) case _ => false val addendum = if hasNames then " (a named tuple)" else "" s"arguments ${argStr(tp)}$addendum"