-
Notifications
You must be signed in to change notification settings - Fork 29
/
ResetEverywhere.scala
156 lines (137 loc) · 5.38 KB
/
ResetEverywhere.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
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
152
153
154
155
156
package com.thoughtworks.dsl.compilerplugins
import com.thoughtworks.dsl.Dsl.{ResetAnnotation, nonTypeConstraintReset, shift}
import scala.reflect.internal.FatalError
import scala.tools.nsc.plugins.{Plugin, PluginComponent}
import scala.tools.nsc.transform.Transform
import scala.tools.nsc.typechecker.ContextMode
import scala.tools.nsc.{Global, Mode, Phase}
/** A Scala compiler plug-in to enable [[Dsl.Keyword#unary_$bang !-notation]] for every methods and functions.
*
* Add the following setting in your `build.sbt` to enable this plug-in.
*
* `<pre>
* // build.sbt
* addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-reseteverywhere" % "latest.release")
* </pre>`
*
* @note Once this [[ResetEverywhere]] plug-in is enabled,
* the `@[[Dsl.reset reset]]` annotations are added to class fields, every methods and every functions automatically.
* Some other macros or compiler plug-ins may conflict with those `@[[Dsl.reset reset]]` annotations.
*
* @author 杨博 (Yang Bo)
*/
final class ResetEverywhere(override val global: Global) extends Plugin {
import global._
import global.analyzer._
private var active = true
private def deactAnalyzerPlugins[A](run: => A): A = {
synchronized {
active = false
try {
run
} finally {
active = true
}
}
}
private type CpsAttachment = (Tree => Tree) => Tree
private var nonTypeConstraintResetSymbol: Symbol = _ // = symbolOf[nonTypeConstraintReset]
override def init(options: List[String], error: String => Unit): Boolean = {
super.init(options, error) && {
try {
nonTypeConstraintResetSymbol = symbolOf[nonTypeConstraintReset]
true
} catch {
case e: ScalaReflectionException =>
error("""This compiler plug-in requires the runtime library:
libraryDependencies += "com.thoughtworks.dsl" %% "dsl" % "latest.release"
""")
false
}
}
}
val name: String = "ResetEverywhere"
private final class ResetAnnotationCreator extends PluginComponent with Transform {
val global: ResetEverywhere.this.global.type = ResetEverywhere.this.global
val phaseName: String = "ResetAnnotationCreator"
val runsAfter = "parser" :: Nil
override val runsBefore = "namer" :: Nil
protected def newTransformer(unit: CompilationUnit): Transformer = new Transformer {
private def annotatedReset(tree: Tree) = {
if (tree.isEmpty) {
tree
} else {
Annotated(q"new $nonTypeConstraintResetSymbol()", transform(tree))
}
}
private def typedReset(tree: Tree, typeTree: Tree) = {
if (tree.isEmpty) {
tree
} else if (typeTree.isEmpty) {
Annotated(q"new $nonTypeConstraintResetSymbol()", transform(tree))
} else {
Annotated(q"new $nonTypeConstraintResetSymbol()", Typed(transform(tree), typeTree))
}
}
private def transformRootValDef(tree: ValDef) = {
val ValDef(mods, name, tpt, rhs) = tree
treeCopy.ValDef(tree, mods, name, tpt, typedReset(rhs, tpt))
}
override def transformTemplate(tree: Template): Template = {
val Template(parents, self, body) = tree
treeCopy.Template(
tree,
parents,
self,
body.mapConserve {
case valDef: ValDef if !valDef.mods.isParamAccessor =>
transformRootValDef(valDef)
case defDef: DefDef if defDef.mods.hasFlag(Flag.MACRO) =>
defDef
case initializer: TermTree =>
annotatedReset(initializer)
case stat =>
transform(stat)
}
)
}
private def annotateArgsAsReset(tree: Tree): Tree = {
tree match {
case tree: Apply =>
treeCopy.Apply(tree, annotateArgsAsReset(tree.fun), tree.args.mapConserve(annotatedReset))
case fun =>
fun
}
}
override def transform(tree: global.Tree): global.Tree = {
tree match {
case tree: TypeTree =>
tree
case Typed(expr, tpt) =>
treeCopy.Typed(tree, transform(expr), tpt)
case Function(vparams, body) =>
treeCopy.Function(tree, vparams, annotatedReset(body))
case DefDef(mods, name, tparams, vparamss, tpt, rhs)
if name != termNames.CONSTRUCTOR && name != termNames.MIXIN_CONSTRUCTOR && rhs.nonEmpty && !mods
.hasAnnotationNamed(definitions.TailrecClass.name) =>
treeCopy.DefDef(tree, mods, name, tparams, transformValDefss(vparamss), tpt, typedReset(rhs, tpt))
case valDef: ValDef if valDef.mods.hasDefault =>
transformRootValDef(valDef)
case Match(EmptyTree, cases) =>
treeCopy.Match(tree, EmptyTree, cases.mapConserve {
case caseDef @ CaseDef(pat, guard, body) =>
treeCopy.CaseDef(caseDef, pat, guard, annotatedReset(body))
})
case q"${Ident(termNames.CONSTRUCTOR)}(...$argss)" =>
annotateArgsAsReset(tree)
case q"super.${termNames.CONSTRUCTOR}(...$argss)" =>
annotateArgsAsReset(tree)
case _ =>
super.transform(tree)
}
}
}
}
val description: String = "Add @reset annotation for every methods and functions automatically"
val components: List[PluginComponent] = List(new ResetAnnotationCreator)
}