@@ -23,6 +23,7 @@ import StdNames._
23
23
import NameKinds .DefaultGetterName
24
24
import ProtoTypes ._
25
25
import Inferencing ._
26
+ import rewrite .Rewrites .patch
26
27
27
28
import collection .mutable
28
29
import config .Printers .{overload , typr , unapp }
@@ -242,7 +243,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
242
243
def success = ok
243
244
244
245
protected def methodType = methType.asInstanceOf [MethodType ]
245
- private def methString : String = i " ${methRef.symbol}: ${methType.show}"
246
+ private def methString : String = i " ${methRef.symbol.showLocated }: ${methType.show}"
246
247
247
248
/** Re-order arguments to correctly align named arguments */
248
249
def reorder [T >: Untyped ](args : List [Trees .Tree [T ]]): List [Trees .Tree [T ]] = {
@@ -669,10 +670,19 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
669
670
* or, if application is an operator assignment, also an `Assign` or
670
671
* Block node.
671
672
*/
672
- def typedApply (tree : untpd.Apply , pt : Type )(implicit ctx : Context ): Tree = {
673
-
673
+ def typedApply (tree : untpd.Apply , pt : Type , scala2InfixOp : Boolean = false )(implicit ctx : Context ): Tree = {
674
674
def realApply (implicit ctx : Context ): Tree = track(" realApply" ) {
675
- val originalProto = new FunProto (tree.args, IgnoredProto (pt), this )(argCtx(tree))
675
+ var originalProto = new FunProto (tree.args, IgnoredProto (pt), this )(argCtx(tree))
676
+
677
+ // In an infix operation `x op (y, z)` under Scala-2 mode, rewrite tuple on the
678
+ // right hand side to multi-parameters, i.e `x.op(y, z)`, relying on auto-tupling
679
+ // to revert this if `op` takes a single argument.
680
+ tree.args match {
681
+ case untpd.Tuple (elems) :: Nil if scala2InfixOp =>
682
+ originalProto = new FunProto (elems, IgnoredProto (pt), this )(argCtx(tree))
683
+ originalProto.scala2InfixOp = true
684
+ case _ =>
685
+ }
676
686
val fun1 = typedExpr(tree.fun, originalProto)
677
687
678
688
// Warning: The following lines are dirty and fragile. We record that auto-tupling was demanded as
@@ -682,7 +692,41 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
682
692
// otherwise we would get possible cross-talk between different `adapt` calls using the same
683
693
// prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with
684
694
// a modified tree but this would be more convoluted and less efficient.
685
- val proto = if (originalProto.isTupled) originalProto.tupled else originalProto
695
+ // All of this is provisional in the long run, since it is only needed under Scala-2 mode.
696
+ val proto =
697
+ if (originalProto.isTupled) originalProto.tupled
698
+ else {
699
+ // If we see a multi-parameter infix operation `x op (y, z)` under Scala-2 mode,
700
+ // issue a migration warning and propose a rewrite to `x .op (y, z)` (or
701
+ // `(x) .op (y, z)` is `x` is itself an infix operation) where this is possible.
702
+ // Not possible are rewrites of operator assignments and right-associative operators.
703
+ tree.fun match {
704
+ case sel @ Select (qual, opName)
705
+ if originalProto.scala2InfixOp && ! fun1.tpe.widen.isErroneous =>
706
+ val opEndOffset = sel.pos.point + opName.toString.length
707
+ val isOpAssign = ctx.source(opEndOffset) == '='
708
+ val isRightAssoc = ! isLeftAssoc(opName)
709
+ val addendum =
710
+ if (isOpAssign) " \n This requires a manual rewrite since it is part of an operator assignment."
711
+ else if (isRightAssoc) " \n This requires a manual rewrite since the operator is right-associative."
712
+ else " \n This can be fixed automatically using -rewrite."
713
+ ctx.migrationWarning(
714
+ em """ infix operator takes exactly one argument,
715
+ |use method call syntax with `.` instead. $addendum""" , fun1.pos)
716
+ val Select (qual, _) = tree.fun
717
+ if (! isOpAssign && ! isRightAssoc) {
718
+ patch(Position (sel.pos.point), " ." )
719
+ qual match {
720
+ case qual : untpd.InfixOp =>
721
+ patch(qual.pos.startPos, " (" )
722
+ patch(qual.pos.endPos, " )" )
723
+ case _ =>
724
+ }
725
+ }
726
+ case _ =>
727
+ }
728
+ originalProto
729
+ }
686
730
687
731
// If some of the application's arguments are function literals without explicitly declared
688
732
// parameter types, relate the normalized result type of the application with the
@@ -750,12 +794,25 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
750
794
* { val xs = es; e' = e' + args }
751
795
*/
752
796
def typedOpAssign (implicit ctx : Context ): Tree = track(" typedOpAssign" ) {
753
- val Apply (Select (lhs, name), rhss) = tree
797
+ val Apply (sel @ Select (lhs, name), rhss) = tree
754
798
val lhs1 = typedExpr(lhs)
755
799
val liftedDefs = new mutable.ListBuffer [Tree ]
756
800
val lhs2 = untpd.TypedSplice (LiftComplex .liftAssigned(liftedDefs, lhs1))
757
- val assign = untpd.Assign (lhs2,
758
- untpd.Apply (untpd.Select (lhs2, name.asSimpleName.dropRight(1 )), rhss))
801
+ val opName = name.asSimpleName.dropRight(1 )
802
+
803
+ // If original operation was an infix operation under Scala-2 mode, generate
804
+ // again an infix operation insteadof an application, so that the logic does
805
+ // the right thing for migrating from auto-tupling and multi-parameter infix ops.
806
+ val app = rhss match {
807
+ case (rhs : untpd.Tuple ) :: Nil if scala2InfixOp =>
808
+ val opEndOffset = sel.pos.point + opName.length
809
+ assert(ctx.source(opEndOffset) == '=' )
810
+ val op = untpd.Ident (opName).withPos(Position (sel.pos.point, opEndOffset))
811
+ untpd.InfixOp (lhs2, op, rhs)
812
+ case _ =>
813
+ untpd.Apply (untpd.Select (lhs2, opName), rhss)
814
+ }
815
+ val assign = untpd.Assign (lhs2, app)
759
816
wrapDefs(liftedDefs, typed(assign))
760
817
}
761
818
0 commit comments