summaryrefslogtreecommitdiff
path: root/cask
diff options
context:
space:
mode:
authorLi Haoyi <haoyi.sg@gmail.com>2018-07-26 11:58:04 +0800
committerLi Haoyi <haoyi.sg@gmail.com>2018-07-26 11:58:04 +0800
commitcdc254b077235efc8a1ad1158d2a0245730262c6 (patch)
tree9358e692a252cd7a14b1ebd8a1c626983a935977 /cask
parent9326dfd92a9fc120c2cdd892575486193281fb26 (diff)
downloadcask-cdc254b077235efc8a1ad1158d2a0245730262c6.tar.gz
cask-cdc254b077235efc8a1ad1158d2a0245730262c6.tar.bz2
cask-cdc254b077235efc8a1ad1158d2a0245730262c6.zip
Provide proper compilation error messages around mis-used decorators and add compileError tests
Diffstat (limited to 'cask')
-rw-r--r--cask/src/cask/internal/Router.scala25
-rw-r--r--cask/src/cask/main/Routes.scala11
-rw-r--r--cask/test/src/test/cask/FailureTests.scala53
3 files changed, 79 insertions, 10 deletions
diff --git a/cask/src/cask/internal/Router.scala b/cask/src/cask/internal/Router.scala
index 460c5e4..98a10d1 100644
--- a/cask/src/cask/internal/Router.scala
+++ b/cask/src/cask/internal/Router.scala
@@ -187,7 +187,7 @@ class Router[C <: Context](val c: C) {
}
}
- def extractMethod(meth: MethodSymbol,
+ def extractMethod(method: MethodSymbol,
curCls: c.universe.Type,
wrapOutput: c.Tree => c.Tree,
ctx: c.Type,
@@ -205,14 +205,19 @@ class Router[C <: Context](val c: C) {
} yield l.value.value.asInstanceOf[String]
(remaining, docValues.headOption)
}
- val (_, methodDoc) = getDocAnnotation(meth.annotations)
+ val (_, methodDoc) = getDocAnnotation(method.annotations)
val argListSymbol = q"${c.fresh[TermName]("argsList")}"
- val argData = for(argListIndex <- 0 until meth.paramLists.length) yield{
+ if (method.paramLists.length != argReaders.length) c.abort(
+ method.pos,
+ s"Endpoint ${method.name}'s number of parameter lists (${method.paramLists.length}) " +
+ s"doesn't match number of decorators (${argReaders.length})"
+ )
+ val argData = for(argListIndex <- 0 until method.paramLists.length) yield{
val annotDeserializeType = annotDeserializeTypes(argListIndex)
val argReader = argReaders(argListIndex)
- val flattenedArgLists = meth.paramss(argListIndex)
+ val flattenedArgLists = method.paramss(argListIndex)
def hasDefault(i: Int) = {
- val defaultName = s"${meth.name}$$default$$${i + 1}"
+ val defaultName = s"${method.name}$$default$$${i + 1}"
if (curCls.members.exists(_.name.toString == defaultName)) Some(defaultName)
else None
}
@@ -275,7 +280,7 @@ class Router[C <: Context](val c: C) {
"""
val reader =
- if (vararg) c.abort(meth.pos, "Varargs are not supported in cask routes")
+ if (vararg) c.abort(method.pos, "Varargs are not supported in cask routes")
else
q"""
cask.internal.Router.makeReadCall(
@@ -285,7 +290,7 @@ class Router[C <: Context](val c: C) {
$argSig.asInstanceOf[cask.internal.Router.ArgSig[Any, _, _, cask.model.ParamContext]]
)
"""
- c.internal.setPos(reader, meth.pos)
+ c.internal.setPos(reader, method.pos)
(reader, argSig)
}
@@ -307,12 +312,12 @@ class Router[C <: Context](val c: C) {
val argSigs = argData.map(_._2)
val argNames = argData.map(_._3)
val readArgs = argData.map(_._4)
- var methodCall: c.Tree = q"$baseArgSym.${meth.name.toTermName}"
+ var methodCall: c.Tree = q"$baseArgSym.${method.name.toTermName}"
for(argNameCast <- argNameCasts) methodCall = q"$methodCall(..$argNameCast)"
val res = q"""
cask.internal.Router.EntryPoint[$curCls, $ctx](
- ${meth.name.toString},
+ ${method.name.toString},
${argSigs.toList},
${methodDoc match{
case None => q"scala.None"
@@ -330,7 +335,7 @@ class Router[C <: Context](val c: C) {
"""
c.internal.transform(res){(t, a) =>
- c.internal.setPos(t, meth.pos)
+ c.internal.setPos(t, method.pos)
a.default(t)
}
diff --git a/cask/src/cask/main/Routes.scala b/cask/src/cask/main/Routes.scala
index 6f5b454..c3d2bfc 100644
--- a/cask/src/cask/main/Routes.scala
+++ b/cask/src/cask/main/Routes.scala
@@ -58,6 +58,17 @@ object Routes{
val annotations = m.annotations.filter(_.tree.tpe <:< c.weakTypeOf[BaseDecorator]).reverse
if annotations.nonEmpty
} yield {
+ if(!(annotations.head.tree.tpe <:< weakTypeOf[Endpoint[_]])) c.abort(
+ annotations.head.tree.pos,
+ s"Last annotation applied to a function must be an instance of Endpoint, " +
+ s"not ${annotations.head.tree.tpe}"
+ )
+ val allEndpoints = annotations.filter(_.tree.tpe <:< weakTypeOf[Endpoint[_]])
+ if(allEndpoints.length > 1) c.abort(
+ annotations.head.tree.pos,
+ s"You can only apply one Endpoint annotation to a function, not " +
+ s"${allEndpoints.length} in ${allEndpoints.map(_.tree.tpe).mkString(", ")}"
+ )
val annotObjects =
for(annot <- annotations)
yield q"new ${annot.tree.tpe}(..${annot.tree.children.tail})"
diff --git a/cask/test/src/test/cask/FailureTests.scala b/cask/test/src/test/cask/FailureTests.scala
new file mode 100644
index 0000000..fcfed79
--- /dev/null
+++ b/cask/test/src/test/cask/FailureTests.scala
@@ -0,0 +1,53 @@
+package test.cask
+
+import cask.model.ParamContext
+import utest._
+
+object FailureTests extends TestSuite {
+ class myDecorator extends cask.Routes.Decorator {
+ def getParamValues(ctx: ParamContext) = Map("extra" -> 31337)
+ }
+ val tests = Tests{
+
+ 'mismatchedDecorators - {
+ utest.compileError("""
+ object Decorated extends cask.MainRoutes{
+ @cask.get("/hello/:world")
+ def hello(world: String)(extra: Int) = world + extra
+ initialize()
+ }
+ """).msg ==>
+ "Endpoint hello's number of parameter lists (2) doesn't match number of decorators (1)"
+
+ utest.compileError("""
+ object Decorated extends cask.MainRoutes{
+ @myDecorator()
+ @cask.get("/hello/:world")
+ def hello(world: String)= world
+ initialize()
+ }
+ """).msg ==>
+ "Endpoint hello's number of parameter lists (1) doesn't match number of decorators (2)"
+
+ utest.compileError("""
+ object Decorated extends cask.MainRoutes{
+ @cask.get("/hello/:world")
+ @myDecorator()
+ def hello(world: String)(extra: Int)= world
+ initialize()
+ }
+ """).msg ==>
+ "Last annotation applied to a function must be an instance of Endpoint, not test.cask.FailureTests.myDecorator"
+
+ utest.compileError("""
+ object Decorated extends cask.MainRoutes{
+ @cask.get("/hello/:world")
+ @cask.get("/hello/:world")
+ def hello(world: String)(extra: Int)= world
+ initialize()
+ }
+ """).msg ==>
+ "You can only apply one Endpoint annotation to a function, not 2 in cask.endpoints.get, cask.endpoints.get"
+ }
+ }
+}