@@ -616,6 +616,8 @@ trait Applications extends Compatibility {
616616 def infoStr = if methType.isErroneous then " " else i " : $methType"
617617 i " ${err.refStr(methRef)}$infoStr"
618618
619+ private type TreeList [T <: Untyped ] = List [Trees .Tree [T ]]
620+
619621 /** Re-order arguments to correctly align named arguments
620622 * Issue errors in the following situations:
621623 *
@@ -627,28 +629,43 @@ trait Applications extends Compatibility {
627629 * (either named or positional), or
628630 * - The formal parameter at the argument position is also mentioned
629631 * in a subsequent named parameter.
630- * - "parameter already instantiated" if a two named arguments have the same name.
632+ * - "parameter already instantiated" if two named arguments have the same name or deprecated alias .
631633 * - "does not have parameter" if a named parameter does not mention a formal
632634 * parameter name.
633635 */
634- def reorder [T <: Untyped ](args : List [Trees .Tree [T ]]): List [Trees .Tree [T ]] =
635-
636- inline def tailOf [A ](list : List [A ]): List [A ] = if list.isEmpty then list else list.tail // list.drop(1)
637-
638- def hasDeprecatedName (pname : Name , other : Name , t : Trees .Tree [T ]): Boolean = ! ctx.isAfterTyper &&
639- methRef.symbol.paramSymss.flatten.find(_.name == pname).flatMap(_.getAnnotation(defn.DeprecatedNameAnnot )).match
640- case Some (annot) =>
641- val name = annot.argumentConstantString(0 )
642- val version = annot.argumentConstantString(1 ).filter(! _.isEmpty)
643- val since = version.map(v => s " (since $v) " ).getOrElse(" " )
644- name.map(_.toTermName) match
645- case Some (`other`) =>
646- report.deprecationWarning(em " the parameter name $other is deprecated $since: use $pname instead " , t.srcPos)
647- case Some (`pname`) | None =>
648- report.deprecationWarning(em " naming parameter $pname is deprecated $since" , t.srcPos)
649- case _ =>
650- true
651- case _ => false
636+ def reorder [T <: Untyped ](args : TreeList [T ]): TreeList [T ] =
637+
638+ extension [A ](list : List [A ]) inline def dropOne = if list.isEmpty then list else list.tail // aka list.drop(1)
639+
640+ extension (dna : Annotation )
641+ def deprecatedName : Name =
642+ dna.argumentConstantString(0 ).map(_.toTermName).getOrElse(nme.NO_NAME )
643+ def since : String =
644+ val version = dna.argumentConstantString(1 ).filter(! _.isEmpty)
645+ version.map(v => s " (since $v) " ).getOrElse(" " )
646+
647+ val deprecatedNames : Map [Name , Annotation ] =
648+ methRef.symbol.paramSymss.find(_.exists(_.isTerm)) match
649+ case Some (ps) if ps.exists(_.hasAnnotation(defn.DeprecatedNameAnnot )) =>
650+ ps.flatMap: p =>
651+ p.getAnnotation(defn.DeprecatedNameAnnot ).map(p.name -> _)
652+ .toMap
653+ case _ => Map .empty
654+
655+ extension (name : Name )
656+ def isMatchedBy (usage : Name ): Boolean =
657+ name == usage
658+ || deprecatedNames.get(name).exists(_.deprecatedName == usage)
659+ def checkDeprecationOf (usage : Name , pos : SrcPos ): Unit = if ! ctx.isAfterTyper then
660+ for dna <- deprecatedNames.get(name) do
661+ dna.deprecatedName match
662+ case `name` | nme.NO_NAME if name == usage =>
663+ report.deprecationWarning(em " naming parameter $usage is deprecated ${dna.since}" , pos)
664+ case `usage` =>
665+ report.deprecationWarning(em " the parameter name $usage is deprecated ${dna.since}: use $name instead " , pos)
666+ case _ =>
667+ def alternative : Name =
668+ deprecatedNames.get(name).map(_.deprecatedName).getOrElse(nme.NO_NAME )
652669
653670 /** Reorder the suffix of named args per a list of required names.
654671 *
@@ -662,55 +679,61 @@ trait Applications extends Compatibility {
662679 *
663680 * 1. `(args diff toDrop)` can be reordered to match `pnames`
664681 * 2. For every `(name -> arg)` in `nameToArg`, `arg` is an element of `args`
682+ *
683+ * Recurse over the parameter names looking for named args to use.
684+ * If there are no more parameters or no args fit, process the next arg:
685+ * a named arg may be previously used, or not yet used, or badly named.
665686 */
666- def handleNamed (pnames : List [Name ], args : List [ Trees . Tree [ T ] ],
687+ def handleNamed (pnames : List [Name ], args : TreeList [ T ],
667688 nameToArg : Map [Name , Trees .NamedArg [T ]], toDrop : Set [Name ],
668- missingArgs : Boolean ): List [ Trees . Tree [ T ] ] =
689+ missingArgs : Boolean ): TreeList [ T ] =
669690 pnames match
670- case pname :: pnames if nameToArg.contains(pname) => // use the named argument for this parameter
671- val arg = nameToArg(pname)
672- hasDeprecatedName (pname, nme. NO_NAME , arg)
691+ case pname :: pnames if nameToArg.contains(pname) =>
692+ val arg = nameToArg(pname) // use the named argument for this parameter
693+ pname.checkDeprecationOf (pname, arg.srcPos )
673694 arg :: handleNamed(pnames, args, nameToArg - pname, toDrop + pname, missingArgs)
695+ case pname :: pnames if nameToArg.contains(pname.alternative) =>
696+ val alt = pname.alternative
697+ val arg = nameToArg(alt) // use the named argument for this parameter
698+ pname.checkDeprecationOf(alt, arg.srcPos)
699+ arg :: handleNamed(pnames, args, nameToArg - alt, toDrop + alt, missingArgs)
674700 case _ =>
675701 args match
676- case allArgs @ (arg @ NamedArg (aname, _)) :: args =>
677- if toDrop.contains(aname) then // named argument was already picked
678- handleNamed(pnames, args, nameToArg, toDrop - aname, missingArgs)
679- else if pnames.nonEmpty && nameToArg.contains(aname) then
680- val pname = pnames.head
681- if hasDeprecatedName(pname, aname, arg) then // name was deprecated alt, so try again with canonical name
682- val parg = cpy.NamedArg (arg)(pname, arg.arg).asInstanceOf [Trees .NamedArg [T ]]
683- handleNamed(pnames, parg :: args, nameToArg.removed(aname).updated(pname, parg), toDrop, missingArgs)
684- else // argument for pname is missing, pass an empty tree
685- genericEmptyTree :: handleNamed(pnames.tail, allArgs, nameToArg, toDrop, missingArgs = true )
686- else // name not (or no longer) available for named arg
687- def msg =
688- if methodType.paramNames.contains(aname) then
689- em " parameter $aname of $methString is already instantiated "
690- else
691- em " $methString does not have a parameter $aname"
692- fail(msg, arg.asInstanceOf [Arg ])
693- arg :: handleNamed(tailOf(pnames), args, nameToArg, toDrop, missingArgs)
694- case arg :: args =>
695- if toDrop.nonEmpty || missingArgs then
696- report.error(i " positional after named argument " , arg.srcPos)
697- arg :: handleNamed(tailOf(pnames), args, 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 then nil
700- else handleNamed(tailOf(pnames), nil, nameToArg, toDrop, missingArgs)
701-
702- /** Skip prefix of positional args, then handleNamed */
703- def handlePositional (pnames : List [Name ], args : List [Trees .Tree [T ]]): List [Trees .Tree [T ]] =
704- args match
705- case (arg @ NamedArg (name, _)) :: args if ! pnames.isEmpty && pnames.head == name =>
706- hasDeprecatedName(name, nme.NO_NAME , arg)
707- arg :: handlePositional(pnames.tail, args)
708- case (_ : NamedArg ) :: _ =>
709- val nameAssocs = args.collect { case arg @ NamedArg (name, _) => name -> arg }
710- handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set .empty, missingArgs = false )
702+ case allArgs @ (arg @ NamedArg (aname, _)) :: args =>
703+ if toDrop.contains(aname) then
704+ // named argument was already picked (using aname), skip it
705+ handleNamed(pnames, args, nameToArg, toDrop - aname, missingArgs)
706+ else if pnames.nonEmpty && nameToArg.contains(aname) then
707+ // argument for pname is missing, pass an empty tree; arg may be used later, so keep it
708+ genericEmptyTree :: handleNamed(pnames.tail, allArgs, nameToArg, toDrop, missingArgs = true )
709+ else // name not (or no longer) available for named arg
710+ def msg =
711+ if methodType.paramNames.exists(nm => nm == aname || nm.alternative == aname) then
712+ em " parameter $aname of $methString is already instantiated "
713+ else
714+ em " $methString does not have a parameter $aname"
715+ fail(msg, arg.asInstanceOf [Arg ])
716+ arg :: handleNamed(pnames.dropOne, args, nameToArg, toDrop, missingArgs)
711717 case arg :: args =>
712- arg :: handlePositional(tailOf(pnames), args)
713- case nil => nil
718+ if toDrop.nonEmpty || missingArgs then
719+ report.error(i " positional after named argument " , arg.srcPos)
720+ arg :: handleNamed(pnames.dropOne, args, nameToArg, toDrop, missingArgs) // unnamed argument; pick it
721+ case nil => // no more args, continue to pick up any preceding named args
722+ if pnames.isEmpty then nil
723+ else handleNamed(pnames.dropOne, args = nil, nameToArg, toDrop, missingArgs)
724+
725+ // Skip prefix of positional args, then handleNamed
726+ def handlePositional (pnames : List [Name ], args : TreeList [T ]): TreeList [T ] =
727+ args match
728+ case (arg @ NamedArg (name, _)) :: args if ! pnames.isEmpty && pnames.head.isMatchedBy(name) =>
729+ pnames.head.checkDeprecationOf(name, arg.srcPos)
730+ arg :: handlePositional(pnames.tail, args)
731+ case (_ : NamedArg ) :: _ =>
732+ val nameAssocs = args.collect { case arg @ NamedArg (name, _) => name -> arg }
733+ handleNamed(pnames, args, nameAssocs.toMap, toDrop = Set .empty, missingArgs = false )
734+ case arg :: args =>
735+ arg :: handlePositional(pnames.dropOne, args)
736+ case nil => nil
714737
715738 handlePositional(methodType.paramNames, args)
716739 end reorder
0 commit comments