Skip to content

Port gestalt TypeToolbox #5388

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 2 commits into from
Nov 12, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
55 changes: 55 additions & 0 deletions compiler/src/dotty/tools/dotc/tastyreflect/SymbolOpsImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tastyreflect

import dotty.tools.dotc.core.Flags
import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Decorators._

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

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

object ClassSymbol extends ClassSymbolModule {
def of(fullName: String)(implicit ctx: Context): ClassSymbol = ctx.requiredClass(fullName)
}

object IsClassSymbol extends IsClassSymbolExtractor {
def unapply(symbol: Symbol)(implicit ctx: Context): Option[ClassSymbol] =
if (symbol.isClass) Some(symbol.asClass) else None
}

def ClassSymbolDeco(symbol: ClassSymbol): ClassSymbolAPI = new ClassSymbolAPI {
def tree(implicit ctx: Context): ClassDef = FromSymbol.classDef(symbol)

def fields(implicit ctx: Context): List[Symbol] = {
symbol.unforcedDecls.filter(isField)
}

def field(name: String)(implicit ctx: Context): Option[Symbol] = {
val sym = symbol.unforcedDecls.find(sym => sym.name == name.toTermName)
if (sym.exists && isField(sym)) Some(sym) else None
}

def classMethod(name: String)(implicit ctx: Context): List[DefSymbol] = {
symbol.typeRef.decls.iterator.collect {
case sym if isMethod(sym) && sym.name.toString == name => sym.asTerm
}.toList
}

def classMethods(implicit ctx: Context): List[DefSymbol] = {
symbol.typeRef.decls.iterator.collect {
case sym if isMethod(sym) => sym.asTerm
}.toList
}

def method(name: String)(implicit ctx: Context): List[DefSymbol] = {
symbol.typeRef.allMembers.iterator.map(_.symbol).collect {
case sym if isMethod(sym) && sym.name.toString == name => sym.asTerm
}.toList
}

def methods(implicit ctx: Context): List[DefSymbol] = {
symbol.typeRef.allMembers.iterator.map(_.symbol).collect {
case sym if isMethod(sym) => sym.asTerm
}.toList
}

private def isMethod(sym: Symbol)(implicit ctx: Context): Boolean =
sym.isTerm && sym.is(Flags.Method) && !sym.isConstructor

def caseFields(implicit ctx: Context): List[ValSymbol] = {
if (!symbol.isClass) Nil
else symbol.asClass.paramAccessors.collect {
case sym if sym.is(Flags.CaseAccessor) => sym.asTerm
}
}

def companionClass(implicit ctx: Context): Option[ClassSymbol] = {
val sym = symbol.companionModule.companionClass
if (sym.exists) Some(sym.asClass) else None
}

private def isField(sym: Symbol)(implicit ctx: Context): Boolean = sym.isTerm && !sym.is(Flags.Method)
}

object IsDefSymbol extends IsDefSymbolExtractor {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ trait TypeOrBoundsTreesOpsImpl extends scala.tasty.reflect.TypeOrBoundsTreeOps w
def TypeTreeDeco(tpt: TypeTree): TypeTreeAPI = new TypeTreeAPI {
def pos(implicit ctx: Context): Position = tpt.pos
def tpe(implicit ctx: Context): Type = tpt.tpe.stripTypeVar
def symbol(implicit ctx: Context): Symbol = {
val sym = tpt.symbol
if (sym.isType) sym else sym.companionClass
}
}

object IsTypeTree extends IsTypeTreeExtractor {
Expand Down
33 changes: 33 additions & 0 deletions library/src/scala/tasty/reflect/SymbolOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ trait SymbolOps extends Core {
def privateWithin(implicit ctx: Context): Option[Type]
def protectedWithin(implicit ctx: Context): Option[Type]

/** Name of the definition */
def name(implicit ctx: Context): String

/** Full name of the definition from the _root_ package */
def fullName(implicit ctx: Context): String

def localContext(implicit ctx: Context): Context
Expand Down Expand Up @@ -51,8 +54,38 @@ trait SymbolOps extends Core {
def unapply(symbol: Symbol)(implicit ctx: Context): Option[ClassSymbol]
}

val ClassSymbol: ClassSymbolModule
abstract class ClassSymbolModule {
/** The ClassSymbol of a global class definition */
def of(fullName: String)(implicit ctx: Context): ClassSymbol
}

trait ClassSymbolAPI {
/** Tree of this class definition */
def tree(implicit ctx: Context): ClassDef

/** Fields directly declared in the class */
def fields(implicit ctx: Context): List[Symbol]

/** Field with the given name directly declared in the class */
def field(name: String)(implicit ctx: Context): Option[Symbol]

/** Get non-private named methods defined directly inside the class */
def classMethod(name: String)(implicit ctx: Context): List[DefSymbol]

/** Get all non-private methods defined directly inside the class, exluding constructors */
def classMethods(implicit ctx: Context): List[DefSymbol]

/** Get named non-private methods declared or inherited */
def method(name: String)(implicit ctx: Context): List[DefSymbol]

/** Get all non-private methods declared or inherited */
def methods(implicit ctx: Context): List[DefSymbol]

/** Fields of a case class type -- only the ones declared in primary constructor */
def caseFields(implicit ctx: Context): List[ValSymbol]

def companionClass(implicit ctx: Context): Option[ClassSymbol]
}
implicit def ClassSymbolDeco(symbol: ClassSymbol): ClassSymbolAPI

Expand Down
1 change: 1 addition & 0 deletions library/src/scala/tasty/reflect/TypeOrBoundsTreeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ trait TypeOrBoundsTreeOps extends Core {
trait TypeTreeAPI {
def pos(implicit ctx: Context): Position
def tpe(implicit ctx: Context): Type
def symbol(implicit ctx: Context): Symbol
}
implicit def TypeTreeDeco(tpt: TypeTree): TypeTreeAPI

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
// Port of https://github.com/liufengyun/gestalt/blob/master/macros/src/main/scala/gestalt/macros/TypeToolbox.scala
// using staging reflection

import scala.quoted._
import scala.tasty._

object TypeToolbox {
/** are the two types equal? */
inline def =:=[A, B]: Boolean = ~tpEqImpl('[A], '[B])
private def tpEqImpl[A, B](a: Type[A], b: Type[B])(implicit reflect: Reflection): Expr[Boolean] = {
import reflect._
val res = a.reflect.tpe =:= b.reflect.tpe
res.toExpr
}

/** is `tp1` a subtype of `tp2` */
inline def <:<[A, B]: Boolean = ~tpLEqImpl('[A], '[B])
private def tpLEqImpl[A, B](a: Type[A], b: Type[B])(implicit reflect: Reflection): Expr[Boolean] = {
import reflect._
val res = a.reflect.tpe <:< b.reflect.tpe
res.toExpr
}

/** type associated with the tree */
inline def typeOf[T, Expected](a: T): Boolean = ~typeOfImpl('(a), '[Expected])
private def typeOfImpl(a: Expr[_], expected: Type[_])(implicit reflect: Reflection): Expr[Boolean] = {
import reflect._
val res = a.reflect.tpe =:= expected.reflect.tpe
res.toExpr
}

/** does the type refer to a case class? */
inline def isCaseClass[A]: Boolean = ~isCaseClassImpl('[A])
private def isCaseClassImpl(tp: Type[_])(implicit reflect: Reflection): Expr[Boolean] = {
import reflect._
val res = tp.reflect.symbol match {
case IsClassSymbol(sym) => sym.flags.isCase
case _ => false
}
res.toExpr
}

/** val fields of a case class Type -- only the ones declared in primary constructor */
inline def caseFields[T]: List[String] = ~caseFieldsImpl('[T])
private def caseFieldsImpl(tp: Type[_])(implicit reflect: Reflection): Expr[List[String]] = {
import reflect._
val fields = tp.reflect.symbol.asClass.caseFields.map(_.name)
fields.toExpr
}

inline def fieldIn[T](inline mem: String): String = ~fieldInImpl('[T], mem)
private def fieldInImpl(t: Type[_], mem: String)(implicit reflect: Reflection): Expr[String] = {
import reflect._
val field = t.reflect.symbol.asClass.field(mem)
field.map(_.name).getOrElse("").toExpr
}

inline def fieldsIn[T]: Seq[String] = ~fieldsInImpl('[T])
private def fieldsInImpl(t: Type[_])(implicit reflect: Reflection): Expr[Seq[String]] = {
import reflect._
val fields = t.reflect.symbol.asClass.fields
fields.map(_.name).toList.toExpr
}

inline def methodIn[T](inline mem: String): Seq[String] = ~methodInImpl('[T], mem)
private def methodInImpl(t: Type[_], mem: String)(implicit reflect: Reflection): Expr[Seq[String]] = {
import reflect._
t.reflect.symbol.asClass.classMethod(mem).map(_.name).toExpr
}

inline def methodsIn[T]: Seq[String] = ~methodsInImpl('[T])
private def methodsInImpl(t: Type[_])(implicit reflect: Reflection): Expr[Seq[String]] = {
import reflect._
t.reflect.symbol.asClass.classMethods.map(_.name).toExpr
}

inline def method[T](inline mem: String): Seq[String] = ~methodImpl('[T], mem)
private def methodImpl(t: Type[_], mem: String)(implicit reflect: Reflection): Expr[Seq[String]] = {
import reflect._
t.reflect.symbol.asClass.method(mem).map(_.name).toExpr
}

inline def methods[T]: Seq[String] = ~methodsImpl('[T])
private def methodsImpl(t: Type[_])(implicit reflect: Reflection): Expr[Seq[String]] = {
import reflect._
t.reflect.symbol.asClass.methods.map(_.name).toExpr
}

inline def typeTag[T](x: T): String = ~typeTagImpl('[T])
private def typeTagImpl(tp: Type[_])(implicit reflect: Reflection): Expr[String] = {
import reflect._
val res = tp.reflect.tpe.showCode
res.toExpr
}

inline def companion[T1, T2]: Boolean = ~companionImpl('[T1], '[T2])
private def companionImpl(t1: Type[_], t2: Type[_])(implicit reflect: Reflection): Expr[Boolean] = {
import reflect._
val res = t1.reflect.symbol.asClass.companionClass.contains(t2.reflect.symbol)
res.toExpr
}

inline def companionName[T1]: String = ~companionNameImpl('[T1])
private def companionNameImpl(tp: Type[_])(implicit reflect: Reflection): Expr[String] = {
import reflect._
tp.reflect.symbol match {
case IsClassSymbol(sym) => sym.companionClass.map(_.fullName).getOrElse("").toExpr
case _ => '("")
}
}

// TODO add to the std lib
private implicit def listIsLiftable[T: Type: Liftable]: Liftable[List[T]] = new Liftable {
def toExpr(list: List[T]): Expr[List[T]] = list match {
case x :: xs => '(~x.toExpr :: ~toExpr(xs))
case Nil => '(Nil)
}
}
}
Loading