Skip to content

Commit f06f39d

Browse files
committed
added qualifiedNameOf
1 parent 5c8de8b commit f06f39d

File tree

4 files changed

+69
-1
lines changed

4 files changed

+69
-1
lines changed

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,20 @@ println("age")
7272
println("sayHello")
7373
```
7474

75+
Without having an instance of the type for nested case classes:
76+
```scala
77+
import com.github.dwickern.macros.NameOf._
78+
79+
case class Pet(age: Int)
80+
case class Person(name: String, pet : Pet)
81+
82+
println(qualifiedNameOf[Person](_.pet.age))
83+
84+
// compiles to:
85+
86+
println("pet.age")
87+
```
88+
7589
You can also use `nameOfType` to get the unqualified name of a type:
7690
```scala mdoc:nest
7791
println(nameOfType[java.lang.String])

src/main/scala/com/github/dwickern/macros/NameOf.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,20 @@ trait NameOf {
2727
*/
2828
def nameOf[T](expr: T => Any): String = macro NameOfImpl.nameOf
2929

30+
/**
31+
* Obtain a fully qualified identifier name as a constant string.
32+
*
33+
* This overload can be used to access an instance method without having an instance of the type.
34+
*
35+
* Example usage:
36+
* {{{
37+
* class Pet(val age: Int)
38+
* class Person(val name: String, val pet : Pet)
39+
* nameOf[Person](_.peta.age) => "pet.age"
40+
* }}}
41+
*/
42+
def qualifiedNameOf[T](expr: T => Any): String = macro NameOfImpl.qualifiedNameOf
43+
3044
/**
3145
* Obtain a type's unqualified name as a constant string.
3246
*

src/main/scala/com/github/dwickern/macros/NameOfImpl.scala

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.github.dwickern.macros
22

33
import scala.language.experimental.macros
4-
import scala.reflect.macros.whitebox
4+
import scala.reflect.macros.{blackbox, whitebox}
55
import scala.annotation.tailrec
66

77
object NameOfImpl {
@@ -47,6 +47,37 @@ object NameOfImpl {
4747
c.Expr[String](q"$name")
4848
}
4949

50+
def qualifiedNameOf(c: blackbox.Context)(expr: c.Expr[Any]): c.Expr[String] = {
51+
import c.universe._
52+
53+
def extractNames(tree: c.Tree): List[c.Name] = {
54+
tree.children.headOption match {
55+
case Some(child) =>
56+
extractNames(child) :+ tree.symbol.name
57+
case None =>
58+
List(tree.symbol.name)
59+
}
60+
}
61+
62+
@tailrec def extract(tree: c.Tree): List[c.Name] = tree match {
63+
case Ident(n) => List(n)
64+
case Select(tree, n) => extractNames(tree) :+ n
65+
case Function(_, body) => extract(body)
66+
case Block(_, expr) => extract(expr)
67+
case Apply(func, _) => extract(func)
68+
case TypeApply(func, _) => extract(func)
69+
case _ => c.abort(c.enclosingPosition, s"Unsupported expression: $expr")
70+
}
71+
72+
val name = extract(expr.tree)
73+
// drop sth like x$1
74+
.drop(1)
75+
.mkString(".")
76+
reify {
77+
c.Expr[String] { Literal(Constant(name)) }.splice
78+
}
79+
}
80+
5081
def nameOfType[T](c: whitebox.Context)(implicit tag: c.WeakTypeTag[T]): c.Expr[String] = {
5182
import c.universe._
5283
val name = showRaw(tag.tpe.typeSymbol.name)

src/test/scala/com/github/dwickern/macros/NameOfTest.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ class NameOfTest extends AnyFunSuite with Matchers {
104104
qualifiedNameOfType[CaseClass] should equal ("com.github.dwickern.macros.NameOfTest.CaseClass")
105105
}
106106

107+
test("nested case class member") {
108+
case class Nested3CaseClass(member: String)
109+
case class Nested2CaseClass(nested3CaseClass: Nested3CaseClass)
110+
case class Nested1CaseClass(nested2CaseClass: Nested2CaseClass)
111+
case class CaseClass(nested1CaseClass: Nested1CaseClass)
112+
113+
qualifiedNameOf[CaseClass](_.nested1CaseClass.nested2CaseClass.nested3CaseClass.member) should equal("nested1CaseClass.nested2CaseClass.nested3CaseClass.member")
114+
}
115+
107116
test("object") {
108117
object SomeObject
109118
nameOf(SomeObject) should equal ("SomeObject")

0 commit comments

Comments
 (0)