Skip to content

Commit 4115910

Browse files
committed
Add support for @deprecatedName
1 parent 3b83317 commit 4115910

File tree

5 files changed

+120
-42
lines changed

5 files changed

+120
-42
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

+1
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,7 @@ class Definitions {
10111011
@tu lazy val DeprecatedAnnot: ClassSymbol = requiredClass("scala.deprecated")
10121012
@tu lazy val DeprecatedOverridingAnnot: ClassSymbol = requiredClass("scala.deprecatedOverriding")
10131013
@tu lazy val DeprecatedInheritanceAnnot: ClassSymbol = requiredClass("scala.deprecatedInheritance")
1014+
@tu lazy val DeprecatedNameAnnot: ClassSymbol = requiredClass("scala.deprecatedName")
10141015
@tu lazy val ImplicitAmbiguousAnnot: ClassSymbol = requiredClass("scala.annotation.implicitAmbiguous")
10151016
@tu lazy val ImplicitNotFoundAnnot: ClassSymbol = requiredClass("scala.annotation.implicitNotFound")
10161017
@tu lazy val InlineParamAnnot: ClassSymbol = requiredClass("scala.annotation.internal.InlineParam")

compiler/src/dotty/tools/dotc/transform/FirstTransform.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ class FirstTransform extends MiniPhase with InfoTransformer { thisPhase =>
173173

174174
override def transformTypeApply(tree: TypeApply)(using Context): Tree =
175175
constToLiteral(tree)
176-
176+
177177
override def transformApply(tree: Apply)(using Context): Tree =
178178
constToLiteral(foldCondition(tree))
179179

compiler/src/dotty/tools/dotc/typer/Applications.scala

+95-41
Original file line numberDiff line numberDiff line change
@@ -529,50 +529,104 @@ trait Applications extends Compatibility {
529529
* 1. `(args diff toDrop)` can be reordered to match `pnames`
530530
* 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args`
531531
*/
532-
def handleNamed(pnames: List[Name], args: List[Trees.Tree[T]],
533-
nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name],
534-
missingArgs: Boolean): List[Trees.Tree[T]] = pnames match {
535-
case pname :: pnames1 if nameToArg contains pname =>
536-
// there is a named argument for this parameter; pick it
537-
nameToArg(pname) :: handleNamed(pnames1, args, nameToArg - pname, toDrop + pname, missingArgs)
538-
case _ =>
539-
def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail
540-
args match {
541-
case (arg @ NamedArg(aname, _)) :: args1 =>
542-
if (toDrop contains aname) // argument is already passed
543-
handleNamed(pnames, args1, nameToArg, toDrop - aname, missingArgs)
544-
else if ((nameToArg contains aname) && pnames.nonEmpty) // argument is missing, pass an empty tree
545-
genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop, missingArgs = true)
546-
else { // name not (or no longer) available for named arg
547-
def msg =
548-
if (methodType.paramNames contains aname)
549-
em"parameter $aname of $methString is already instantiated"
550-
else
551-
em"$methString does not have a parameter $aname"
552-
fail(msg, arg.asInstanceOf[Arg])
553-
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop, missingArgs)
554-
}
555-
case arg :: args1 =>
556-
if toDrop.nonEmpty || missingArgs then
557-
report.error(i"positional after named argument", arg.srcPos)
558-
arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
559-
case Nil => // no more args, continue to pick up any preceding named args
560-
if (pnames.isEmpty) Nil
561-
else handleNamed(pnamesRest, args, nameToArg, toDrop, missingArgs)
562-
}
563-
}
564-
565-
def handlePositional(pnames: List[Name], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] =
566-
args match {
567-
case (arg: NamedArg @unchecked) :: _ =>
568-
val nameAssocs = for (case arg @ NamedArg(name, _) <- args) yield (name, arg)
569-
handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set(), missingArgs = false)
532+
def handleNamed(names: List[(Name, Option[(Name, Annotation)])], args: List[Trees.Tree[T]],
533+
nameToArg: Map[Name, Trees.NamedArg[T]], toDrop: Set[Name], missingArgs: Boolean): List[Trees.Tree[T]] =
534+
535+
/** Returns the corresponding name parameter with the name that helped find it */
536+
def findNameArgument(pname: (Name, Option[(Name, Annotation)])): Option[(Name, Trees.NamedArg[T])] =
537+
pname._2 match
538+
// This parameter has a deprecated name, looking for this first
539+
case Some(name, annot) => nameToArg.get(name) match
540+
// Argument with a deprecated name was found: emit a warning and change the parameter name
541+
case Some(arg) =>
542+
//println(i"${arg.srcPos}")
543+
val sinceMsg = annot.argumentConstantString(1).map(version => s" (since: $version)").getOrElse("")
544+
report.deprecationWarning(em"naming parameter $name is deprecated$sinceMsg", arg.srcPos)
545+
Some((name, arg))
546+
// fallback to the primary name
547+
case None => nameToArg.get(pname._1).map((pname._1, _))
548+
// no-deprecated name, we can look for the argument with the main name
549+
case None => nameToArg.get(pname._1).map((pname._1, _))
550+
end findNameArgument
551+
552+
names match
553+
case pname :: pnames =>
554+
findNameArgument(pname) match
555+
// named argument was found, move on to the next parameter
556+
case Some((name, arg)) =>
557+
val namesToDrop = pname._2.map(_._1).toSet + pname._1 // drop both the deprecated and the primary name
558+
arg :: handleNamed(pnames, args, nameToArg - name, toDrop ++ namesToDrop, missingArgs)
559+
case None => args match
560+
case (arg @ NamedArg(name, _)) :: args1 =>
561+
if toDrop.contains(name) then
562+
handleNamed(names, args1, nameToArg, toDrop - name, missingArgs)
563+
else if nameToArg.contains(name) && pnames.nonEmpty then // an argument is missing, pass an empty tree
564+
genericEmptyTree :: handleNamed(pnames, args, nameToArg, toDrop, missingArgs = true)
565+
else
566+
def msg =
567+
if methodType.paramNames contains name then
568+
em"parameter $name of $methString is already instantiated"
569+
else
570+
em"$methString does not have a parameter $name"
571+
fail(msg, arg.asInstanceOf[Arg])
572+
arg :: handleNamed(pnames, args1, nameToArg, toDrop, missingArgs)
573+
case arg :: args1 =>
574+
if toDrop.nonEmpty || missingArgs then
575+
report.error(em"positional after named argument", arg.srcPos)
576+
arg :: handleNamed(pnames, args1, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
577+
// no more args, continue to pick up any preceding named args
578+
case Nil =>
579+
if pnames.isEmpty then Nil else handleNamed(pnames, args, nameToArg, toDrop, missingArgs)
580+
case Nil => args match
581+
case (arg @ NamedArg(name, _)) :: args1 =>
582+
if toDrop.contains(name) then
583+
handleNamed(names, args1, nameToArg, toDrop - name, missingArgs)
584+
else
585+
def msg =
586+
if methodType.paramNames contains name then
587+
em"parameter $name of $methString is already instantiated"
588+
else
589+
em"$methString does not have a parameter $name"
590+
fail(msg, arg.asInstanceOf[Arg])
591+
arg :: handleNamed(Nil, args1, nameToArg, toDrop, missingArgs)
592+
case arg :: args1 =>
593+
if toDrop.nonEmpty || missingArgs then
594+
report.error(em"positional after named argument", arg.srcPos)
595+
arg :: handleNamed(Nil, args1, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
596+
case Nil => Nil // processed all the parameters and all the arguments
597+
Nil
598+
end handleNamed
599+
600+
def handlePositional(names: List[(Name, Option[(Name, Annotation)])], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] =
601+
args match
602+
// Named argument, switch to handleNamed processing
603+
case (_: NamedArg @unchecked) :: _ =>
604+
val nameAssocs = for case arg @ NamedArg(name, _) <- args yield (name, arg)
605+
handleNamed(names, args, nameAssocs.toMap, toDrop = Set(), missingArgs = false)
606+
// Positional argument, skip it and move to the next one
570607
case arg :: args1 =>
571-
arg :: handlePositional(if (pnames.isEmpty) Nil else pnames.tail, args1)
608+
arg :: handlePositional(if names.isEmpty then Nil else names.tail, args1)
572609
case Nil => Nil
573-
}
610+
end handlePositional
611+
612+
// format: (primary_name, (deprecated_name, annotation): Option)
613+
// NB: deprecated_name might be different from (or the same as) primary_name
614+
// names also has the same order as the parameter list
615+
val names =
616+
for p <- methRef.symbol.paramSymss.flatten
617+
if methodType.paramNames.contains(p.name)
618+
yield
619+
p.getAnnotation(defn.DeprecatedNameAnnot) match
620+
case Some(annot) =>
621+
// Get the name of the deprecated
622+
val deprecated = annot.argumentConstantString(0).map(_.toTermName).getOrElse(p.name)
623+
(p.name, Some(deprecated, annot))
624+
case None =>
625+
(p.name, None)
626+
end names
627+
628+
handlePositional(names, args)
574629

575-
handlePositional(methodType.paramNames, args)
576630
}
577631

578632
/** Is `sym` a constructor of a Java-defined annotation? */

tests/warn/i19077.check

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
-- Deprecation Warning: tests/warn/i19077.scala:8:6 ----------------------------
2+
8 | f(x = 2) // warn
3+
| ^^^^^
4+
| naming parameter x is deprecated
5+
-- Deprecation Warning: tests/warn/i19077.scala:9:6 ----------------------------
6+
9 | g(x = 2) // warn
7+
| ^^^^^
8+
| naming parameter x is deprecated
9+
-- Deprecation Warning: tests/warn/i19077.scala:10:6 ---------------------------
10+
10 | h(x = 2) // warn
11+
| ^^^^^
12+
| naming parameter x is deprecated

tests/warn/i19077.scala

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//> using options -deprecation
2+
3+
def f(@deprecatedName x: Int) = x * 2
4+
def g(@deprecatedName("x") x: Int) = x * 2
5+
def h(@deprecatedName("x") y: Int) = y * 2
6+
7+
@main def Test =
8+
f(x = 2) // warn
9+
g(x = 2) // warn
10+
h(x = 2) // warn
11+
h(y = 2) // no-warn

0 commit comments

Comments
 (0)