@@ -616,6 +616,8 @@ trait Applications extends Compatibility {
616
616
def infoStr = if methType.isErroneous then " " else i " : $methType"
617
617
i " ${err.refStr(methRef)}$infoStr"
618
618
619
+ private type TreeList [T <: Untyped ] = List [Trees .Tree [T ]]
620
+
619
621
/** Re-order arguments to correctly align named arguments
620
622
* Issue errors in the following situations:
621
623
*
@@ -627,28 +629,43 @@ trait Applications extends Compatibility {
627
629
* (either named or positional), or
628
630
* - The formal parameter at the argument position is also mentioned
629
631
* 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 .
631
633
* - "does not have parameter" if a named parameter does not mention a formal
632
634
* parameter name.
633
635
*/
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 )
652
669
653
670
/** Reorder the suffix of named args per a list of required names.
654
671
*
@@ -662,55 +679,61 @@ trait Applications extends Compatibility {
662
679
*
663
680
* 1. `(args diff toDrop)` can be reordered to match `pnames`
664
681
* 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.
665
686
*/
666
- def handleNamed (pnames : List [Name ], args : List [ Trees . Tree [ T ] ],
687
+ def handleNamed (pnames : List [Name ], args : TreeList [ T ],
667
688
nameToArg : Map [Name , Trees .NamedArg [T ]], toDrop : Set [Name ],
668
- missingArgs : Boolean ): List [ Trees . Tree [ T ] ] =
689
+ missingArgs : Boolean ): TreeList [ T ] =
669
690
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 )
673
694
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)
674
700
case _ =>
675
701
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)
711
717
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
714
737
715
738
handlePositional(methodType.paramNames, args)
716
739
end reorder
0 commit comments