/
InternalTrees.scala
124 lines (99 loc) · 4.56 KB
/
InternalTrees.scala
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
package scala.meta
package internal
package trees
import scala.meta.inputs._
import scala.meta.internal.prettyprinters.TreeSyntax
import scala.meta.tokenizers._
import scala.meta.tokens._
import scala.meta.tokens.Token._
import scala.meta.trees.Error
import scala.meta.trees.Origin
// NOTE: Methods that start with "private" are NOT intended to be called outside scala.meta.
// Calling these methods from hosts will compile (because hosts are in meta), but is strongly discouraged.
trait InternalTree extends Product {
self: Tree =>
// ==============================================================
// Pieces of internal state of scala.meta ASTs.
// Some nodes define all of them as fields, some nodes define only a subset.
// However, all nodes have corresponding defs to simplify uniform treatment.
// Everyone except for scala.meta's core should be using "private"-less versions of these methods,
// because those are only available on appropriate trees.
// ==============================================================
private[meta] def privatePrototype: Tree
private[meta] def privateParent: Tree
private[meta] def privateCopy(
prototype: Tree = this,
parent: Tree = privateParent,
destination: String = null,
origin: Origin = origin
): Tree
// ==============================================================
// Getters for pieces of internal state defined above.
// ==============================================================
def parent: Option[Tree] = Option(privateParent)
// NOTE: InternalTree inherits traditional productXXX methods from Product
// and also adds a new method called productFields.
// def productPrefix: String
// def productArity: Int
// def productElement(n: Int): Any
// def productIterator: Iterator[Any]
def productFields: List[String]
def origin: Origin
def pos: Position = origin.position
// ==============================================================
// Tokens
// ==============================================================
@deprecated("dialect is ignored, use parameterless `tokens` method", "4.9.0")
final def tokens(dialect: Dialect): Tokens = tokens
def tokens: Tokens = tokensOpt.getOrElse {
throw new Error.MissingDialectException(
"Tree missing a dialect; update root tree `.withDialectIfRootAndNotSet` first, or call `.tokenizeFor`."
)
}
def tokenizeFor(dialect: Dialect): Tokens =
if (origin.dialectOpt.contains(dialect)) tokensOpt.get else tokenizeForDialect(dialect)
private lazy val tokensOpt: Option[Tokens] =
origin match {
case x: Origin.Parsed => Some(x.tokens)
case _ => origin.dialectOpt.map(tokenizeForDialect)
}
private def tokenizeForDialect(dialect: Dialect): Tokens =
this match {
case Lit.String(value) =>
val input = Input.VirtualFile("<InternalTrees.tokens>", value)
Tokens(Array(Constant.String(input, dialect, 0, value.length, value)))
case _ => dialect(textAsInput(dialect)).tokenize.get
}
private[meta] def textAsInput(implicit dialect: Dialect): Input =
Input.VirtualFile("<InternalTrees.text>", printSyntaxFor(dialect))
// ==============================================================
// Text or syntax
// ==============================================================
def text: String = textOpt.getOrElse {
throw new Error.MissingDialectException(
"Tree missing a dialect; update root tree `.withDialectIfRootAndNotSet` first, or call `.printSyntaxFor`."
)
}
def printSyntaxFor(dialect: Dialect): String =
if (origin.dialectOpt.contains(dialect)) textOpt.get else reprintSyntax(dialect)
private def reprintSyntax(dialect: Dialect): String =
TreeSyntax.reprint(this)(dialect).toString
private lazy val textOpt: Option[String] =
origin match {
case x: Origin.Parsed => Some(x.position.text)
case _ => origin.dialectOpt.map(reprintSyntax)
}
// ==============================================================
// Intellij-friendly stubs.
// See https://github.com/scalameta/scalameta/pull/907#discussion_r120090447.
// ==============================================================
protected def checkFields(x: Any): Unit = ()
protected def checkParent(x: Any): Unit = ()
}
trait InternalTreeXtensions {
private[meta] implicit class XtensionOriginTree[T <: Tree](tree: T) {
def withOrigin(origin: Origin): T = tree.privateCopy(origin = origin).asInstanceOf[T]
def withDialectNonRecursiveIfNotSet(implicit dialect: Dialect): T =
if (tree.origin ne Origin.None) tree else withOrigin(Origin.DialectOnly(dialect))
}
}