@@ -110,6 +110,24 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
110110 case _ => 0
111111 }
112112
113+ /** The type arguments of a possibly curried call */
114+ def typeArgss (tree : Tree ): List [List [Tree ]] =
115+ @ tailrec
116+ def loop (tree : Tree , argss : List [List [Tree ]]): List [List [Tree ]] = tree match
117+ case TypeApply (fn, args) => loop(fn, args :: argss)
118+ case Apply (fn, args) => loop(fn, argss)
119+ case _ => argss
120+ loop(tree, Nil )
121+
122+ /** The term arguments of a possibly curried call */
123+ def termArgss (tree : Tree ): List [List [Tree ]] =
124+ @ tailrec
125+ def loop (tree : Tree , argss : List [List [Tree ]]): List [List [Tree ]] = tree match
126+ case Apply (fn, args) => loop(fn, args :: argss)
127+ case TypeApply (fn, args) => loop(fn, argss)
128+ case _ => argss
129+ loop(tree, Nil )
130+
113131 /** All term arguments of an application in a single flattened list */
114132 def allArguments (tree : Tree ): List [Tree ] = unsplice(tree) match {
115133 case Apply (fn, args) => allArguments(fn) ::: args
@@ -308,6 +326,132 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
308326 case Block (_, expr) => forallResults(expr, p)
309327 case _ => p(tree)
310328 }
329+
330+ /** Applications in Scala can have one of the following shapes:
331+ *
332+ * 1) naked core: Ident(_) or Select(_, _) or basically anything else
333+ * 2) naked core with targs: TypeApply(core, targs) or AppliedTypeTree(core, targs)
334+ * 3) apply or several applies wrapping a core: Apply(core, _), or Apply(Apply(core, _), _), etc
335+ *
336+ * This class provides different ways to decompose applications and simplifies their analysis.
337+ *
338+ * ***Examples***
339+ * (TypeApply in the examples can be replaced with AppliedTypeTree)
340+ *
341+ * Ident(foo):
342+ * * callee = Ident(foo)
343+ * * core = Ident(foo)
344+ * * targs = Nil
345+ * * argss = Nil
346+ *
347+ * TypeApply(foo, List(targ1, targ2...))
348+ * * callee = TypeApply(foo, List(targ1, targ2...))
349+ * * core = foo
350+ * * targs = List(targ1, targ2...)
351+ * * argss = Nil
352+ *
353+ * Apply(foo, List(arg1, arg2...))
354+ * * callee = foo
355+ * * core = foo
356+ * * targs = Nil
357+ * * argss = List(List(arg1, arg2...))
358+ *
359+ * Apply(Apply(foo, List(arg21, arg22, ...)), List(arg11, arg12...))
360+ * * callee = foo
361+ * * core = foo
362+ * * targs = Nil
363+ * * argss = List(List(arg21, arg22, ...), List(arg11, arg12, ...))
364+ *
365+ * Apply(Apply(TypeApply(foo, List(targs1, targs2, ...)), List(arg21, arg22, ...)), List(arg11, arg12...))
366+ * * callee = TypeApply(foo, List(targs1, targs2, ...))
367+ * * core = foo
368+ * * targs = Nil
369+ * * argss = List(List(arg21, arg22, ...), List(arg11, arg12, ...))
370+ */
371+ final class Applied (val tree : Tree ) {
372+ /** The tree stripped of the possibly nested applications.
373+ * The original tree if it's not an application.
374+ */
375+ def callee : Tree = stripApply(tree)
376+
377+ /** The `callee` unwrapped from type applications.
378+ * The original `callee` if it's not a type application.
379+ */
380+ def core : Tree = callee match {
381+ case TypeApply (fn, _) => fn
382+ case AppliedTypeTree (fn, _) => fn
383+ case tree => tree
384+ }
385+
386+ /** The type arguments of the `callee`.
387+ * `Nil` if the `callee` is not a type application.
388+ */
389+ def targs : List [Tree ] = callee match {
390+ case TypeApply (_, args) => args
391+ case AppliedTypeTree (_, args) => args
392+ case _ => Nil
393+ }
394+
395+ /** (Possibly multiple lists of) value arguments of an application.
396+ * `Nil` if the `callee` is not an application.
397+ */
398+ def argss : List [List [Tree ]] = termArgss(tree)
399+ }
400+
401+ /** Destructures applications into important subparts described in `Applied` class,
402+ * namely into: core, targs and argss (in the specified order).
403+ *
404+ * Trees which are not applications are also accepted. Their callee and core will
405+ * be equal to the input, while targs and argss will be Nil.
406+ *
407+ * The provided extractors don't expose all the API of the `Applied` class.
408+ * For advanced use, call `dissectApplied` explicitly and use its methods instead of pattern matching.
409+ */
410+ object Applied {
411+ def apply (tree : Tree ): Applied = new Applied (tree)
412+
413+ def unapply (applied : Applied ): Some [(Tree , List [Tree ], List [List [Tree ]])] =
414+ Some ((applied.core, applied.targs, applied.argss))
415+
416+ def unapply (tree : Tree ): Some [(Tree , List [Tree ], List [List [Tree ]])] =
417+ unapply(new Applied (tree))
418+ }
419+
420+ /** Is tree an application with result `this.type`?
421+ * Accept `b.addOne(x)` and also `xs(i) += x`
422+ * where the op is an assignment operator.
423+ */
424+ def isThisTypeResult (tree : Tree )(using Context ): Boolean = tree match {
425+ case Applied (fun @ Select (receiver, op), _, argss) =>
426+ tree.tpe match {
427+ case ThisType (tref) =>
428+ tref.symbol == receiver.symbol
429+ case tref : TermRef =>
430+ tref.symbol == receiver.symbol || argss.exists(_.exists(tref.symbol == _.symbol))
431+ case _ =>
432+ def checkSingle (sym : Symbol ): Boolean =
433+ (sym == receiver.symbol) || {
434+ receiver match {
435+ case Apply (_, _) => op.isOpAssignmentName // xs(i) += x
436+ case _ => receiver.symbol != NoSymbol &&
437+ (receiver.symbol.isGetter || receiver.symbol.isField) // xs.addOne(x) for var xs
438+ }
439+ }
440+ @ tailrec def loop (mt : Type ): Boolean = mt match {
441+ case m : MethodType =>
442+ m.resType match {
443+ case ThisType (tref) => checkSingle(tref.symbol)
444+ case tref : TermRef => checkSingle(tref.symbol)
445+ case restpe => loop(restpe)
446+ }
447+ case PolyType (_, restpe) => loop(restpe)
448+ case _ => false
449+ }
450+ fun.symbol != NoSymbol && loop(fun.symbol.info)
451+ }
452+ case _ =>
453+ tree.tpe.isInstanceOf [ThisType ]
454+ }
311455}
312456
313457trait UntypedTreeInfo extends TreeInfo [Untyped ] { self : Trees .Instance [Untyped ] =>
@@ -683,24 +827,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
683827 }
684828 }
685829
686- /** The type arguments of a possibly curried call */
687- def typeArgss (tree : Tree ): List [List [Tree ]] =
688- @ tailrec
689- def loop (tree : Tree , argss : List [List [Tree ]]): List [List [Tree ]] = tree match
690- case TypeApply (fn, args) => loop(fn, args :: argss)
691- case Apply (fn, args) => loop(fn, argss)
692- case _ => argss
693- loop(tree, Nil )
694-
695- /** The term arguments of a possibly curried call */
696- def termArgss (tree : Tree ): List [List [Tree ]] =
697- @ tailrec
698- def loop (tree : Tree , argss : List [List [Tree ]]): List [List [Tree ]] = tree match
699- case Apply (fn, args) => loop(fn, args :: argss)
700- case TypeApply (fn, args) => loop(fn, argss)
701- case _ => argss
702- loop(tree, Nil )
703-
704830 /** The type and term arguments of a possibly curried call, in the order they are given */
705831 def allArgss (tree : Tree ): List [List [Tree ]] =
706832 @ tailrec
0 commit comments