Skip to content

Commit 69772af

Browse files
Merge pull request #5388 from dotty-staging/port-gestalt-type-toolbox
Port gestalt TypeToolbox
2 parents 180c8df + 906ad2a commit 69772af

File tree

6 files changed

+404
-0
lines changed

6 files changed

+404
-0
lines changed

compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tastyreflect
33

44
import dotty.tools.dotc.core.Flags
55
import dotty.tools.dotc.core.Symbols._
6+
import dotty.tools.dotc.core.Decorators._
67

78
trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl {
89

@@ -93,13 +94,72 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl {
9394
def tree(implicit ctx: Context): TypeDef = FromSymbol.typeDefFromSym(symbol)
9495
}
9596

97+
object ClassSymbol extends ClassSymbolModule {
98+
def of(fullName: String)(implicit ctx: Context): ClassSymbol = ctx.requiredClass(fullName)
99+
}
100+
96101
object IsClassSymbol extends IsClassSymbolExtractor {
97102
def unapply(symbol: Symbol)(implicit ctx: Context): Option[ClassSymbol] =
98103
if (symbol.isClass) Some(symbol.asClass) else None
99104
}
100105

101106
def ClassSymbolDeco(symbol: ClassSymbol): ClassSymbolAPI = new ClassSymbolAPI {
102107
def tree(implicit ctx: Context): ClassDef = FromSymbol.classDef(symbol)
108+
109+
def fields(implicit ctx: Context): List[Symbol] = {
110+
symbol.unforcedDecls.filter(isField)
111+
}
112+
113+
def field(name: String)(implicit ctx: Context): Option[Symbol] = {
114+
val sym = symbol.unforcedDecls.find(sym => sym.name == name.toTermName)
115+
if (sym.exists && isField(sym)) Some(sym) else None
116+
}
117+
118+
def classMethod(name: String)(implicit ctx: Context): List[DefSymbol] = {
119+
symbol.typeRef.decls.iterator.collect {
120+
case sym if isMethod(sym) && sym.name.toString == name => sym.asTerm
121+
}.toList
122+
}
123+
124+
def classMethods(implicit ctx: Context): List[DefSymbol] = {
125+
symbol.typeRef.decls.iterator.collect {
126+
case sym if isMethod(sym) => sym.asTerm
127+
}.toList
128+
}
129+
130+
def method(name: String)(implicit ctx: Context): List[DefSymbol] = {
131+
symbol.typeRef.allMembers.iterator.map(_.symbol).collect {
132+
case sym if isMethod(sym) && sym.name.toString == name => sym.asTerm
133+
}.toList
134+
}
135+
136+
def methods(implicit ctx: Context): List[DefSymbol] = {
137+
symbol.typeRef.allMembers.iterator.map(_.symbol).collect {
138+
case sym if isMethod(sym) => sym.asTerm
139+
}.toList
140+
}
141+
142+
private def isMethod(sym: Symbol)(implicit ctx: Context): Boolean =
143+
sym.isTerm && sym.is(Flags.Method) && !sym.isConstructor
144+
145+
def caseFields(implicit ctx: Context): List[ValSymbol] = {
146+
if (!symbol.isClass) Nil
147+
else symbol.asClass.paramAccessors.collect {
148+
case sym if sym.is(Flags.CaseAccessor) => sym.asTerm
149+
}
150+
}
151+
152+
def companionClass(implicit ctx: Context): Option[ClassSymbol] = {
153+
val sym = symbol.companionModule.companionClass
154+
if (sym.exists) Some(sym.asClass) else None
155+
}
156+
157+
def companionModule(implicit ctx: Context): Option[ValSymbol] = {
158+
val sym = symbol.companionModule
159+
if (sym.exists) Some(sym.asTerm) else None
160+
}
161+
162+
private def isField(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm && !sym.is(Flags.Method)
103163
}
104164

105165
object IsDefSymbol extends IsDefSymbolExtractor {
@@ -118,6 +178,11 @@ trait SymbolOpsImpl extends scala.tasty.reflect.SymbolOps with CoreImpl {
118178

119179
def ValSymbolDeco(symbol: ValSymbol): ValSymbolAPI = new ValSymbolAPI {
120180
def tree(implicit ctx: Context): ValDef = FromSymbol.valDefFromSym(symbol)
181+
182+
def companionClass(implicit ctx: Context): Option[ClassSymbol] = {
183+
val sym = symbol.companionClass
184+
if (sym.exists) Some(sym.asClass) else None
185+
}
121186
}
122187

123188
object IsBindSymbol extends IsBindSymbolExtractor {

compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsTreesOpsImpl.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ trait TypeOrBoundsTreesOpsImpl extends scala.tasty.reflect.TypeOrBoundsTreeOps w
1919
def TypeTreeDeco(tpt: TypeTree): TypeTreeAPI = new TypeTreeAPI {
2020
def pos(implicit ctx: Context): Position = tpt.pos
2121
def tpe(implicit ctx: Context): Type = tpt.tpe.stripTypeVar
22+
def symbol(implicit ctx: Context): Symbol = tpt.symbol
2223
}
2324

2425
object IsTypeTree extends IsTypeTreeExtractor {

library/src/scala/tasty/reflect/SymbolOps.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ trait SymbolOps extends Core {
1515
def privateWithin(implicit ctx: Context): Option[Type]
1616
def protectedWithin(implicit ctx: Context): Option[Type]
1717

18+
/** Name of the definition */
1819
def name(implicit ctx: Context): String
20+
21+
/** Full name of the definition from the _root_ package */
1922
def fullName(implicit ctx: Context): String
2023

2124
def localContext(implicit ctx: Context): Context
@@ -51,8 +54,43 @@ trait SymbolOps extends Core {
5154
def unapply(symbol: Symbol)(implicit ctx: Context): Option[ClassSymbol]
5255
}
5356

57+
val ClassSymbol: ClassSymbolModule
58+
abstract class ClassSymbolModule {
59+
/** The ClassSymbol of a global class definition */
60+
def of(fullName: String)(implicit ctx: Context): ClassSymbol
61+
}
62+
5463
trait ClassSymbolAPI {
64+
/** Tree of this class definition */
5565
def tree(implicit ctx: Context): ClassDef
66+
67+
/** Fields directly declared in the class */
68+
def fields(implicit ctx: Context): List[Symbol]
69+
70+
/** Field with the given name directly declared in the class */
71+
def field(name: String)(implicit ctx: Context): Option[Symbol]
72+
73+
/** Get non-private named methods defined directly inside the class */
74+
def classMethod(name: String)(implicit ctx: Context): List[DefSymbol]
75+
76+
/** Get all non-private methods defined directly inside the class, exluding constructors */
77+
def classMethods(implicit ctx: Context): List[DefSymbol]
78+
79+
/** Get named non-private methods declared or inherited */
80+
def method(name: String)(implicit ctx: Context): List[DefSymbol]
81+
82+
/** Get all non-private methods declared or inherited */
83+
def methods(implicit ctx: Context): List[DefSymbol]
84+
85+
/** Fields of a case class type -- only the ones declared in primary constructor */
86+
def caseFields(implicit ctx: Context): List[ValSymbol]
87+
88+
/** The class symbol of the companion module class */
89+
def companionClass(implicit ctx: Context): Option[ClassSymbol]
90+
91+
/** The symbol of the companion module */
92+
def companionModule(implicit ctx: Context): Option[ValSymbol]
93+
5694
}
5795
implicit def ClassSymbolDeco(symbol: ClassSymbol): ClassSymbolAPI
5896

@@ -89,6 +127,10 @@ trait SymbolOps extends Core {
89127

90128
trait ValSymbolAPI {
91129
def tree(implicit ctx: Context): ValDef
130+
131+
/** The class symbol of the companion module class */
132+
def companionClass(implicit ctx: Context): Option[ClassSymbol]
133+
92134
}
93135
implicit def ValSymbolDeco(symbol: ValSymbol): ValSymbolAPI
94136

library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ trait TypeOrBoundsTreeOps extends Core {
1313
trait TypeTreeAPI {
1414
def pos(implicit ctx: Context): Position
1515
def tpe(implicit ctx: Context): Type
16+
def symbol(implicit ctx: Context): Symbol
1617
}
1718
implicit def TypeTreeDeco(tpt: TypeTree): TypeTreeAPI
1819

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/main/scala/gestalt/macros/TypeToolbox.scala
2+
// using staging reflection
3+
4+
import scala.quoted._
5+
import scala.tasty._
6+
7+
object TypeToolbox {
8+
/** are the two types equal? */
9+
inline def =:=[A, B]: Boolean = ~tpEqImpl('[A], '[B])
10+
private def tpEqImpl[A, B](a: Type[A], b: Type[B])(implicit reflect: Reflection): Expr[Boolean] = {
11+
import reflect._
12+
val res = a.reflect.tpe =:= b.reflect.tpe
13+
res.toExpr
14+
}
15+
16+
/** is `tp1` a subtype of `tp2` */
17+
inline def <:<[A, B]: Boolean = ~tpLEqImpl('[A], '[B])
18+
private def tpLEqImpl[A, B](a: Type[A], b: Type[B])(implicit reflect: Reflection): Expr[Boolean] = {
19+
import reflect._
20+
val res = a.reflect.tpe <:< b.reflect.tpe
21+
res.toExpr
22+
}
23+
24+
/** type associated with the tree */
25+
inline def typeOf[T, Expected](a: T): Boolean = ~typeOfImpl('(a), '[Expected])
26+
private def typeOfImpl(a: Expr[_], expected: Type[_])(implicit reflect: Reflection): Expr[Boolean] = {
27+
import reflect._
28+
val res = a.reflect.tpe =:= expected.reflect.tpe
29+
res.toExpr
30+
}
31+
32+
/** does the type refer to a case class? */
33+
inline def isCaseClass[A]: Boolean = ~isCaseClassImpl('[A])
34+
private def isCaseClassImpl(tp: Type[_])(implicit reflect: Reflection): Expr[Boolean] = {
35+
import reflect._
36+
val res = tp.reflect.symbol match {
37+
case IsClassSymbol(sym) => sym.flags.isCase
38+
case _ => false
39+
}
40+
res.toExpr
41+
}
42+
43+
/** val fields of a case class Type -- only the ones declared in primary constructor */
44+
inline def caseFields[T]: List[String] = ~caseFieldsImpl('[T])
45+
private def caseFieldsImpl(tp: Type[_])(implicit reflect: Reflection): Expr[List[String]] = {
46+
import reflect._
47+
val fields = tp.reflect.symbol.asClass.caseFields.map(_.name)
48+
fields.toExpr
49+
}
50+
51+
inline def fieldIn[T](inline mem: String): String = ~fieldInImpl('[T], mem)
52+
private def fieldInImpl(t: Type[_], mem: String)(implicit reflect: Reflection): Expr[String] = {
53+
import reflect._
54+
val field = t.reflect.symbol.asClass.field(mem)
55+
field.map(_.name).getOrElse("").toExpr
56+
}
57+
58+
inline def fieldsIn[T]: Seq[String] = ~fieldsInImpl('[T])
59+
private def fieldsInImpl(t: Type[_])(implicit reflect: Reflection): Expr[Seq[String]] = {
60+
import reflect._
61+
val fields = t.reflect.symbol.asClass.fields
62+
fields.map(_.name).toList.toExpr
63+
}
64+
65+
inline def methodIn[T](inline mem: String): Seq[String] = ~methodInImpl('[T], mem)
66+
private def methodInImpl(t: Type[_], mem: String)(implicit reflect: Reflection): Expr[Seq[String]] = {
67+
import reflect._
68+
t.reflect.symbol.asClass.classMethod(mem).map(_.name).toExpr
69+
}
70+
71+
inline def methodsIn[T]: Seq[String] = ~methodsInImpl('[T])
72+
private def methodsInImpl(t: Type[_])(implicit reflect: Reflection): Expr[Seq[String]] = {
73+
import reflect._
74+
t.reflect.symbol.asClass.classMethods.map(_.name).toExpr
75+
}
76+
77+
inline def method[T](inline mem: String): Seq[String] = ~methodImpl('[T], mem)
78+
private def methodImpl(t: Type[_], mem: String)(implicit reflect: Reflection): Expr[Seq[String]] = {
79+
import reflect._
80+
t.reflect.symbol.asClass.method(mem).map(_.name).toExpr
81+
}
82+
83+
inline def methods[T]: Seq[String] = ~methodsImpl('[T])
84+
private def methodsImpl(t: Type[_])(implicit reflect: Reflection): Expr[Seq[String]] = {
85+
import reflect._
86+
t.reflect.symbol.asClass.methods.map(_.name).toExpr
87+
}
88+
89+
inline def typeTag[T](x: T): String = ~typeTagImpl('[T])
90+
private def typeTagImpl(tp: Type[_])(implicit reflect: Reflection): Expr[String] = {
91+
import reflect._
92+
val res = tp.reflect.tpe.showCode
93+
res.toExpr
94+
}
95+
96+
inline def companion[T1, T2]: Boolean = ~companionImpl('[T1], '[T2])
97+
private def companionImpl(t1: Type[_], t2: Type[_])(implicit reflect: Reflection): Expr[Boolean] = {
98+
import reflect._
99+
val res = t1.reflect.symbol.asClass.companionModule.contains(t2.reflect.symbol)
100+
res.toExpr
101+
}
102+
103+
inline def companionName[T1]: String = ~companionNameImpl('[T1])
104+
private def companionNameImpl(tp: Type[_])(implicit reflect: Reflection): Expr[String] = {
105+
import reflect._
106+
val companionClassOpt = tp.reflect.symbol match {
107+
case IsClassSymbol(sym) => sym.companionClass
108+
case IsValSymbol(sym) => sym.companionClass
109+
case _ => None
110+
}
111+
companionClassOpt.map(_.fullName).getOrElse("").toExpr
112+
}
113+
114+
// TODO add to the std lib
115+
private implicit def listIsLiftable[T: Type: Liftable]: Liftable[List[T]] = new Liftable {
116+
def toExpr(list: List[T]): Expr[List[T]] = list match {
117+
case x :: xs => '(~x.toExpr :: ~toExpr(xs))
118+
case Nil => '(Nil)
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)