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
|
package dotty.tools.dotc
package ast
import core.Contexts.Context
import core.Decorators._
import util.Positions._
import Trees.{MemberDef, DefTree}
/** Utility functions to go from typed to untyped ASTs */
object NavigateAST {
/** The untyped tree corresponding to typed tree `tree` in the compilation
* unit specified by `ctx`
*/
def toUntyped(tree: tpd.Tree)(implicit ctx: Context): untpd.Tree =
untypedPath(tree, exactMatch = true) match {
case (utree: untpd.Tree) :: _ =>
utree
case _ =>
val loosePath = untypedPath(tree, exactMatch = false)
throw new
Error(i"""no untyped tree for $tree, pos = ${tree.pos}
|best matching path =\n$loosePath%\n====\n%
|path positions = ${loosePath.map(_.pos)}""")
}
/** The reverse path of untyped trees starting with a tree that closest matches
* `tree` and ending in the untyped tree at the root of the compilation unit
* specified by `ctx`.
* @param exactMatch If `true`, the path must start with a node that exactly
* matches `tree`, or `Nil` is returned.
* If `false` the path might start with a node enclosing
* the logical position of `tree`.
* Note: A complication concerns member definitions. ValDefs and DefDefs
* have after desugaring a position that spans just the name of the symbol being
* defined and nothing else. So we look instead for an untyped tree approximating the
* envelope of the definition, and declare success if we find another DefTree.
*/
def untypedPath(tree: tpd.Tree, exactMatch: Boolean = false)(implicit ctx: Context): List[Positioned] =
tree match {
case tree: MemberDef[_] =>
untypedPath(tree.pos) match {
case path @ (last: DefTree[_]) :: _ => path
case path if !exactMatch => path
case _ => Nil
}
case _ =>
untypedPath(tree.pos) match {
case (path @ last :: _) if last.pos == tree.pos || !exactMatch => path
case _ => Nil
}
}
/** The reverse part of the untyped root of the compilation unit of `ctx` to
* position `pos`.
*/
def untypedPath(pos: Position)(implicit ctx: Context): List[Positioned] =
pathTo(pos, ctx.compilationUnit.untpdTree)
/** The reverse path from node `from` to the node that closest encloses position `pos`,
* or `Nil` if no such path exists. If a non-empty path is returned it starts with
* the node closest enclosing `pos` and ends with `from`.
*/
def pathTo(pos: Position, from: Positioned)(implicit ctx: Context): List[Positioned] = {
def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = {
while (it.hasNext) {
val path1 = it.next match {
case p: Positioned => singlePath(p, path)
case xs: List[_] => childPath(xs.iterator, path)
case _ => path
}
if (path1 ne path) return path1
}
path
}
def singlePath(p: Positioned, path: List[Positioned]): List[Positioned] =
if (p.pos contains pos) childPath(p.productIterator, p :: path)
else path
singlePath(from, Nil)
}
}
|