Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

unifies approaches to call analysis in TreeInfo

Instead of a flurry of methods such as `methPart` or `typeArguments`
to analyze applications, there is a single access point to related info:

  `treeInfo.dissectApplied(tree)`

Dissection returns an instance of the `Applied` class, which can
extract parts of applications and perform other application-specific
operations, e.g. `applyCount`.

For the sake of convenience, there's also an extractor named `Applied`,
which extracts `core`, `targs` and `argss`, where core is everything
except targs and argss. Extractor works for both `Tree` and `Applied`
(in the former case, it's equivalent to first dissecting and pattern
matching).
  • Loading branch information...
commit 85f320258cbd68c4235cf0cdf2fede9ab6e88c8b 1 parent 5896985
@xeno-by xeno-by authored
View
5 src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala
@@ -1279,7 +1279,10 @@ trait ContextErrors {
fail()
}
- private def implRefError(message: String) = genericError(methPart(macroDdef.rhs), message)
+ private def implRefError(message: String) = {
+ val treeInfo.Applied(implRef, _, _) = macroDdef.rhs
+ genericError(implRef, message)
+ }
private def compatibilityError(message: String) =
implRefError(
View
21 src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -3891,7 +3891,7 @@ trait Typers extends Modes with Adaptations with Tags {
case DynamicApplicationNamed(qual, _) if acceptsApplyDynamic(qual.tpe.widen) => true
case _ => false
// look deeper?
- // val methPart = treeInfo.methPart(fun)
+ // val treeInfo.Applied(methPart, _, _) = fun
// println("methPart of "+ fun +" is "+ methPart)
// if (methPart ne fun) isApplyDynamicNamed(methPart)
// else false
@@ -3927,7 +3927,7 @@ trait Typers extends Modes with Adaptations with Tags {
*/
def mkInvoke(cxTree: Tree, tree: Tree, qual: Tree, name: Name): Option[Tree] = {
log(s"dyna.mkInvoke($cxTree, $tree, $qual, $name)")
- val treeSelection = treeInfo.methPart(tree)
+ val treeInfo.Applied(treeSelection, _, _) = tree
def isDesugaredApply = treeSelection match {
case Select(`qual`, nme.apply) => true
case _ => false
@@ -3940,7 +3940,7 @@ trait Typers extends Modes with Adaptations with Tags {
// not supported: foo.bar(a1,..., an: _*)
def hasStar(args: List[Tree]) = treeInfo.isWildcardStarArgList(args)
def applyOp(args: List[Tree]) = if (hasNamed(args)) nme.applyDynamicNamed else nme.applyDynamic
- def matches(t: Tree) = isDesugaredApply || treeInfo.methPart(t) == treeSelection
+ def matches(t: Tree) = isDesugaredApply || treeInfo.dissectApplied(t).core == treeSelection
/** Note that the trees which arrive here are potentially some distance from
* the trees of direct interest. `cxTree` is some enclosing expression which
@@ -3958,9 +3958,8 @@ trait Typers extends Modes with Adaptations with Tags {
case _ => t.children flatMap findSelection headOption
}
findSelection(cxTree) match {
- case Some((opName, tapply)) =>
- val targs = treeInfo.typeArguments(tapply)
- val fun = gen.mkTypeApply(Select(qual, opName), targs)
+ case Some((opName, treeInfo.Applied(_, targs, _))) =>
+ val fun = gen.mkTypeApply(Select(qual, opName), targs)
atPos(qual.pos)(Apply(fun, Literal(Constant(name.decode)) :: Nil))
case _ =>
setError(tree)
@@ -4149,8 +4148,8 @@ trait Typers extends Modes with Adaptations with Tags {
return fail()
if (treeInfo.mayBeVarGetter(varsym)) {
- treeInfo.methPart(lhs1) match {
- case Select(qual, name) =>
+ lhs1 match {
+ case treeInfo.Applied(Select(qual, name), _, _) =>
val sel = Select(qual, nme.getterToSetter(name.toTermName)) setPos lhs.pos
val app = Apply(sel, List(rhs)) setPos tree.pos
return typed(app, mode, pt)
@@ -4568,9 +4567,9 @@ trait Typers extends Modes with Adaptations with Tags {
}
case Apply(fn, indices) =>
- treeInfo.methPart(fn) match {
- case Select(table, nme.apply) => mkUpdate(table, indices)
- case _ => UnexpectedTreeAssignmentConversionError(qual)
+ fn match {
+ case treeInfo.Applied(Select(table, nme.apply), _, _) => mkUpdate(table, indices)
+ case _ => UnexpectedTreeAssignmentConversionError(qual)
}
}
typed1(tree1, mode, pt)
View
172 src/reflect/scala/reflect/internal/TreeInfo.scala
@@ -159,7 +159,7 @@ abstract class TreeInfo {
* Also accounts for varargs.
*/
private def applyMethodParameters(fn: Tree): List[Symbol] = {
- val depth = applyDepth(fn)
+ val depth = dissectApplied(fn).applyDepth
// There could be applies which go beyond the parameter list(s),
// being applied to the result of the method call.
// !!! Note that this still doesn't seem correct, although it should
@@ -195,29 +195,26 @@ abstract class TreeInfo {
def isGetter = mayBeVarGetter(sym) && sym.owner.info.member(nme.getterToSetter(sym.name.toTermName)) != NoSymbol
tree match {
- case Ident(_) => isVar
- case Select(_, _) => isVar || isGetter
- case _ =>
- methPart(tree) match {
- case Select(qual, nme.apply) => qual.tpe.member(nme.update) != NoSymbol
- case _ => false
- }
+ case Ident(_) => isVar
+ case Select(_, _) => isVar || isGetter
+ case Applied(Select(qual, nme.apply), _, _) => qual.tpe.member(nme.update) != NoSymbol
+ case _ => false
}
}
/** Is tree a self constructor call this(...)? I.e. a call to a constructor of the
* same object?
*/
- def isSelfConstrCall(tree: Tree): Boolean = methPart(tree) match {
- case Ident(nme.CONSTRUCTOR)
- | Select(This(_), nme.CONSTRUCTOR) => true
+ def isSelfConstrCall(tree: Tree): Boolean = tree match {
+ case Applied(Ident(nme.CONSTRUCTOR), _, _) => true
+ case Applied(Select(This(_), nme.CONSTRUCTOR), _, _) => true
case _ => false
}
/** Is tree a super constructor call?
*/
- def isSuperConstrCall(tree: Tree): Boolean = methPart(tree) match {
- case Select(Super(_, _), nme.CONSTRUCTOR) => true
+ def isSuperConstrCall(tree: Tree): Boolean = tree match {
+ case Applied(Select(Super(_, _), nme.CONSTRUCTOR), _, _) => true
case _ => false
}
@@ -399,22 +396,6 @@ abstract class TreeInfo {
case _ => false
}
- /** If this tree represents a type application (after unwrapping
- * any applies) the first type argument. Otherwise, EmptyTree.
- */
- def firstTypeArg(tree: Tree): Tree = tree match {
- case Apply(fn, _) => firstTypeArg(fn)
- case TypeApply(_, targ :: _) => targ
- case _ => EmptyTree
- }
-
- /** If this tree represents a type application the type arguments. Otherwise Nil.
- */
- def typeArguments(tree: Tree): List[Tree] = tree match {
- case TypeApply(_, targs) => targs
- case _ => Nil
- }
-
/** If this tree has type parameters, those. Otherwise Nil.
*/
def typeParameters(tree: Tree): List[TypeDef] = tree match {
@@ -514,30 +495,121 @@ abstract class TreeInfo {
def hasSynthCaseSymbol(t: Tree) = t.symbol != null && isSynthCaseSymbol(t.symbol)
- /** The method part of an application node
+ /** Applications in Scala can have one of the following shapes:
+ *
+ * 1) naked core: Ident(_) or Select(_, _) or basically anything else
+ * 2) naked core with targs: TypeApply(core, targs) or AppliedTypeTree(core, targs)
+ * 3) apply or several applies wrapping a core: Apply(core, _), or Apply(Apply(core, _), _), etc
+ *
+ * This class provides different ways to decompose applications and simplifies their analysis.
+ *
+ * ***Examples***
+ * (TypeApply in the examples can be replaced with AppliedTypeTree)
+ *
+ * Ident(foo):
+ * * callee = Ident(foo)
+ * * core = Ident(foo)
+ * * targs = Nil
+ * * argss = Nil
+ *
+ * TypeApply(foo, List(targ1, targ2...))
+ * * callee = TypeApply(foo, List(targ1, targ2...))
+ * * core = foo
+ * * targs = List(targ1, targ2...)
+ * * argss = Nil
+ *
+ * Apply(foo, List(arg1, arg2...))
+ * * callee = foo
+ * * core = foo
+ * * targs = Nil
+ * * argss = List(List(arg1, arg2...))
+ *
+ * Apply(Apply(foo, List(arg21, arg22, ...)), List(arg11, arg12...))
+ * * callee = foo
+ * * core = foo
+ * * targs = Nil
+ * * argss = List(List(arg11, arg12...), List(arg21, arg22, ...))
+ *
+ * Apply(Apply(TypeApply(foo, List(targs1, targs2, ...)), List(arg21, arg22, ...)), List(arg11, arg12...))
+ * * callee = TypeApply(foo, List(targs1, targs2, ...))
+ * * core = foo
+ * * targs = Nil
+ * * argss = List(List(arg11, arg12...), List(arg21, arg22, ...))
*/
- def methPart(tree: Tree): Tree = tree match {
- case Apply(fn, _) => methPart(fn)
- case TypeApply(fn, _) => methPart(fn)
- case AppliedTypeTree(fn, _) => methPart(fn)
- case _ => tree
+ class Applied(val tree: Tree) {
+ /** The tree stripped of the possibly nested applications.
+ * The original tree if it's not an application.
+ */
+ def callee: Tree = {
+ def loop(tree: Tree): Tree = tree match {
+ case Apply(fn, _) => loop(fn)
+ case tree => tree
+ }
+ loop(tree)
+ }
+
+ /** The `callee` unwrapped from type applications.
+ * The original `callee` if it's not a type application.
+ */
+ def core: Tree = callee match {
+ case TypeApply(fn, _) => fn
+ case AppliedTypeTree(fn, _) => fn
+ case tree => tree
+ }
+
+ /** The type arguments of the `callee`.
+ * `Nil` if the `callee` is not a type application.
+ */
+ def targs: List[Tree] = callee match {
+ case TypeApply(_, args) => args
+ case AppliedTypeTree(_, args) => args
+ case _ => Nil
+ }
+
+ /** (Possibly multiple lists of) value arguments of an application.
+ * `Nil` if the `callee` is not an application.
+ */
+ def argss: List[List[Tree]] = {
+ def loop(tree: Tree): List[List[Tree]] = tree match {
+ case Apply(fn, args) => loop(fn) :+ args
+ case _ => Nil
+ }
+ loop(tree)
+ }
+
+ /** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _)
+ * has depth 3. Continues through type applications (without counting them.)
+ */
+ def applyDepth: Int = {
+ def loop(tree: Tree): Int = tree match {
+ case Apply(fn, _) => 1 + loop(fn)
+ case TypeApply(fn, _) => loop(fn)
+ case AppliedTypeTree(fn, _) => loop(fn)
+ case _ => 0
+ }
+ loop(tree)
+ }
}
- /** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _)
- * has depth 3. Continues through type applications (without counting them.)
+ /** Returns a wrapper that knows how to destructure and analyze applications.
*/
- def applyDepth(tree: Tree): Int = tree match {
- case Apply(fn, _) => 1 + applyDepth(fn)
- case TypeApply(fn, _) => applyDepth(fn)
- case AppliedTypeTree(fn, _) => applyDepth(fn)
- case _ => 0
- }
- def firstArgument(tree: Tree): Tree = tree match {
- case Apply(fn, args) =>
- val f = firstArgument(fn)
- if (f == EmptyTree && !args.isEmpty) args.head else f
- case _ =>
- EmptyTree
+ def dissectApplied(tree: Tree) = new Applied(tree)
+
+ /** Destructures applications into important subparts described in `Applied` class,
+ * namely into: core, targs and argss (in the specified order).
+ *
+ * Trees which are not applications are also accepted. Their callee and core will
+ * be equal to the input, while targs and argss will be Nil.
+ *
+ * The provided extractors don't expose all the API of the `Applied` class.
+ * For advanced use, call `dissectApplied` explicitly and use its methods instead of pattern matching.
+ */
+ object Applied {
+ def unapply(applied: Applied): Option[(Tree, List[Tree], List[List[Tree]])] =
+ Some((applied.core, applied.targs, applied.argss))
+
+ def unapply(tree: Tree): Option[(Tree, List[Tree], List[List[Tree]])] =
+ unapply(dissectApplied(tree))
}
/** Does list of trees start with a definition of
@@ -634,7 +706,7 @@ abstract class TreeInfo {
}
def unapply(tree: Tree) = refPart(tree) match {
- case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, typeArguments(tree)))
+ case ref: RefTree => Some((ref.qualifier.symbol, ref.symbol, dissectApplied(tree).targs))
case _ => None
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.