diff options
author | Eugene Burmako <xeno.by@gmail.com> | 2012-11-25 12:30:13 +0100 |
---|---|---|
committer | Eugene Burmako <xeno.by@gmail.com> | 2012-12-06 23:17:26 +0100 |
commit | 85f320258cbd68c4235cf0cdf2fede9ab6e88c8b (patch) | |
tree | 4fd19e9d8dea508b3d423c68f7ae6f5d55ae95aa | |
parent | 58969850a0991a72c360433540943eae4b10dc6b (diff) | |
download | scala-85f320258cbd68c4235cf0cdf2fede9ab6e88c8b.tar.gz scala-85f320258cbd68c4235cf0cdf2fede9ab6e88c8b.tar.bz2 scala-85f320258cbd68c4235cf0cdf2fede9ab6e88c8b.zip |
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).
3 files changed, 136 insertions, 62 deletions
diff --git a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala b/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala index faacb60d75..f915f84a32 100644 --- a/src/compiler/scala/tools/nsc/typechecker/ContextErrors.scala +++ b/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( diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index ae506cd6ab..7c76eff9d8 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/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) diff --git a/src/reflect/scala/reflect/internal/TreeInfo.scala b/src/reflect/scala/reflect/internal/TreeInfo.scala index 8ad15f37e4..7423a2ff57 100644 --- a/src/reflect/scala/reflect/internal/TreeInfo.scala +++ b/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 } } |