diff --git a/main/settings/src/main/scala/sbt/Structure.scala b/main/settings/src/main/scala/sbt/Structure.scala index 9e31f71c1..bdc422b49 100644 --- a/main/settings/src/main/scala/sbt/Structure.scala +++ b/main/settings/src/main/scala/sbt/Structure.scala @@ -16,6 +16,7 @@ package sbt import Types._ import language.experimental.macros + import reflect.internal.annotations.compileTimeOnly sealed trait Scoped { def scope: Scope; val key: AttributeKey[_] } diff --git a/main/settings/src/main/scala/sbt/std/TaskMacro.scala b/main/settings/src/main/scala/sbt/std/TaskMacro.scala index da6e0bcc4..5cff4797e 100644 --- a/main/settings/src/main/scala/sbt/std/TaskMacro.scala +++ b/main/settings/src/main/scala/sbt/std/TaskMacro.scala @@ -10,6 +10,7 @@ package std import language.experimental.macros import scala.reflect._ import reflect.macros._ + import reflect.internal.annotations.compileTimeOnly /** Instance for the monad/applicative functor for plain Tasks. */ object TaskInstance extends MonadInstance @@ -220,39 +221,43 @@ object TaskMacro } 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 std.TaskMacro.valueMacroImpl[T] } def valueMacroImpl[T: c.WeakTypeTag](c: Context): c.Expr[T] = - ContextUtil.selectMacroImpl[T,Any](c)( ts => InputWrapper.wrapKey[T](c)(ts) ) + ContextUtil.selectMacroImpl[T,Any](c)( (ts,pos) => InputWrapper.wrapKey[T](c)(ts,pos) ) sealed abstract class RawParserInput[T] { + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") def parsed: T = macro std.TaskMacro.rawParserMacro[T] } sealed abstract class InitParserInput[T] { + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") def parsed: T = macro std.TaskMacro.initParserMacro[T] } sealed abstract class StateParserInput[T] { + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") def parsed: T = macro std.TaskMacro.stateParserMacro[T] } /** Implements `Parser[T].parsed` by wrapping the Parser with the ParserInput wrapper.*/ def rawParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] = - ContextUtil.selectMacroImpl[T, Parser[T]](c) { p => c.universe.reify { - ParserInput.parser_\u2603\u2603[T](InputTask.parserAsInput(p.splice)) - }} + ContextUtil.selectMacroImpl[T, Parser[T]](c) { (p,pos) => + ParserInput.wrap[T](c)(c.universe.reify { InputTask.parserAsInput(p.splice) }, pos) + } /** Implements the `Initialize[Parser[T]].parsed` macro by wrapping the input with the ParserInput wrapper.*/ def initParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] = - ContextUtil.selectMacroImpl[T, Initialize[Parser[T]]](c) { t => c.universe.reify { - ParserInput.parser_\u2603\u2603[T](InputTask.initParserAsInput(t.splice)) - }} + ContextUtil.selectMacroImpl[T, Initialize[Parser[T]]](c) { (t,pos) => + ParserInput.wrap[T](c)(c.universe.reify { InputTask.initParserAsInput(t.splice) }, pos) + } /** Implements the `Initialize[State => Parser[T]].parsed` macro by wrapping the input with the ParserInput wrapper.*/ def stateParserMacro[T: c.WeakTypeTag](c: Context): c.Expr[T] = - ContextUtil.selectMacroImpl[T, Initialize[State => Parser[T]]](c) { t => c.universe.reify { - ParserInput.parser_\u2603\u2603[T](t.splice) - }} + ContextUtil.selectMacroImpl[T, Initialize[State => Parser[T]]](c) { (t,pos) => + ParserInput.wrap[T](c)(t, pos) + } /** Implementation detail. The method temporarily holds the input parser (as a Tree, at compile time) until the input task macro processes it. */ object ParserInput { @@ -261,8 +266,11 @@ object TaskMacro * The user should never see this method because it is compile-time only and only used internally by the task macros.*/ val WrapName = "parser_\u2603\u2603" - // This method should be annotated as compile-time only when that feature is implemented + @compileTimeOnly("`parsed` can only be used within an input task macro, such as := or Def.inputTask.") def parser_\u2603\u2603[T](i: Initialize[State => Parser[T]]): T = error("This method is an implementation detail and should not be referenced.") + + def wrap[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = + InputWrapper.wrapImpl[T,ParserInput.type](c, ParserInput, WrapName)(ts, pos) } def inputTaskMacroImpl[T: c.WeakTypeTag](c: Context)(t: c.Expr[T]): c.Expr[Initialize[InputTask[T]]] = diff --git a/sbt/src/sbt-test/actions/compile-time-only/build.sbt b/sbt/src/sbt-test/actions/compile-time-only/build.sbt new file mode 100644 index 000000000..12bdd2f9a --- /dev/null +++ b/sbt/src/sbt-test/actions/compile-time-only/build.sbt @@ -0,0 +1 @@ +libraryDependencies += "org.scala-sbt" % "sbt" % sbtVersion.value \ No newline at end of file diff --git a/sbt/src/sbt-test/actions/compile-time-only/changes/A1.scala b/sbt/src/sbt-test/actions/compile-time-only/changes/A1.scala new file mode 100644 index 000000000..ec128cbe4 --- /dev/null +++ b/sbt/src/sbt-test/actions/compile-time-only/changes/A1.scala @@ -0,0 +1,14 @@ +import sbt._ +import Def.Initialize +import complete.{DefaultParsers, Parser} + +object A { + val x1: Initialize[Task[Int]] = Def.task { 3 } + val y1 = Def.task { x1.value } + + val x2: Initialize[Parser[Int]] = Def.setting { DefaultParsers.success(3) } + val y2 = Def.inputTask { x1.value + x2.parsed } + + val x3: Initialize[Int] = Def.setting { 3 } + val y3 = Def.setting { x3.value } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/actions/compile-time-only/changes/A2.scala b/sbt/src/sbt-test/actions/compile-time-only/changes/A2.scala new file mode 100644 index 000000000..b2d5e5148 --- /dev/null +++ b/sbt/src/sbt-test/actions/compile-time-only/changes/A2.scala @@ -0,0 +1,7 @@ +import sbt._ +import Def.Initialize + +object A { + val x1: Initialize[Task[Int]] = Def.task { 3 } + val y1 = x1.value +} \ No newline at end of file diff --git a/sbt/src/sbt-test/actions/compile-time-only/changes/A3.scala b/sbt/src/sbt-test/actions/compile-time-only/changes/A3.scala new file mode 100644 index 000000000..65b4a7804 --- /dev/null +++ b/sbt/src/sbt-test/actions/compile-time-only/changes/A3.scala @@ -0,0 +1,8 @@ +import sbt._ +import Def.Initialize +import complete.{DefaultParsers, Parser} + +object A { + val x1: Initialize[Parser[Int]] = Def.setting { DefaultParsers.success(3) } + val y1 = Def.task { x1.parsed } +} \ No newline at end of file diff --git a/sbt/src/sbt-test/actions/compile-time-only/test b/sbt/src/sbt-test/actions/compile-time-only/test new file mode 100644 index 000000000..43b7af6f1 --- /dev/null +++ b/sbt/src/sbt-test/actions/compile-time-only/test @@ -0,0 +1,8 @@ +$ copy-file changes/A1.scala A.scala +> compile + +$ copy-file changes/A2.scala A.scala +-> compile + +$ copy-file changes/A3.scala A.scala +-> compile \ No newline at end of file diff --git a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala index 85a2aed50..41a52003f 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/ContextUtil.scala @@ -20,11 +20,11 @@ object ContextUtil { * * Given `myImplicitConversion(someValue).extensionMethod`, where `extensionMethod` is a macro that uses this * method, the result of this method is `f()`. */ - def selectMacroImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: Context)(f: c.Expr[S] => c.Expr[T]): c.Expr[T] = + def selectMacroImpl[T: c.WeakTypeTag, S: c.WeakTypeTag](c: Context)(f: (c.Expr[S], c.Position) => c.Expr[T]): c.Expr[T] = { import c.universe._ c.macroApplication match { - case Select(Apply(_, t :: Nil), _) => f( c.Expr[S](t) ) + case s @ Select(Apply(_, t :: Nil), tp) => f( c.Expr[S](t), s.pos ) case x => unexpectedTree(x) } } diff --git a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala index f70941dd0..49b8bb71a 100644 --- a/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala +++ b/util/appmacro/src/main/scala/sbt/appmacro/Instance.scala @@ -27,7 +27,9 @@ trait MonadInstance extends Instance import scala.reflect._ import macros._ + import reflect.internal.annotations.compileTimeOnly +// This needs to be moved to main/settings object InputWrapper { /** The name of the wrapper method should be obscure. @@ -35,22 +37,26 @@ object InputWrapper * The user should never see this method because it is compile-time only and only used internally by the task macro system.*/ final val WrapName = "wrap_\u2603\u2603" - // This method should be annotated as compile-time only when that feature is implemented + @compileTimeOnly("`value` can only be used within a task or setting macro, such as :=, +=, ++=, Def.task, or Def.setting.") def wrap_\u2603\u2603[T](in: Any): T = error("This method is an implementation detail and should not be referenced.") - /** Wraps an arbitrary Tree in a call to the `wrap` method of this module for later processing by an enclosing macro. + def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any], pos: c.Position): c.Expr[T] = wrapImpl[T,InputWrapper.type](c, InputWrapper, WrapName)(ts, pos) + + /** Wraps an arbitrary Tree in a call to the `.` method of this module for later processing by an enclosing macro. * The resulting Tree is the manually constructed version of: * - * `c.universe.reify { InputWrapper.[T](ts.splice) }` + * `c.universe.reify { .[T](ts.splice) }` */ - def wrapKey[T: c.WeakTypeTag](c: Context)(ts: c.Expr[Any]): c.Expr[T] = + def wrapImpl[T: c.WeakTypeTag, S <: AnyRef with Singleton](c: 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,_} val util = new ContextUtil[c.type](c) - val iw = util.singleton(InputWrapper) + val iw = util.singleton(s) val tpe = c.weakTypeOf[T] - val nme = newTermName(WrapName).encoded - val tree = ApplyTree(TypeApply(Select(Ident(iw), nme), TypeTree(tpe) :: Nil), ts.tree :: Nil) + val nme = newTermName(wrapName).encoded + 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) c.Expr[T](tree) }