1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
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,
* fit their positions between the positions of the known subtrees
* and transitively visit their children.
* The method is likely time-critical because it is invoked on any node
* we create, so we want to avoid object allocations in the common case.
* The method is naturally expressed as two mutually (tail-)recursive
* functions, one which computes the next element to consider or terminates if there
* is none and the other which propagates the position information to that element.
* But since mutual tail recursion is not supported in Scala, we express it instead
* as a while loop with a termination by return in the middle.
*/
private def setChildPositions(pos: Position): Unit = {
var n = productArity // subnodes are analyzed right to left
var elems: List[Any] = Nil // children in lists still to be considered, from right to left
var end = pos.end // the last defined offset, fill in positions up to this offset
var outstanding: List[Positioned] = Nil // nodes that need their positions filled once a start position
// is known, from left to right.
def fillIn(ps: List[Positioned], start: Int, end: Int): Unit = ps match {
case p :: ps1 =>
p.setPos(Position(start, end))
fillIn(ps1, end, end)
case nil =>
}
while (true) {
var nextChild: Any = null // the next child to be considered
if (elems.nonEmpty) {
nextChild = elems.head
elems = elems.tail
}
else if (n > 0) {
n = n - 1
nextChild = productElement(n)
}
else {
fillIn(outstanding, pos.start, end)
return
}
nextChild match {
case p: Positioned =>
if (p.pos.exists) {
fillIn(outstanding, p.pos.end, end)
outstanding = Nil
end = p.pos.start
}
else outstanding = p :: outstanding
case xs: List[_] =>
elems = elems ::: xs.reverse
case _ =>
}
}
}
/** 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
}
}
}
|