diff --git a/README.md b/README.md index 3628091..2bbe217 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,8 @@ The kinds of compilation-time data that `sourcecode` provides are: you have multiple statements in a `{}` block, `sourcecode.Text` will only capture the source code for the last expression that gets returned. This implicit is slightly experimental; be sure to report any bugs you find! +- `sourcecode.Args`: the arguments that where provided to the nearest enclosing + method - `sourcecode.Name.Machine`, `sourcecode.FullName.Machine` and `sourcecode.Enclosing.Machine` which are similar to `sourcecode.Name`, `sourcecode.FullName` and `sourcecode.Enclosing` except they do not filter @@ -475,6 +477,21 @@ be printed. You can, or course, define your own `log` method in the same way, customizing it to print or not-print exactly what you want to see via the implicits that `sourcecode` provides! +`sourcecode.Args` can be used to access all parameters that where provided +to a method: + +```scala +def debug(implicit name: sourcecode.Name, args: sourcecode.Args): Unit = { + println(name.value + args.value.map(_.map(a => a.source + "=" + a.value).mkString("(", ", ", ")")).mkString("")) +} + +def foo(bar: String, baz: Int)(p: Boolean): Unit = { + debug +} + +foo("baz", 42)(true) // foo(bar=baz, baz=42)(p=true) +``` + Embedding Domain-Specific Languages ----------------------------------- diff --git a/sourcecode/shared/src/main/scala-2.10/sourcecode/Compat.scala b/sourcecode/shared/src/main/scala-2.10/sourcecode/Compat.scala index 92fec60..31ba2e0 100644 --- a/sourcecode/shared/src/main/scala-2.10/sourcecode/Compat.scala +++ b/sourcecode/shared/src/main/scala-2.10/sourcecode/Compat.scala @@ -9,4 +9,17 @@ object Compat{ .owner .asInstanceOf[c.Symbol] } + + def enclosingParamList(c: Context): List[List[c.Symbol]] = { + def nearestClassOrMethod(owner: c.Symbol): c.Symbol = + if (owner.isMethod || owner.isClass) owner else nearestClassOrMethod(owner.owner) + + val com = nearestClassOrMethod(enclosingOwner(c)) + if (com.isClass) { + val pc = com.typeSignature.members.filter(m => m.isMethod && m.asMethod.isPrimaryConstructor) + pc.head.asMethod.paramss + } else { + com.asMethod.paramss + } + } } \ No newline at end of file diff --git a/sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala b/sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala index 70ff8ac..b3c08e4 100644 --- a/sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala +++ b/sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala @@ -3,4 +3,13 @@ package sourcecode object Compat{ type Context = scala.reflect.macros.blackbox.Context def enclosingOwner(c: Context) = c.internal.enclosingOwner + + def enclosingParamList(c: Context): List[List[c.Symbol]] = { + def nearestEnclosingMethod(owner: c.Symbol): c.Symbol = + if (owner.isMethod) owner + else if (owner.isClass) owner.asClass.primaryConstructor + else nearestEnclosingMethod(owner.owner) + + nearestEnclosingMethod(enclosingOwner(c)).asMethod.paramLists + } } \ No newline at end of file diff --git a/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala b/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala index 27966e8..6ba86c5 100644 --- a/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala +++ b/sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala @@ -111,6 +111,19 @@ object Text{ def apply[T](v: T): Text[T] = macro Impls.text[T] } + +case class Args(value: Seq[Seq[Text[_]]]) extends SourceValue[Seq[Seq[Text[_]]]] +object Args extends SourceCompanion[Seq[Seq[Text[_]]], Args](new Args(_)) { + implicit def generate: Args = macro impl + def impl(c: Compat.Context): c.Expr[Args] = { + import c.universe._ + val param = Compat.enclosingParamList(c) + val texts = param.map(_.map(p => c.Expr[Text[_]](q"""sourcecode.Text($p, ${p.name.toString})"""))) + val textSeqs = texts.map(s => c.Expr(q"""Seq(..$s)""")) + c.Expr[Args](q"""Seq(..$textSeqs)""") + } +} + object Impls{ def text[T: c.WeakTypeTag](c: Compat.Context)(v: c.Expr[T]): c.Expr[sourcecode.Text[T]] = { import c.universe._ diff --git a/sourcecode/shared/src/test/scala/sourcecode/ArgsTests.scala b/sourcecode/shared/src/test/scala/sourcecode/ArgsTests.scala new file mode 100644 index 0000000..31a28a3 --- /dev/null +++ b/sourcecode/shared/src/test/scala/sourcecode/ArgsTests.scala @@ -0,0 +1,60 @@ +package sourcecode + +object ArgsTests { + def apply() = { + + var args: Seq[Seq[(String, Any)]] = Seq() + + def debug(implicit arguments: sourcecode.Args): Unit = args = arguments.value.map(_.map(t => t.source -> t.value)) + + def foo(p1: String, p2: Long, p3: Boolean)(foo: String, bar: String): Unit = { + debug + } + + def bar(p1: String, p2: Long, p3: Boolean)(foo: String, bar: String): Unit = { + val bar = { + debug + "bar" + } + } + + def baz: Unit = { + debug + } + + def withImplicit(p1: String, p2: Long, p3: Boolean)(implicit foo: String): Unit = { + debug + } + + class Foo(p1: String, p2: Long, p3: Boolean)(foo: String, bar: String) { + debug + + def this(p1: String, p2: Long) = { + this(p1, p2, false)("foo", "bar") + debug + } + } + + new Foo("text", 42, false)("foo", "bar") + assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo", "bar" -> "bar"))) + + new Foo("text", 42) + assert(args == Seq(Seq("p1" -> "text", "p2" -> 42))) + + foo("text", 42, false)("foo", "bar") + assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo", "bar" -> "bar"))) + + bar("text", 42, false)("foo", "bar") + assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo", "bar" -> "bar"))) + + baz + assert(args == Seq()) + + withImplicit("text", 42, false)("foo") + assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "foo"))) + + implicit val implicitFoo = "bar" + withImplicit("text", 42, false) + assert(args == Seq(Seq("p1" -> "text", "p2" -> 42, "p3" -> false), Seq("foo" -> "bar"))) + } +} diff --git a/sourcecode/shared/src/test/scala/sourcecode/Tests.scala b/sourcecode/shared/src/test/scala/sourcecode/Tests.scala index 288a80b..546e576 100644 --- a/sourcecode/shared/src/test/scala/sourcecode/Tests.scala +++ b/sourcecode/shared/src/test/scala/sourcecode/Tests.scala @@ -20,6 +20,7 @@ object Tests{ Synthetic.run() ManualImplicit() TextTests() + ArgsTests() println("================LogExample================") logExample()