blob: e0bd6c75aa0a52aac0576adb45c890480b3c8df9 (
plain) (
tree)
|
|
package dotty.tools.dotc
package ast
import util.Positions._
import util.DotClass
/** A base class for things that have positions (currently: modifiers and trees)
*/
abstract class Positioned extends DotClass with Product {
private[this] var curPos: Position = _
setPos(initialPos)
/** The item's position.
*/
def pos: Position = curPos
/** Destructively update `curPos` to given position. Also, set any missing
* positions in children.
*/
protected def setPos(pos: Position): Unit = {
curPos = pos
if (pos.exists) setChildPositions(pos.toSynthetic)
}
/** The envelope containing the item in its entirety. Envelope is different from
* `pos` for definitions (instances of MemberDef).
*/
def envelope: Position = pos.toSynthetic
/** A positioned item like this one with the position set to `pos`.
* if the positioned item is source-derived, a clone is returned.
* If the positioned item is synthetic, the position is updated
* destructively and the item itself is returned.
*/
def withPos(pos: Position): this.type = {
val newpd = (if (pos == curPos || curPos.isSynthetic) this else clone).asInstanceOf[Positioned]
newpd.setPos(pos)
newpd.asInstanceOf[this.type]
}
def withPos(posd: Positioned): this.type =
if (posd == null) this else withPos(posd.pos)
/** This item with a position that's the union of the given `pos` and the
* current position.
*/
def addPos(pos: Position): this.type = withPos(pos union this.pos)
/** Set position of this tree only, without performing
* any checks of consistency with - or updates of - other positions.
* Called from Unpickler when entering positions.
*/
private[dotc] def setPosUnchecked(pos: Position) = curPos = pos
/** If any children of this node do not have positions, set them to the given position,
* and transitively visit their children.
*/
private def setChildPositions(pos: Position): Unit = {
def deepSetPos(x: Any): Unit = x match {
case p: Positioned =>
if (!p.pos.exists) p.setPos(pos)
case xs: List[_] =>
xs foreach deepSetPos
case _ =>
}
var n = productArity
while (n > 0) {
n -= 1
deepSetPos(productElement(n))
}
}
/** The initial, synthetic position. This is usually the union of all positioned children's
* envelopes.
*/
protected def initialPos: Position = {
var n = productArity
var pos = NoPosition
while (n > 0) {
n -= 1
productElement(n) match {
case p: Positioned => pos = pos union p.envelope
case xs: List[_] => pos = unionPos(pos, xs)
case _ =>
}
}
pos.toSynthetic
}
private def unionPos(pos: Position, xs: List[_]): Position = xs match {
case (p: Positioned) :: xs1 => unionPos(pos union p.envelope, xs1)
case _ => pos
}
def contains(that: Positioned): Boolean = {
def isParent(x: Any): Boolean = x match {
case x: Positioned =>
x contains that
case xs: List[_] =>
xs exists isParent
case _ =>
false
}
(this eq that) ||
(this.envelope contains that.pos) && {
var n = productArity
var found = false
while (n > 0 && !found) {
n -= 1
found = isParent(productElement(n))
}
found
}
}
/** The path from this node to `that` node, represented
* as a list starting with `this`, ending with`that` where
* every node is a child of its predecessor.
* Nil if no such path exists.
*/
def pathTo(that: Positioned): List[Positioned] = {
def childPath(it: Iterator[Any]): List[Positioned] =
if (it.hasNext) {
val cpath = it.next match {
case x: Positioned => x.pathTo(that)
case xs: List[_] => childPath(xs.iterator)
case _ => Nil
}
if (cpath.nonEmpty) cpath else childPath(it)
} else Nil
if (this eq that) this :: Nil
else if (this.envelope contains that.pos) {
val cpath = childPath(productIterator)
if (cpath.nonEmpty) this :: cpath else Nil
} else Nil
}
}
|