@@ -26,6 +26,19 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Ad
26
26
(addrDelta << 3 ) | (toInt(hasStartDelta) << 2 ) | (toInt(hasEndDelta) << 1 ) | toInt(hasPoint)
27
27
}
28
28
29
+ /** The result type of `matchDegree`, which encodes the kind of match between
30
+ * the position of a Positioned item and the inferred position computed by Positioned#setInitialPos.
31
+ */
32
+ private type MatchDegree = Int
33
+ private final val Unknown = 0 // Nothing known yet
34
+ private final val StartMatch = 1 // We know that at least one element matches `start`
35
+ private final val EndMatch = 2 // We know that at least one element matches `end`
36
+ private final val SourceMatch = 4 // We know that the first element matches `source`
37
+ private final val SpanMismatch = 8 // We know that the span is different from initialSpan
38
+ private final val SourceMismatch = 16 // We know the source is different from the first element
39
+ private final val SpanMatch = StartMatch | EndMatch
40
+ private final val AllMatch = SpanMatch | SourceMatch
41
+
29
42
def picklePositions (roots : List [Tree ])(implicit ctx : Context ): Unit = {
30
43
var lastIndex = 0
31
44
var lastSpan = Span (0 , 0 )
@@ -66,30 +79,91 @@ class PositionPickler(pickler: TastyPickler, addrOfTree: untpd.Tree => Option[Ad
66
79
case _ => false
67
80
}
68
81
82
+ // val msgs = new mutable.ListBuffer[String]
83
+
84
+ /** The degree to which the source position of `x` matches the initial position computed
85
+ * from its elements.
86
+ * @pre The span of `x` exists
87
+ */
88
+ def matchDegree (x : Positioned ): MatchDegree = {
89
+ val src = x.source
90
+ val start = x.span.start
91
+ val end = x.span.end
92
+
93
+ def checkElem (acc : MatchDegree , elem : Any ): MatchDegree = elem match {
94
+ case _ : Trees .TypeTree [_] =>
95
+ // TypeTrees contribute nothing since they are pickled as types
96
+ acc
97
+ case elem : Positioned =>
98
+ val espan = elem.span
99
+ if (! espan.exists) acc // elements without position don't contribute
100
+ else {
101
+ val esrc = elem.source
102
+ if (! esrc.exists) acc // elements without source don't contribute
103
+ else if (esrc `ne` src)
104
+ if ((acc & SourceMatch ) == 0 ) SourceMismatch // first element source is different -> no match
105
+ else acc // subsequent elements with different source don't contribute
106
+ else {
107
+ var matches = acc | SourceMatch
108
+ val estart = espan.start
109
+ val eend = espan.end
110
+ if (estart == start) matches |= StartMatch
111
+ if (eend == end) matches |= EndMatch
112
+ if (estart < start || eend > end) matches |= SpanMismatch
113
+ matches
114
+ }
115
+ }
116
+ case elem : List [_] =>
117
+ checkElems(acc, elem)
118
+ case m : untpd.Modifiers =>
119
+ checkElems(checkElems(acc, m.mods), m.annotations)
120
+ case _ =>
121
+ acc
122
+ }
123
+ def checkElems (acc : MatchDegree , elems : List [Any ]): MatchDegree = elems match {
124
+ case elem :: elems1 => checkElems(checkElem(acc, elem), elems1)
125
+ case nil => acc
126
+ }
127
+
128
+ val limit = x.relevantElemCount
129
+ var n = 0
130
+ var acc : MatchDegree = Unknown
131
+ while (n < limit) {
132
+ acc = checkElem(acc, x.productElement(n))
133
+ n += 1
134
+ }
135
+ acc
136
+ }
137
+
69
138
def traverse (x : Any , current : SourceFile ): Unit = x match {
70
139
case x : untpd.Tree =>
71
140
var sourceFile = current
72
- val span = if (x.isInstanceOf [untpd.MemberDef ]) x.span else x.span.toSynthetic
73
- val sourceChange =
74
- x.source != x.elemsSource ||
75
- ! x.elemsSource.exists && x.source != current
76
- if (span.exists && (
77
- span != x.initialSpan(ignoreTypeTrees = true ).toSynthetic ||
78
- sourceChange ||
79
- alwaysNeedsPos(x))) {
80
- addrOfTree(x) match {
81
- case Some (addr) if ! pickledIndices.contains(addr.index) || sourceChange =>
82
- // we currently do not share trees when unpickling, so if one path to a tree contains
83
- // a source change while another does not, we have to record the position of the tree twice
84
- // in order not to miss the source change. Test case is t3232a.scala.
85
- // println(i"pickling $x with $span at $addr")
86
- pickleDeltas(addr.index, span)
87
- if (x.source != current) {
88
- pickleSource(x.source)
89
- sourceFile = x.source
90
- }
91
- case _ =>
92
- // println(i"no address for $x")
141
+ if (x.span.exists) {
142
+ val mdegree = matchDegree(x)
143
+ val sourceChange =
144
+ mdegree == SourceMismatch || // source different from initial source, or
145
+ (mdegree & SourceMatch ) == 0 && // initial source unknown, and
146
+ (x.source `ne` current) // source different from context
147
+ val needPos =
148
+ (mdegree & SpanMismatch ) != 0 || // initial span exceeds current in some direction, or
149
+ (mdegree & SpanMatch ) != SpanMatch || // initial span smaller than current, or
150
+ sourceChange || // source needs to be specified, or
151
+ alwaysNeedsPos(x) // always needs position anyway
152
+ if (needPos) {
153
+ addrOfTree(x) match {
154
+ case Some (addr) if ! pickledIndices.contains(addr.index) || sourceChange =>
155
+ // we currently do not share trees when unpickling, so if one path to a tree contains
156
+ // a source change while another does not, we have to record the position of the tree twice
157
+ // in order not to miss the source change. Test case is t3232a.scala.
158
+ // println(i"pickling $x with $span at $addr")
159
+ pickleDeltas(addr.index, x.span)
160
+ if (x.source != current) {
161
+ pickleSource(x.source)
162
+ sourceFile = x.source
163
+ }
164
+ case _ =>
165
+ // println(i"no address for $x")
166
+ }
93
167
}
94
168
}
95
169
// else if (x.span.exists) println(i"skipping $x")
0 commit comments