Skip to content

Added macro for passing all arguments of a method as implicit parameter #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
May 24, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
-----------------------------------

Expand Down
13 changes: 13 additions & 0 deletions sourcecode/shared/src/main/scala-2.10/sourcecode/Compat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
9 changes: 9 additions & 0 deletions sourcecode/shared/src/main/scala-2.11/sourcecode/Compat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
13 changes: 13 additions & 0 deletions sourcecode/shared/src/main/scala/sourcecode/SourceContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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._
Expand Down
60 changes: 60 additions & 0 deletions sourcecode/shared/src/test/scala/sourcecode/ArgsTests.scala
Original file line number Diff line number Diff line change
@@ -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")))
}
}
1 change: 1 addition & 0 deletions sourcecode/shared/src/test/scala/sourcecode/Tests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ object Tests{
Synthetic.run()
ManualImplicit()
TextTests()
ArgsTests()

println("================LogExample================")
logExample()
Expand Down