Skip to content

Commit

Permalink
Named arg may be deprecatedName
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Sep 14, 2024
1 parent a40a52e commit be1097e
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 17 deletions.
46 changes: 35 additions & 11 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -619,25 +634,32 @@ 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
em"parameter $aname of $methString is already instantiated"
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)
Expand All @@ -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 =>
Expand Down
12 changes: 6 additions & 6 deletions tests/neg/i18122.check
Original file line number Diff line number Diff line change
Expand Up @@ -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
| ^
Expand Down
24 changes: 24 additions & 0 deletions tests/warn/i19077.check
Original file line number Diff line number Diff line change
@@ -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
11 changes: 11 additions & 0 deletions tests/warn/i19077.scala
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit be1097e

Please sign in to comment.