-
Notifications
You must be signed in to change notification settings - Fork 929
/
InputWrapper.scala
318 lines (282 loc) · 13.6 KB
/
InputWrapper.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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
/*
* sbt
* Copyright 2011 - 2018, Lightbend, Inc.
* Copyright 2008 - 2010, Mark Harrah
* Licensed under Apache License 2.0 (see LICENSE)
*/
package sbt
package std
import scala.language.experimental.macros
import scala.annotation.compileTimeOnly
import scala.reflect.macros._
import Def.Initialize
import sbt.internal.util.appmacro.ContextUtil
import sbt.internal.util.complete.Parser
/** Implementation detail. The wrap methods temporarily hold inputs (as a Tree, at compile time) until a task or setting macro processes it. */
object InputWrapper {
/* The names of the wrapper methods should be obscure.
* Wrapper checking is based solely on this name, so it must not conflict with a user method name.
* The user should never see this method because it is compile-time only and only used internally by the task macro system.*/
private[std] final val WrapTaskName = "wrapTask_\u2603\u2603"
private[std] final val WrapInitName = "wrapInit_\u2603\u2603"
private[std] final val WrapInitTaskName = "wrapInitTask_\u2603\u2603"
private[std] final val WrapInitInputName = "wrapInitInputTask_\u2603\u2603"
private[std] final val WrapInputName = "wrapInputTask_\u2603\u2603"
private[std] final val WrapPreviousName = "wrapPrevious_\u2603\u2603"
@compileTimeOnly(
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
)
def wrapTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting."
)
def wrapInit_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be called on a task within a task definition macro, such as :=, +=, ++=, or Def.task."
)
def wrapInitTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask."
)
def wrapInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`value` can only be called on an input task within a task definition macro, such as := or Def.inputTask."
)
def wrapInitInputTask_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
@compileTimeOnly(
"`previous` can only be called on a task within a task or input task definition macro, such as :=, +=, ++=, Def.task, or Def.inputTask."
)
def wrapPrevious_\u2603\u2603[T](@deprecated("unused", "") in: Any): T = implDetailError
private[this] def implDetailError =
sys.error("This method is an implementation detail and should not be referenced.")
private[std] def wrapTask[T: c.WeakTypeTag](c: blackbox.Context)(
ts: c.Expr[Any],
pos: c.Position
): c.Expr[T] =
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapTaskName)(ts, pos)
private[std] def wrapInit[T: c.WeakTypeTag](c: blackbox.Context)(
ts: c.Expr[Any],
pos: c.Position
): c.Expr[T] =
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInitName)(ts, pos)
private[std] def wrapInitTask[T: c.WeakTypeTag](c: blackbox.Context)(
ts: c.Expr[Any],
pos: c.Position
): c.Expr[T] =
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInitTaskName)(ts, pos)
private[std] def wrapInitInputTask[T: c.WeakTypeTag](c: blackbox.Context)(
ts: c.Expr[Any],
pos: c.Position
): c.Expr[T] =
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInitInputName)(ts, pos)
private[std] def wrapInputTask[T: c.WeakTypeTag](c: blackbox.Context)(
ts: c.Expr[Any],
pos: c.Position
): c.Expr[T] =
wrapImpl[T, InputWrapper.type](c, InputWrapper, WrapInputName)(ts, pos)
private[std] def wrapPrevious[T: c.WeakTypeTag](c: blackbox.Context)(
ts: c.Expr[Any],
pos: c.Position
): c.Expr[Option[T]] =
wrapImpl[Option[T], InputWrapper.type](c, InputWrapper, WrapPreviousName)(ts, pos)
/**
* Wraps an arbitrary Tree in a call to the `<s>.<wrapName>` method of this module for later processing by an enclosing macro.
* The resulting Tree is the manually constructed version of:
*
* `c.universe.reify { <s>.<wrapName>[T](ts.splice) }`
*/
def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](
c: blackbox.Context,
s: S,
wrapName: String
)(ts: c.Expr[Any], pos: c.Position)(implicit it: c.TypeTag[s.type]): c.Expr[T] = {
import c.universe.{ Apply => ApplyTree, _ }
import internal.decorators._
val util = new ContextUtil[c.type](c)
val iw = util.singleton(s)
val tpe = c.weakTypeOf[T]
val nme = TermName(wrapName).encodedName
val sel = Select(Ident(iw), nme)
sel.setPos(pos) // need to set the position on Select, because that is where the compileTimeOnly check looks
val tree = ApplyTree(TypeApply(sel, TypeTree(tpe) :: Nil), ts.tree :: Nil)
tree.setPos(ts.tree.pos)
// JZ: I'm not sure why we need to do this. Presumably a caller is wrapping this tree in a
// typed tree *before* handing the whole thing back to the macro engine. One must never splice
// untyped trees under typed trees, as the type checker doesn't descend if `tree.tpe == null`.
//
// #1031 The previous attempt to fix this just set the type on `tree`, which worked in cases when the
// call to `.value` was inside a the task macro and eliminated before the end of the typer phase.
// But, if a "naked" call to `.value` left the typer, the superaccessors phase would freak out when
// if hit the untyped trees, before we could get to refchecks and the desired @compileTimeOnly warning.
val typedTree = c.typecheck(tree)
c.Expr[T](typedTree)
}
def valueMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[T] =
ContextUtil.selectMacroImpl[T](c) { (ts, pos) =>
ts.tree.tpe match {
case tpe if tpe <:< c.weakTypeOf[Initialize[T]] =>
if (c.weakTypeOf[T] <:< c.weakTypeOf[InputTask[_]]) {
c.abort(
pos,
"""`value` is removed from input tasks. Use `evaluated` or `inputTaskValue`.
|See http://www.scala-sbt.org/1.0/docs/Input-Tasks.html for more details.""".stripMargin
)
}
InputWrapper.wrapInit[T](c)(ts, pos)
case tpe if tpe <:< c.weakTypeOf[Initialize[Task[T]]] =>
InputWrapper.wrapInitTask[T](c)(ts, pos)
case tpe if tpe <:< c.weakTypeOf[Task[T]] => InputWrapper.wrapTask[T](c)(ts, pos)
case tpe if tpe <:< c.weakTypeOf[InputTask[T]] => InputWrapper.wrapInputTask[T](c)(ts, pos)
case tpe if tpe <:< c.weakTypeOf[Initialize[InputTask[T]]] =>
InputWrapper.wrapInitInputTask[T](c)(ts, pos)
case tpe => unexpectedType(c)(pos, tpe)
}
}
def inputTaskValueMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[InputTask[T]] =
ContextUtil.selectMacroImpl[InputTask[T]](c) { (ts, pos) =>
InputWrapper.wrapInit[InputTask[T]](c)(ts, pos)
}
def taskValueMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Task[T]] =
ContextUtil.selectMacroImpl[Task[T]](c) { (ts, pos) =>
val tpe = ts.tree.tpe
if (tpe <:< c.weakTypeOf[Initialize[Task[T]]])
InputWrapper.wrapInit[Task[T]](c)(ts, pos)
else
unexpectedType(c)(pos, tpe)
}
/** Translates <task: TaskKey[T]>.previous(format) to Previous.runtime(<task>)(format).value*/
def previousMacroImpl[T: c.WeakTypeTag](
c: blackbox.Context
)(format: c.Expr[sjsonnew.JsonFormat[T]]): c.Expr[Option[T]] = {
import c.universe._
c.macroApplication match {
case a @ Apply(Select(Apply(_, t :: Nil), _), _) =>
if (t.tpe <:< c.weakTypeOf[TaskKey[T]]) {
val tsTyped = c.Expr[TaskKey[T]](t)
val newTree = c.universe.reify { Previous.runtime[T](tsTyped.splice)(format.splice) }
wrapPrevious[T](c)(newTree, a.pos)
} else
unexpectedType(c)(a.pos, t.tpe)
case x => ContextUtil.unexpectedTree(x)
}
}
private def unexpectedType(c: blackbox.Context)(pos: c.Position, tpe: c.Type) =
c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.widen}")
}
sealed abstract class MacroTaskValue[T] {
@compileTimeOnly(
"`taskValue` can only be used within a setting macro, such as :=, +=, ++=, or Def.setting."
)
def taskValue: Task[T] = macro InputWrapper.taskValueMacroImpl[T]
}
sealed abstract class MacroValue[T] {
@compileTimeOnly(
"`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting."
)
def value: T = macro InputWrapper.valueMacroImpl[T]
}
sealed abstract class ParserInput[T] {
@compileTimeOnly(
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
)
def parsed: T = macro ParserInput.parsedMacroImpl[T]
}
sealed abstract class InputEvaluated[T] {
@compileTimeOnly(
"`evaluated` can only be used within an input task macro, such as := or Def.inputTask."
)
def evaluated: T = macro InputWrapper.valueMacroImpl[T]
@compileTimeOnly(
"`inputTaskValue` can only be used within an input task macro, such as := or Def.inputTask."
)
def inputTaskValue: InputTask[T] = macro InputWrapper.inputTaskValueMacroImpl[T]
}
sealed abstract class ParserInputTask[T] {
@compileTimeOnly(
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
)
def parsed: Task[T] = macro ParserInput.parsedInputMacroImpl[T]
}
sealed abstract class MacroPrevious[T] {
@compileTimeOnly(
"`previous` can only be used within a task macro, such as :=, +=, ++=, or Def.task."
)
def previous(implicit format: sjsonnew.JsonFormat[T]): Option[T] =
macro InputWrapper.previousMacroImpl[T]
}
/** Implementation detail. The wrap method temporarily holds the input parser (as a Tree, at compile time) until the input task macro processes it. */
object ParserInput {
/* The name of the wrapper method should be obscure.
* Wrapper checking is based solely on this name, so it must not conflict with a user method name.
* The user should never see this method because it is compile-time only and only used internally by the task macros.*/
private[std] val WrapName = "parser_\u2603\u2603"
private[std] val WrapInitName = "initParser_\u2603\u2603"
@compileTimeOnly(
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
)
def parser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T =
sys.error("This method is an implementation detail and should not be referenced.")
@compileTimeOnly(
"`parsed` can only be used within an input task macro, such as := or Def.inputTask."
)
def initParser_\u2603\u2603[T](@deprecated("unused", "") i: Any): T =
sys.error("This method is an implementation detail and should not be referenced.")
private[std] def wrap[T: c.WeakTypeTag](
c: blackbox.Context
)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
InputWrapper.wrapImpl[T, ParserInput.type](c, ParserInput, WrapName)(ts, pos)
private[std] def wrapInit[T: c.WeakTypeTag](
c: blackbox.Context
)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] =
InputWrapper.wrapImpl[T, ParserInput.type](c, ParserInput, WrapInitName)(ts, pos)
private[std] def inputParser[T: c.WeakTypeTag](
c: blackbox.Context
)(t: c.Expr[InputTask[T]]): c.Expr[State => Parser[Task[T]]] =
c.universe.reify(t.splice.parser)
def parsedInputMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[Task[T]] =
ContextUtil.selectMacroImpl[Task[T]](c) { (p, pos) =>
p.tree.tpe match {
case tpe if tpe <:< c.weakTypeOf[InputTask[T]] => wrapInputTask[T](c)(p.tree, pos)
case tpe if tpe <:< c.weakTypeOf[Initialize[InputTask[T]]] =>
wrapInitInputTask[T](c)(p.tree, pos)
case tpe => unexpectedType(c)(pos, tpe, "parsedInputMacroImpl")
}
}
private def wrapInputTask[T: c.WeakTypeTag](
c: blackbox.Context
)(tree: c.Tree, pos: c.Position) = {
val e = c.Expr[InputTask[T]](tree)
wrap[Task[T]](c)(inputParser(c)(e), pos)
}
private def wrapInitInputTask[T: c.WeakTypeTag](
c: blackbox.Context
)(tree: c.Tree, pos: c.Position) = {
val e = c.Expr[Initialize[InputTask[T]]](tree)
wrapInit[Task[T]](c)(c.universe.reify { Def.toIParser(e.splice) }, pos)
}
/** Implements `Parser[T].parsed` by wrapping the Parser with the ParserInput wrapper.*/
def parsedMacroImpl[T: c.WeakTypeTag](c: blackbox.Context): c.Expr[T] =
ContextUtil.selectMacroImpl[T](c) { (p, pos) =>
p.tree.tpe match {
case tpe if tpe <:< c.weakTypeOf[Parser[T]] => wrapParser[T](c)(p.tree, pos)
case tpe if tpe <:< c.weakTypeOf[State => Parser[T]] => wrap[T](c)(p, pos)
case tpe if tpe <:< c.weakTypeOf[Initialize[Parser[T]]] =>
wrapInitParser[T](c)(p.tree, pos)
case tpe if tpe <:< c.weakTypeOf[Initialize[State => Parser[T]]] => wrapInit[T](c)(p, pos)
case tpe => unexpectedType(c)(pos, tpe, "parsedMacroImpl")
}
}
private def wrapParser[T: c.WeakTypeTag](c: blackbox.Context)(tree: c.Tree, pos: c.Position) = {
val e = c.Expr[Parser[T]](tree)
wrap[T](c)(c.universe.reify { Def.toSParser(e.splice) }, pos)
}
private def wrapInitParser[T: c.WeakTypeTag](
c: blackbox.Context
)(tree: c.Tree, pos: c.Position) = {
val e = c.Expr[Initialize[Parser[T]]](tree)
val es = c.universe.reify { Def.toISParser(e.splice) }
wrapInit[T](c)(es, pos)
}
private def unexpectedType(c: blackbox.Context)(pos: c.Position, tpe: c.Type, label: String) =
c.abort(pos, s"Internal sbt error. Unexpected type ${tpe.dealias} in $label.")
}