aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/ast/Positioned.scala
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
  }
}