@@ -15,179 +15,117 @@ import annotation.internal.sharable
15
15
*/
16
16
abstract class Positioned (implicit @ transientParam src : SourceFile ) extends Product with Cloneable {
17
17
18
+ private [this ] var myUniqueId : Int = _
19
+ private [this ] var mySpan : Span = _
20
+
18
21
/** A unique identifier. Among other things, used for determining the source file
19
22
* component of the position.
20
23
*/
21
- private var myUniqueId : Int = - 1
22
- private [this ] var mySpan : Span = NoSpan
24
+ def uniqueId : Int = myUniqueId
25
+
26
+ def uniqueId_= (id : Int ): Unit = {
27
+ // assert(id != 2523, this)
28
+ myUniqueId = id
29
+ }
23
30
24
31
/** The span part of the item's position */
25
32
def span : Span = mySpan
26
33
27
- /** item's id */
28
- def uniqueId : Int = myUniqueId
34
+ def span_= (span : Span ): Unit = {
35
+ mySpan = span
36
+ }
37
+
38
+ uniqueId = src.nextId
39
+ span = envelope(src)
29
40
30
41
def source : SourceFile = SourceFile .fromId(uniqueId)
31
42
def sourcePos (implicit ctx : Context ): SourcePosition = source.atSpan(span)
32
43
33
- // setId(initialSource(src).nextId)
34
- setPos(initialSpan(src), source)
35
-
36
- protected def setId (id : Int ): Unit = {
37
- myUniqueId = id
38
- // assert(id != 2067, getClass)
39
- }
40
-
41
- /** Destructively update `mySpan` to given span and potentially update `id` so that
42
- * it refers to `file`. Also, set any missing positions in children.
43
- */
44
- protected def setPos (span : Span , file : SourceFile ): Unit = {
45
- setOnePos(span, file)
46
- if (span.exists) setChildPositions(span.toSynthetic, file)
47
- }
48
-
49
44
/** A positioned item like this one with given `span`.
50
45
* If the positioned item is source-derived, a clone is returned.
51
46
* If the positioned item is synthetic, the position is updated
52
47
* destructively and the item itself is returned.
53
48
*/
54
- def withSpan (span : Span ): this .type = {
55
- val ownSpan = this .span
56
- val newpd : this .type =
57
- if (span == ownSpan || ownSpan.isSynthetic) this else cloneIn(source)
58
- newpd.setPos(span, source)
59
- newpd
60
- }
61
-
62
- /** Set span of this tree only, without updating children spans.
63
- * Called from Unpickler when entering positions.
64
- */
65
- private [dotc] def setOnePos (span : Span , file : SourceFile = this .source): Unit = {
66
- if (file `ne` this .source) setId(file.nextId)
67
- mySpan = span
68
- }
69
-
70
- /** If any children of this node do not have spans,
71
- * fit their spans between the spans of the known subtrees
72
- * and transitively visit their children.
73
- * The method is likely time-critical because it is invoked on any node
74
- * we create, so we want to avoid object allocations in the common case.
75
- * The method is naturally expressed as two mutually (tail-)recursive
76
- * functions, one which computes the next element to consider or terminates if there
77
- * is none and the other which propagates the span information to that element.
78
- * But since mutual tail recursion is not supported in Scala, we express it instead
79
- * as a while loop with a termination by return in the middle.
80
- */
81
- private def setChildPositions (span : Span , file : SourceFile ): Unit = {
82
- var n = productArity // subnodes are analyzed right to left
83
- var elems : List [Any ] = Nil // children in lists still to be considered, from right to left
84
- var end = span.end // the last defined offset, fill in spans up to this offset
85
- var outstanding : List [Positioned ] = Nil // nodes that need their spans filled once a start offset
86
- // is known, from left to right.
87
- def fillIn (ps : List [Positioned ], start : Int , end : Int ): Unit = ps match {
88
- case p :: ps1 =>
89
- // If a tree has no span or a zero-extent span, it should be
90
- // synthetic. We can preserve this invariant by always setting a
91
- // zero-extent span for these trees here.
92
- if (! p.span.exists || p.span.isZeroExtent) {
93
- p.setPos(Span (start, start), file)
94
- fillIn(ps1, start, end)
95
- } else {
96
- p.setPos(Span (start, end), file)
97
- fillIn(ps1, end, end)
49
+ def withSpan (span : Span ): this .type =
50
+ if (span == mySpan) this
51
+ else {
52
+ val newpd : this .type =
53
+ if (mySpan.isSynthetic) {
54
+ if (! mySpan.exists && span.exists)
55
+ envelope(source, span.startPos) // fill in children spans
56
+ this
98
57
}
99
- case nil =>
58
+ else cloneIn(source)
59
+ newpd.span = span
60
+ newpd
100
61
}
101
- while (true ) {
102
- var nextChild : Any = null // the next child to be considered
103
- if (elems.nonEmpty) {
104
- nextChild = elems.head
105
- elems = elems.tail
106
- }
107
- else if (n > 0 ) {
108
- n = n - 1
109
- nextChild = productElement(n)
110
- }
111
- else {
112
- fillIn(outstanding, span.start, end)
113
- return
114
- }
115
- nextChild match {
62
+
63
+ /** The union of startSpan and the spans of all positioned children that
64
+ * have the same source as this node, except that Inlined nodes only
65
+ * consider their `call` child.
66
+ *
67
+ * Side effect: Any descendants without spans have but with the same source as this
68
+ * node have their span set to the end position of the envelope of all children to
69
+ * the left, or, if that one does not exist, to the start position of the envelope
70
+ * of all children to the right.
71
+ *
72
+ * @param ignoreTypeTrees If true, don't count type trees in the union.
73
+ * This is used to decide whether we need to pickle a position for a tree.
74
+ * TypeTreesare pickled as types and therefore contribute nothing to the span union.
75
+ */
76
+ def envelope (src : SourceFile , startSpan : Span = NoSpan , ignoreTypeTrees : Boolean = false ): Span = this match {
77
+ case Trees .Inlined (call, _, _) =>
78
+ // println(s"envelope of $this # $uniqueId = ${call.span}")
79
+ call.span
80
+ case _ =>
81
+ def include (span : Span , x : Any ): Span = x match {
82
+ case p : Trees .TypeTree [_] if ignoreTypeTrees =>
83
+ span
84
+ case core.tasty.TreePickler .Hole if ignoreTypeTrees =>
85
+ span
116
86
case p : Positioned =>
117
- if (p.span.exists) {
118
- fillIn(outstanding, p.span.end, end)
119
- outstanding = Nil
120
- end = p.span.start
87
+ if (p.source `ne` src) span
88
+ else if (p.span.exists) span.union(p.span)
89
+ else if (span.exists) {
90
+ if (span.end != MaxOffset )
91
+ p.span = p.envelope(src, span.endPos, ignoreTypeTrees)
92
+ span
121
93
}
122
- else outstanding = p :: outstanding
94
+ else // No span available to assign yet, signal this by returning a span with MaxOffset end
95
+ Span (MaxOffset , MaxOffset )
123
96
case m : untpd.Modifiers =>
124
- if (m.mods.nonEmpty || m.annotations.nonEmpty)
125
- elems = elems ::: m.mods.reverse ::: m.annotations.reverse
126
- case xs : List [_] =>
127
- elems = elems ::: xs.reverse
128
- case _ =>
97
+ include(include(span, m.mods), m.annotations)
98
+ case y :: ys =>
99
+ include(include(span, y), ys)
100
+ case _ => span
129
101
}
130
- }
102
+ val limit = productArity
103
+ def includeChildren (span : Span , n : Int ): Span =
104
+ if (n < limit) includeChildren(include(span, productElement(n)), n + 1 )
105
+ else span
106
+ val span1 = includeChildren(startSpan, 0 )
107
+ val span2 =
108
+ if (! span1.exists || span1.end != MaxOffset )
109
+ span1
110
+ else if (span1.start == MaxOffset )
111
+ // No positioned child was found
112
+ NoSpan
113
+ else {
114
+ // /println(s"revisit $uniqueId with $span1")
115
+ // We have some children left whose span could not be assigned.
116
+ // Go through it again with the known start position.
117
+ includeChildren(span1.startPos, 0 )
118
+ }
119
+ span2.toSynthetic
131
120
}
132
121
133
122
/** Clone this node but assign it a fresh id which marks it as a node in `file`. */
134
- protected def cloneIn (file : SourceFile ): this .type = {
123
+ def cloneIn (src : SourceFile ): this .type = {
135
124
val newpd : this .type = clone.asInstanceOf [this .type ]
136
- newpd.setId(file .nextId)
125
+ newpd.uniqueId = src .nextId
137
126
newpd
138
127
}
139
128
140
- /** The initial, synthetic span. This is usually the union of all positioned children's spans.
141
- */
142
- def initialSpan (givenSource : SourceFile ): Span = {
143
-
144
- def include (span1 : Span , p2 : Positioned ): Span = {
145
- val span2 = p2.span
146
- if (span2.exists) {
147
- var src = if (uniqueId == - 1 ) NoSource else source
148
- val src2 = p2.source
149
- if (src `eq` src2) span1.union(span2)
150
- else if (! src.exists) {
151
- setId(src2.nextId)
152
- if (span1.exists) initialSpan(givenSource) // we might have some mis-classified children; re-run everything
153
- else span2
154
- }
155
- else span1 // sources differ: ignore child span
156
- }
157
- else span1
158
- }
159
-
160
- def includeAll (span : Span , xs : List [_]): Span = xs match {
161
- case Nil => span
162
- case (p : Positioned ) :: xs1 => includeAll(include(span, p), xs1)
163
- case (xs0 : List [_]) :: xs1 => includeAll(includeAll(span, xs0), xs1)
164
- case _ :: xs1 => includeAll(span, xs1)
165
- }
166
-
167
- val limit = relevantElemCount
168
- var n = 0
169
- var span = NoSpan
170
- while (n < limit) {
171
- productElement(n) match {
172
- case p : Positioned =>
173
- span = include(span, p)
174
- case m : untpd.Modifiers =>
175
- span = includeAll(includeAll(span, m.mods), m.annotations)
176
- case xs : :: [_] =>
177
- span = includeAll(span, xs)
178
- case _ =>
179
- }
180
- n += 1
181
- }
182
- if (uniqueId == - 1 ) setId(givenSource.nextId)
183
- span.toSynthetic
184
- }
185
-
186
- /** How many elements to consider when computing the span.
187
- * Normally: all, overridden in Inlined.
188
- */
189
- def relevantElemCount = productArity
190
-
191
129
def contains (that : Positioned ): Boolean = {
192
130
def isParent (x : Any ): Boolean = x match {
193
131
case x : Positioned =>
@@ -222,10 +160,10 @@ abstract class Positioned(implicit @transientParam src: SourceFile) extends Prod
222
160
def check (p : Any ): Unit = p match {
223
161
case p : Positioned =>
224
162
assert(span contains p.span,
225
- s """ position error, parent span does not contain child span
226
- |parent = $this,
163
+ i """ position error, parent span does not contain child span
164
+ |parent = $this # $uniqueId ,
227
165
|parent span = $span,
228
- |child = $p,
166
+ |child = $p # ${p.uniqueId} ,
229
167
|child span = ${p.span}""" .stripMargin)
230
168
p match {
231
169
case tree : Tree if ! tree.isEmpty =>
@@ -242,7 +180,7 @@ abstract class Positioned(implicit @transientParam src: SourceFile) extends Prod
242
180
// ignore transition from last wildcard parameter to body
243
181
case _ =>
244
182
assert(! lastSpan.exists || ! p.span.exists || lastSpan.end <= p.span.start,
245
- s """ position error, child positions overlap or in wrong order
183
+ i """ position error, child positions overlap or in wrong order
246
184
|parent = $this
247
185
|1st child = $lastPositioned
248
186
|1st child span = $lastSpan
0 commit comments