From be1097ef0555380af206f14b81b8baf9bab6caf6 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Thu, 12 Sep 2024 19:29:51 -0700 Subject: [PATCH] Named arg may be deprecatedName --- .../dotty/tools/dotc/typer/Applications.scala | 46 ++++++++++++++----- tests/neg/i18122.check | 12 ++--- tests/warn/i19077.check | 24 ++++++++++ tests/warn/i19077.scala | 11 +++++ 4 files changed, 76 insertions(+), 17 deletions(-) create mode 100644 tests/warn/i19077.check create mode 100644 tests/warn/i19077.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 9145552f54ab..331813427c83 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -604,6 +604,21 @@ trait Applications extends Compatibility { inline def tailOf[A](list: List[A]): List[A] = if list.isEmpty then list else list.tail // list.drop(1) + def hasDeprecatedName(pname: Name, other: Name, t: Trees.Tree[T]): Boolean = !ctx.isAfterTyper && + methRef.symbol.paramSymss.flatten.find(_.name == pname).flatMap(_.getAnnotation(defn.DeprecatedNameAnnot)).match + case Some(annot) => + val name = annot.argumentConstantString(0) + val version = annot.argumentConstantString(1).filter(!_.isEmpty) + val since = version.map(v => s" (since $v)").getOrElse("") + name.map(_.toTermName) match + case Some(`other`) => + report.deprecationWarning(em"the parameter name $other is deprecated$since: use $pname instead", t.srcPos) + case Some(`pname`) | None => + report.deprecationWarning(em"naming parameter $pname is deprecated$since", t.srcPos) + case _ => + true + case _ => false + /** Reorder the suffix of named args per a list of required names. * * @param pnames The list of parameter names that are missing arguments @@ -619,17 +634,24 @@ trait Applications extends Compatibility { */ def handleNamed(pnames: List[Name], args: List[Trees.Tree[T]], nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name], - missingArgs: Boolean): List[Trees.Tree[T]] = pnames match - case pname :: pnames if nameToArg.contains(pname) => - // there is a named argument for this parameter; pick it - nameToArg(pname) :: handleNamed(pnames, args, nameToArg - pname, toDrop + pname, missingArgs) + missingArgs: Boolean): List[Trees.Tree[T]] = + pnames match + case pname :: pnames if nameToArg.contains(pname) => // use the named argument for this parameter + val arg = nameToArg(pname) + hasDeprecatedName(pname, nme.NO_NAME, arg) + arg :: handleNamed(pnames, args, nameToArg - pname, toDrop + pname, missingArgs) case _ => args match - case (arg @ NamedArg(aname, _)) :: args1 => - if toDrop.contains(aname) // named argument is already passed - then handleNamed(pnames, args1, nameToArg, toDrop - aname, missingArgs) - else if nameToArg.contains(aname) && pnames.nonEmpty // argument is missing, pass an empty tree - then genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop, missingArgs = true) + case allArgs @ (arg @ NamedArg(aname, _)) :: args => + if toDrop.contains(aname) then // named argument was already picked + handleNamed(pnames, args, nameToArg, toDrop - aname, missingArgs) + else if pnames.nonEmpty && nameToArg.contains(aname) then + val pname = pnames.head + if hasDeprecatedName(pname, aname, arg) then // name was deprecated alt, so try again with canonical name + val parg = cpy.NamedArg(arg)(pname, arg.arg).asInstanceOf[Trees.NamedArg[T]] + handleNamed(pnames, parg :: args, nameToArg.removed(aname).updated(pname, parg), toDrop, missingArgs) + else // argument for pname is missing, pass an empty tree + genericEmptyTree :: handleNamed(pnames.tail, allArgs, nameToArg, toDrop, missingArgs = true) else // name not (or no longer) available for named arg def msg = if methodType.paramNames.contains(aname) then @@ -637,7 +659,7 @@ trait Applications extends Compatibility { else em"$methString does not have a parameter $aname" fail(msg, arg.asInstanceOf[Arg]) - arg :: handleNamed(tailOf(pnames), args1, nameToArg, toDrop, missingArgs) + arg :: handleNamed(tailOf(pnames), args, nameToArg, toDrop, missingArgs) case arg :: args => if toDrop.nonEmpty || missingArgs then report.error(i"positional after named argument", arg.srcPos) @@ -649,8 +671,10 @@ trait Applications extends Compatibility { /** Skip prefix of positional args, then handleNamed */ def handlePositional(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] = args match + case (arg @ NamedArg(name, _)) :: args if !pnames.isEmpty && pnames.head == name => + hasDeprecatedName(name, nme.NO_NAME, arg) + arg :: handlePositional(pnames.tail, args) case (_: NamedArg) :: _ => - //val nameAssocs = for case arg @ NamedArg(name, _) <- args yield (name, arg) val nameAssocs = args.collect { case arg @ NamedArg(name, _) => name -> arg } handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set.empty, missingArgs = false) case arg :: args => diff --git a/tests/neg/i18122.check b/tests/neg/i18122.check index 0d08dc33c52a..22d9f2427df0 100644 --- a/tests/neg/i18122.check +++ b/tests/neg/i18122.check @@ -30,18 +30,18 @@ 23 | bar1(ys = 1) // error: missing arg | ^^^^^^^^^^^^ | missing argument for parameter x of method bar1 in object Test: (x: Int, ys: Int*): Unit --- Error: tests/neg/i18122.scala:43:16 --------------------------------------------------------------------------------- +-- Error: tests/neg/i18122.scala:43:22 --------------------------------------------------------------------------------- 43 | bar1(x = 1, 2, ys = 3) // error: positional after named - | ^ - | positional after named argument + | ^^^^^^ + | parameter ys of method bar1 in object Test: (x: Int, ys: Int*): Unit is already instantiated -- Error: tests/neg/i18122.scala:44:18 --------------------------------------------------------------------------------- 44 | bar1(1, 2, ys = 3) // error: parameter ys is already instantiated | ^^^^^^ | parameter ys of method bar1 in object Test: (x: Int, ys: Int*): Unit is already instantiated --- Error: tests/neg/i18122.scala:45:16 --------------------------------------------------------------------------------- +-- Error: tests/neg/i18122.scala:45:22 --------------------------------------------------------------------------------- 45 | bar2(x = 1, 2, ys = 3) // error: positional after named - | ^ - | positional after named argument + | ^^^^^^ + | parameter ys of method bar2 in object Test: (x: Int, ys: Int*): Unit is already instantiated -- Error: tests/neg/i18122.scala:46:17 --------------------------------------------------------------------------------- 46 | bar1(ys = 1, 2, x = 3) // error: positional after named | ^ diff --git a/tests/warn/i19077.check b/tests/warn/i19077.check new file mode 100644 index 000000000000..9a97b9826674 --- /dev/null +++ b/tests/warn/i19077.check @@ -0,0 +1,24 @@ +-- Deprecation Warning: tests/warn/i19077.scala:8:6 -------------------------------------------------------------------- +8 | f(x = 1, 2, 3) // warn + | ^^^^^ + | naming parameter x is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:9:9 -------------------------------------------------------------------- +9 | f(1, y = 2, 3) // warn + | ^^^^^ + | naming parameter y is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:10:12 ------------------------------------------------------------------ +10 | f(1, 2, w = 3) // warn + | ^^^^^ + | the parameter name w is deprecated: use z instead +-- Deprecation Warning: tests/warn/i19077.scala:11:13 ------------------------------------------------------------------ +11 | f(w = 3, x = 1, y = 2) // warn // warn // warn + | ^^^^^ + | naming parameter x is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:11:20 ------------------------------------------------------------------ +11 | f(w = 3, x = 1, y = 2) // warn // warn // warn + | ^^^^^ + | naming parameter y is deprecated +-- Deprecation Warning: tests/warn/i19077.scala:11:6 ------------------------------------------------------------------- +11 | f(w = 3, x = 1, y = 2) // warn // warn // warn + | ^^^^^ + | the parameter name w is deprecated: use z instead diff --git a/tests/warn/i19077.scala b/tests/warn/i19077.scala new file mode 100644 index 000000000000..91842f97a724 --- /dev/null +++ b/tests/warn/i19077.scala @@ -0,0 +1,11 @@ +//> using options -deprecation + +def f(@deprecatedName("x") x: Int, @deprecatedName y: Int, @deprecatedName("w") z: Int) = x+y+z + +@main def Test = + f(1, 2, 3) // nowarn + f(1, 2, z = 3) // nowarn + f(x = 1, 2, 3) // warn + f(1, y = 2, 3) // warn + f(1, 2, w = 3) // warn + f(w = 3, x = 1, y = 2) // warn // warn // warn