@@ -641,6 +641,8 @@ trait Applications extends Compatibility {
641641 def infoStr = if methType.isErroneous then " " else i " : $methType"
642642 i " ${err.refStr(methRef)}$infoStr"
643643
644+ private type TreeList [T <: Untyped ] = List [Trees .Tree [T ]]
645+
644646 /** Re-order arguments to correctly align named arguments
645647 * Issue errors in the following situations:
646648 *
@@ -652,67 +654,118 @@ trait Applications extends Compatibility {
652654 * (either named or positional), or
653655 * - The formal parameter at the argument position is also mentioned
654656 * in a subsequent named parameter.
655- * - "parameter already instantiated" if a two named arguments have the same name.
657+ * - "parameter already instantiated" if two named arguments have the same name or deprecated alias .
656658 * - "does not have parameter" if a named parameter does not mention a formal
657659 * parameter name.
658660 */
659- def reorder [T <: Untyped ](args : List [Trees .Tree [T ]]): List [Trees .Tree [T ]] = {
661+ def reorder [T <: Untyped ](args : TreeList [T ]): TreeList [T ] =
662+
663+ extension [A ](list : List [A ]) inline def dropOne = if list.isEmpty then list else list.tail // aka list.drop(1)
664+
665+ extension (dna : Annotation )
666+ def deprecatedName : Name =
667+ dna.argumentConstantString(0 ).map(_.toTermName).getOrElse(nme.NO_NAME )
668+ def since : String =
669+ val version = dna.argumentConstantString(1 ).filter(! _.isEmpty)
670+ version.map(v => s " (since $v) " ).getOrElse(" " )
660671
661- /** @param pnames The list of parameter names that are missing arguments
672+ val deprecatedNames : Map [Name , Annotation ] =
673+ val sym = methRef.symbol
674+ val paramss =
675+ if sym.hasAnnotation(defn.MappedAlternativeAnnot ) then sym.rawParamss
676+ else sym.paramSymss
677+ paramss.find(_.exists(_.isTerm)) match
678+ case Some (ps) if ps.exists(_.hasAnnotation(defn.DeprecatedNameAnnot )) =>
679+ ps.flatMap: p =>
680+ p.getAnnotation(defn.DeprecatedNameAnnot ).map(p.name -> _)
681+ .toMap
682+ case _ => Map .empty
683+
684+ extension (name : Name )
685+ def isMatchedBy (usage : Name ): Boolean =
686+ name == usage
687+ || deprecatedNames.get(name).exists(_.deprecatedName == usage)
688+ def checkDeprecationOf (usage : Name , pos : SrcPos ): Unit = if ! ctx.isAfterTyper then
689+ for dna <- deprecatedNames.get(name) do
690+ dna.deprecatedName match
691+ case `name` | nme.NO_NAME if name == usage =>
692+ report.deprecationWarning(em " naming parameter $usage is deprecated ${dna.since}" , pos)
693+ case `usage` =>
694+ report.deprecationWarning(em " the parameter name $usage is deprecated ${dna.since}: use $name instead " , pos)
695+ case _ =>
696+ def alternative : Name =
697+ deprecatedNames.get(name).map(_.deprecatedName).getOrElse(nme.NO_NAME )
698+
699+ /** Reorder the suffix of named args per a list of required names.
700+ *
701+ * @param pnames The list of parameter names that are missing arguments
662702 * @param args The list of arguments that are not yet passed, or that are waiting to be dropped
663703 * @param nameToArg A map from as yet unseen names to named arguments
664- * @param toDrop A set of names that have already be passed as named arguments
704+ * @param toDrop A set of names that have already been passed as named arguments
705+ * @param missingArgs true if args were already missing, so error on positional
665706 *
666707 * For a well-typed application we have the invariants
667708 *
668709 * 1. `(args diff toDrop)` can be reordered to match `pnames`
669710 * 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args`
711+ *
712+ * Recurse over the parameter names looking for named args to use.
713+ * If there are no more parameters or no args fit, process the next arg:
714+ * a named arg may be previously used, or not yet used, or badly named.
670715 */
671- def handleNamed (pnames : List [Name ], args : List [ Trees . Tree [ T ] ],
716+ def handleNamed (pnames : List [Name ], args : TreeList [ T ],
672717 nameToArg : Map [Name , Trees .NamedArg [T ]], toDrop : Set [Name ],
673- missingArgs : Boolean ): List [Trees .Tree [T ]] = pnames match {
674- case pname :: pnames1 if nameToArg contains pname =>
675- // there is a named argument for this parameter; pick it
676- nameToArg(pname) :: handleNamed(pnames1, args, nameToArg - pname, toDrop + pname, missingArgs)
718+ missingArgs : Boolean ): TreeList [T ] =
719+ pnames match
720+ case pname :: pnames if nameToArg.contains(pname) =>
721+ val arg = nameToArg(pname) // use the named argument for this parameter
722+ pname.checkDeprecationOf(pname, arg.srcPos)
723+ arg :: handleNamed(pnames, args, nameToArg - pname, toDrop + pname, missingArgs)
724+ case pname :: pnames if nameToArg.contains(pname.alternative) =>
725+ val alt = pname.alternative
726+ val arg = nameToArg(alt) // use the named argument for this parameter
727+ pname.checkDeprecationOf(alt, arg.srcPos)
728+ arg :: handleNamed(pnames, args, nameToArg - alt, toDrop + alt, missingArgs)
677729 case _ =>
678- def pnamesRest = if (pnames.isEmpty) pnames else pnames.tail
679- args match {
680- case (arg @ NamedArg (aname, _)) :: args1 =>
681- if (toDrop contains aname) // argument is already passed
682- handleNamed(pnames, args1, nameToArg, toDrop - aname, missingArgs)
683- else if ((nameToArg contains aname) && pnames.nonEmpty) // argument is missing, pass an empty tree
684- genericEmptyTree :: handleNamed(pnames.tail, args, nameToArg, toDrop, missingArgs = true )
685- else { // name not (or no longer) available for named arg
686- def msg =
687- if (methodType.paramNames contains aname)
688- em " parameter $aname of $methString is already instantiated "
689- else
690- em " $methString does not have a parameter $aname"
691- fail(msg, arg.asInstanceOf [Arg ])
692- arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop, missingArgs)
693- }
694- case arg :: args1 =>
695- if toDrop.nonEmpty || missingArgs then
696- report.error(i " positional after named argument " , arg.srcPos)
697- arg :: handleNamed(pnamesRest, args1, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
698- case Nil => // no more args, continue to pick up any preceding named args
699- if (pnames.isEmpty) Nil
700- else handleNamed(pnamesRest, args, nameToArg, toDrop, missingArgs)
701- }
702- }
703-
704- def handlePositional (pnames : List [Name ], args : List [Trees .Tree [T ]]): List [Trees .Tree [T ]] =
705- args match {
706- case (arg : NamedArg @ unchecked) :: _ =>
707- val nameAssocs = for (case arg @ NamedArg (name, _) <- args) yield (name, arg)
708- handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set (), missingArgs = false )
709- case arg :: args1 =>
710- arg :: handlePositional(if (pnames.isEmpty) Nil else pnames.tail, args1)
711- case Nil => Nil
712- }
730+ args match
731+ case allArgs @ (arg @ NamedArg (aname, _)) :: args =>
732+ if toDrop.contains(aname) then
733+ // named argument was already picked (using aname), skip it
734+ handleNamed(pnames, args, nameToArg, toDrop - aname, missingArgs)
735+ else if pnames.nonEmpty && nameToArg.contains(aname) then
736+ // argument for pname is missing, pass an empty tree; arg may be used later, so keep it
737+ genericEmptyTree :: handleNamed(pnames.tail, allArgs, nameToArg, toDrop, missingArgs = true )
738+ else // name not (or no longer) available for named arg
739+ def msg =
740+ if methodType.paramNames.exists(nm => nm == aname || nm.alternative == aname) then
741+ em " parameter $aname of $methString is already instantiated "
742+ else
743+ em " $methString does not have a parameter $aname"
744+ fail(msg, arg.asInstanceOf [Arg ])
745+ arg :: handleNamed(pnames.dropOne, args, nameToArg, toDrop, missingArgs)
746+ case arg :: args =>
747+ if toDrop.nonEmpty || missingArgs then
748+ report.error(i " positional after named argument " , arg.srcPos)
749+ arg :: handleNamed(pnames.dropOne, args, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
750+ case nil => // no more args, continue to pick up any preceding named args
751+ if pnames.isEmpty then nil
752+ else handleNamed(pnames.dropOne, args = nil, nameToArg, toDrop, missingArgs)
753+
754+ // Skip prefix of positional args, then handleNamed
755+ def handlePositional (pnames : List [Name ], args : TreeList [T ]): TreeList [T ] =
756+ args match
757+ case (arg @ NamedArg (name, _)) :: args if ! pnames.isEmpty && pnames.head.isMatchedBy(name) =>
758+ pnames.head.checkDeprecationOf(name, arg.srcPos)
759+ arg :: handlePositional(pnames.tail, args)
760+ case (_ : NamedArg ) :: _ =>
761+ val nameAssocs = args.collect { case arg @ NamedArg (name, _) => name -> arg }
762+ handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set .empty, missingArgs = false )
763+ case arg :: args =>
764+ arg :: handlePositional(pnames.dropOne, args)
765+ case nil => nil
713766
714767 handlePositional(methodType.paramNames, args)
715- }
768+ end reorder
716769
717770 /** Is `sym` a constructor of a Java-defined annotation? */
718771 def isJavaAnnotConstr (sym : Symbol ): Boolean =
0 commit comments