@@ -110,6 +110,24 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
110
110
case _ => 0
111
111
}
112
112
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
+
113
131
/** All term arguments of an application in a single flattened list */
114
132
def allArguments (tree : Tree ): List [Tree ] = unsplice(tree) match {
115
133
case Apply (fn, args) => allArguments(fn) ::: args
@@ -308,6 +326,132 @@ trait TreeInfo[T <: Untyped] { self: Trees.Instance[T] =>
308
326
case Block (_, expr) => forallResults(expr, p)
309
327
case _ => p(tree)
310
328
}
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
+ }
311
455
}
312
456
313
457
trait UntypedTreeInfo extends TreeInfo [Untyped ] { self : Trees .Instance [Untyped ] =>
@@ -683,24 +827,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
683
827
}
684
828
}
685
829
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
-
704
830
/** The type and term arguments of a possibly curried call, in the order they are given */
705
831
def allArgss (tree : Tree ): List [List [Tree ]] =
706
832
@ tailrec
0 commit comments