Skip to content

Commit 6a35378

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

File tree

5 files changed

+108
-43
lines changed

5 files changed

+108
-43
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

+83-42
Original file line numberDiff line numberDiff line change
@@ -529,50 +529,91 @@ 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+
val sinceMsg = annot.argumentConstantString(1).map(version => s" (since: $version)").getOrElse("")
543+
report.deprecationWarning(em"naming parameter $name is deprecated$sinceMsg", arg.srcPos)
544+
Some((name, arg))
545+
// fallback to the primary name
546+
case None => nameToArg.get(pname._1).map((pname._1, _))
547+
// no-deprecated name, we can look for the argument with the main name
548+
case None => nameToArg.get(pname._1).map((pname._1, _))
549+
end findNameArgument
550+
551+
def namesRest = if names.isEmpty then Nil else names.tail
552+
553+
println(s"$names - $args - $nameToArg - $toDrop - $missingArgs")
554+
555+
names match
556+
case pname :: pnames if findNameArgument(pname).nonEmpty => findNameArgument(pname) match
557+
case Some((name, arg)) => // named argument was found, move on to the next parameter
558+
val namesToDrop = pname._2.map(_._1).toSet + pname._1 // drop both the deprecated and the primary name
559+
arg :: handleNamed(pnames, args, nameToArg - name, toDrop ++ namesToDrop, missingArgs)
560+
case _ => throw new IllegalStateException
561+
case _ => args match
562+
case (arg @ NamedArg(name, _)) :: args1 =>
563+
if toDrop.contains(name) then // argument is already passed
564+
handleNamed(names, args1, nameToArg, toDrop - name, missingArgs)
565+
else if (nameToArg.contains(name) && names.nonEmpty) // argument is missing, pass an empty tree
566+
genericEmptyTree :: handleNamed(names.tail, args, nameToArg, toDrop, missingArgs = true)
567+
else // name not (or no longer) available for named arg
568+
def msg =
569+
if methodType.paramNames.contains(name) then
570+
em"parameter $name of $methString is already instantiated"
571+
else
572+
em"$methString does not have a parameter $name"
573+
fail(msg, arg.asInstanceOf[Arg])
574+
arg :: handleNamed(namesRest, args1, nameToArg, toDrop, missingArgs)
575+
case arg :: args1 =>
576+
if toDrop.nonEmpty || missingArgs then
577+
report.error(em"positional after named argument", arg.srcPos)
578+
arg :: handleNamed(namesRest, args1, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
579+
case Nil => // no more args, continue to pick up any preceding named args
580+
if names.isEmpty then Nil
581+
else handleNamed(names.tail, args, nameToArg, toDrop, missingArgs)
582+
end handleNamed
583+
584+
def handlePositional(names: List[(Name, Option[(Name, Annotation)])], args: List[Trees.Tree[T]]): List[Trees.Tree[T]] =
585+
args match
586+
// Named argument, switch to handleNamed processing
587+
case (_: NamedArg @unchecked) :: _ =>
588+
val nameAssocs = for case arg @ NamedArg(name, _) <- args yield (name, arg)
589+
handleNamed(names, args, nameAssocs.toMap, toDrop = Set(), missingArgs = false)
590+
// Positional argument, skip it and move to the next one
570591
case arg :: args1 =>
571-
arg :: handlePositional(if (pnames.isEmpty) Nil else pnames.tail, args1)
592+
arg :: handlePositional(if names.isEmpty then Nil else names.tail, args1)
572593
case Nil => Nil
573-
}
574-
575-
handlePositional(methodType.paramNames, args)
594+
end handlePositional
595+
596+
// format: (primary_name, (deprecated_name, annotation): Option)
597+
// NB: deprecated_name might be different from (or the same as) primary_name
598+
// names also has the same order as the parameter list
599+
val names =
600+
for p <- methRef.symbol.paramSymss.flatten
601+
name = p.name
602+
if methodType.paramNames.contains(name)
603+
yield
604+
p.getAnnotation(defn.DeprecatedNameAnnot) match
605+
case Some(annot) =>
606+
// Get the name of the deprecated
607+
val deprecated = annot.argumentConstantString(0).map(_.toTermName).getOrElse(name)
608+
(name, Some(deprecated, annot))
609+
case None =>
610+
(name, None)
611+
end names
612+
613+
println(i"=============${methRef.symbol.id}==============")
614+
val t = handlePositional(names, args)
615+
println("===============================")
616+
t
576617
}
577618

578619
/** 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)