Skip to content

Move Reflection to scala.quoted #10291

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 1 commit into from
Nov 12, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, QuoteUnpickl
}
end extension

object reflect extends scala.tasty.Reflection:
object reflect extends scala.quoted.Reflection:

def rootContext: Context = ctx

Expand Down
3 changes: 2 additions & 1 deletion library/src/scala/quoted/QuoteContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ trait QuoteContext { self: internal.QuoteUnpickler & internal.QuoteMatching =>
/** Low-level Typed AST API metaprogramming API.
* This API does not have the static type guarantiees that `Expr` and `Type` provide.
*/
val reflect: scala.tasty.Reflection
val reflect: scala.quoted.Reflection
// TODO move Reflcetion definition in here

/** Type of a QuoteContext provided by a splice within a quote that took this context.
* It is only required if working with the reflection API.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package scala.tasty
package scala.quoted

import scala.tasty.reflect._

/** TASTy Reflect Interface.
/** AST reflection interface.
*
* Provides all functionality related with AST based metaprogramming.
*
Expand Down
1 change: 0 additions & 1 deletion scala3doc/src/dotty/dokka/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import java.util.jar._
import collection.JavaConverters._
import collection.immutable.ArraySeq

import scala.tasty.Reflection
import scala.tasty.inspector.TastyInspector
import java.nio.file.Files

Expand Down
2 changes: 1 addition & 1 deletion scala3doc/src/dotty/dokka/tasty/BasicSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import dotty.dokka.model.api.Annotation
trait BasicSupport:
self: TastyParser =>
import qctx.reflect._
object SymOps extends SymOps[qctx.reflect.type](qctx.reflect)
object SymOps extends SymOps[qctx.type](qctx)
export SymOps._

def parseAnnotation(annotTerm: Term): Annotation =
Expand Down
2 changes: 1 addition & 1 deletion scala3doc/src/dotty/dokka/tasty/ClassLikeSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ trait ClassLikeSupport:
}
)

val supertypes = getSupertypes(classDef).map {
val supertypes = getSupertypes(using qctx)(classDef).map {
case (symbol, tpe) => LinkToType(tpe.dokkaType.asSignature, symbol.dri, kindForClasslike(symbol))
}
val selfSiangture: DSignature = typeForClass(classDef).dokkaType.asSignature
Expand Down
4 changes: 2 additions & 2 deletions scala3doc/src/dotty/dokka/tasty/ScalaDocSupport.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ trait ScaladocSupport { self: TastyParser =>

val parser = commentSyntax match {
case CommentSyntax.Wiki =>
comments.WikiCommentParser(comments.Repr(qctx.reflect)(tree.symbol))
comments.WikiCommentParser(comments.Repr(qctx)(tree.symbol))
case CommentSyntax.Markdown =>
comments.MarkdownCommentParser(comments.Repr(qctx.reflect)(tree.symbol))
comments.MarkdownCommentParser(comments.Repr(qctx)(tree.symbol))
}
val parsed = parser.parse(preparsed)

Expand Down
13 changes: 7 additions & 6 deletions scala3doc/src/dotty/dokka/tasty/SymOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import dotty.dokka._
import dotty.dokka.model.api.Visibility
import dotty.dokka.model.api.VisibilityScope
import dotty.dokka.model.api.Modifier
import scala.tasty.Reflection

class SymOps[R <: Reflection](val r: R):
import r._
import scala.quoted._

given R = r
extension (sym: r.Symbol):
class SymOps[Q <: QuoteContext](val q: Q):
import q.reflect._

given Q = q
extension (sym: Symbol):
def packageName(using ctx: Context): String =
if (sym.isPackageDef) sym.fullName
else sym.maybeOwner.packageName
Expand Down Expand Up @@ -49,7 +50,7 @@ class SymOps[R <: Reflection](val r: R):
// TODO: #49 Remove it after TASTY-Reflect release with published flag Extension
def hackIsOpen: Boolean = {
import dotty.tools.dotc
given dotc.core.Contexts.Context = r.rootContext.asInstanceOf
given dotc.core.Contexts.Context = qctx.reflect.rootContext.asInstanceOf
val symbol = sym.asInstanceOf[dotc.core.Symbols.Symbol]
symbol.is(dotc.core.Flags.Open)
}
Expand Down
40 changes: 22 additions & 18 deletions scala3doc/src/dotty/dokka/tasty/SyntheticSupport.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package dotty.dokka.tasty

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

trait SyntheticsSupport:
self: TastyParser =>
Expand Down Expand Up @@ -31,11 +31,11 @@ trait SyntheticsSupport:

def isDefaultHelperMethod: Boolean = ".*\\$default\\$\\d+$".r.matches(s.name)

def isOpaque: Boolean = hackIsOpaque(qctx.reflect)(s)
def isOpaque: Boolean = hackIsOpaque(using qctx)(s)

def isInfix: Boolean = hackIsInfix(qctx.reflect)(s)
def isInfix: Boolean = hackIsInfix(using qctx)(s)

def getAllMembers: List[Symbol] = hackGetAllMembers(qctx.reflect)(s)
def getAllMembers: List[Symbol] = hackGetAllMembers(using qctx)(s)

def isSyntheticField(c: Symbol) =
c.flags.is(Flags.CaseAccessor) || c.flags.is(Flags.Object)
Expand All @@ -52,53 +52,57 @@ trait SyntheticsSupport:
}

// TODO: #49 Remove it after TASTY-Reflect release with published flag Extension
def hackIsInfix(r: Reflection)(rsym: r.Symbol): Boolean = {
def hackIsInfix(using QuoteContext)(rsym: qctx.reflect.Symbol): Boolean = {
import qctx.reflect._
import dotty.tools.dotc
given ctx as dotc.core.Contexts.Context = r.rootContext.asInstanceOf
given ctx as dotc.core.Contexts.Context = rootContext.asInstanceOf
val sym = rsym.asInstanceOf[dotc.core.Symbols.Symbol]
ctx.definitions.isInfix(sym)
}
/* We need there to filter out symbols with certain flagsets, because these symbols come from compiler and TASTY can't handle them well.
They are valdefs that describe case companion objects and cases from enum.
TASTY crashed when calling _.tree on them.
*/
def hackGetAllMembers(r: Reflection)(rsym: r.Symbol): List[r.Symbol] = {
def hackGetAllMembers(using QuoteContext)(rsym: qctx.reflect.Symbol): List[qctx.reflect.Symbol] = {
import qctx.reflect._
import dotty.tools.dotc
given ctx as dotc.core.Contexts.Context = r.rootContext.asInstanceOf
given ctx as dotc.core.Contexts.Context = rootContext.asInstanceOf
val sym = rsym.asInstanceOf[dotc.core.Symbols.Symbol]
sym.typeRef.appliedTo(sym.typeParams.map(_.typeRef)).allMembers.iterator.map(_.symbol)
.collect {
case sym if
!sym.is(dotc.core.Flags.ModuleVal) &&
!sym.flags.isAllOf(dotc.core.Flags.Enum | dotc.core.Flags.Case | dotc.core.Flags.JavaStatic) =>
sym.asInstanceOf[r.Symbol]
sym.asInstanceOf[Symbol]
}.toList
}

def hackIsOpaque(r: Reflection)(rsym: r.Symbol): Boolean = {
def hackIsOpaque(using QuoteContext)(rsym: qctx.reflect.Symbol): Boolean = {
import dotty.tools.dotc
given dotc.core.Contexts.Context = r.rootContext.asInstanceOf
given dotc.core.Contexts.Context = qctx.reflect.rootContext.asInstanceOf
val sym = rsym.asInstanceOf[dotc.core.Symbols.Symbol]
sym.is(dotc.core.Flags.Opaque)
}

def hackGetSupertypes(r: Reflection)(rdef: r.ClassDef) = {
def hackGetSupertypes(using QuoteContext)(rdef: qctx.reflect.ClassDef) = {
import qctx.reflect._
import dotty.tools.dotc
given dotc.core.Contexts.Context = r.rootContext.asInstanceOf
given dotc.core.Contexts.Context = qctx.reflect.rootContext.asInstanceOf
val classdef = rdef.asInstanceOf[dotc.ast.tpd.TypeDef]
val ref = classdef.symbol.info.asInstanceOf[dotc.core.Types.ClassInfo].appliedRef
val baseTypes: List[(dotc.core.Symbols.Symbol, dotc.core.Types.Type)] =
ref.baseClasses.map(b => b -> ref.baseType(b))
baseTypes.asInstanceOf[List[(r.Symbol, r.TypeRepr)]]
baseTypes.asInstanceOf[List[(Symbol, TypeRepr)]]
}

def getSupertypes(c: ClassDef) = hackGetSupertypes(qctx.reflect)(c).tail
def getSupertypes(using QuoteContext)(c: ClassDef) = hackGetSupertypes(c).tail

def typeForClass(c: ClassDef): r.TypeRepr =
def typeForClass(c: ClassDef): TypeRepr =
import qctx.reflect._
import dotty.tools.dotc
given dotc.core.Contexts.Context = r.rootContext.asInstanceOf
given dotc.core.Contexts.Context = rootContext.asInstanceOf
val cSym = c.symbol.asInstanceOf[dotc.core.Symbols.Symbol]
cSym.typeRef.appliedTo(cSym.typeParams.map(_.typeRef)).asInstanceOf[r.TypeRepr]
cSym.typeRef.appliedTo(cSym.typeParams.map(_.typeRef)).asInstanceOf[TypeRepr]

object MatchTypeCase:
def unapply(tpe: TypeRepr): Option[(TypeRepr, TypeRepr)] =
Expand Down
5 changes: 3 additions & 2 deletions scala3doc/src/dotty/dokka/tasty/comments/Comments.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import com.vladsch.flexmark.util.{ast => mdu}
import com.vladsch.flexmark.formatter.Formatter
import com.vladsch.flexmark.util.options.MutableDataSet

import scala.tasty.Reflection
class Repr(val r: Reflection)(val sym: r.Symbol)
import scala.quoted._

class Repr(val qctx: QuoteContext)(val sym: qctx.reflect.Symbol)

case class Comment (
body: dkkd.DocTag,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dotty.dokka
package tasty.comments

import scala.jdk.CollectionConverters._
import scala.tasty.Reflection

import org.jetbrains.dokka.model.{doc => dkkd}
import com.vladsch.flexmark.{ast => mda}
Expand All @@ -17,10 +16,10 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter {

// makeshift support for not passing an owner
// see same in wiki.Converter
val r: repr.r.type = if repr == null then null else repr.r
val owner: r.Symbol = if repr == null then null.asInstanceOf[r.Symbol] else repr.sym
val qctx: repr.qctx.type = if repr == null then null else repr.qctx
val owner: qctx.reflect.Symbol = if repr == null then null.asInstanceOf[qctx.reflect.Symbol] else repr.sym

object SymOps extends SymOps[r.type](r)
object SymOps extends SymOps[qctx.type](qctx)
import SymOps._

def convertDocument(doc: mdu.Document): dkkd.DocTag = {
Expand Down Expand Up @@ -178,7 +177,7 @@ class MarkdownConverter(val repr: Repr) extends BaseConverter {
List(dkk.text(resolved)).asJava

withParsedQuery(queryStr) { query =>
MemberLookup.lookup(using r)(query, owner) match {
MemberLookup.lookup(using qctx)(query, owner) match {
case Some((sym, targetText)) =>
dkkd.DocumentationLink(sym.dri, resolveBody(default = targetText), kt.emptyMap)
case None =>
Expand Down
54 changes: 28 additions & 26 deletions scala3doc/src/dotty/dokka/tasty/comments/MemberLookup.scala
Original file line number Diff line number Diff line change
@@ -1,27 +1,28 @@
package dotty.dokka.tasty.comments

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

trait MemberLookup {

def lookup(using r: Reflection)(
def lookup(using QuoteContext)(
query: Query,
owner: r.Symbol,
): Option[(r.Symbol, String)] = lookupOpt(query, Some(owner))
owner: qctx.reflect.Symbol,
): Option[(qctx.reflect.Symbol, String)] = lookupOpt(query, Some(owner))

def lookupOpt(using r: Reflection)(
def lookupOpt(using QuoteContext)(
query: Query,
ownerOpt: Option[r.Symbol],
): Option[(r.Symbol, String)] = {
ownerOpt: Option[qctx.reflect.Symbol],
): Option[(qctx.reflect.Symbol, String)] = {
import qctx.reflect._

def nearestClass(sym: r.Symbol): r.Symbol =
def nearestClass(sym: Symbol): Symbol =
if sym.isClassDef then sym else nearestClass(sym.owner)

def nearestPackage(sym: r.Symbol): r.Symbol =
if sym.flags.is(r.Flags.Package) then sym else nearestPackage(sym.owner)
def nearestPackage(sym: Symbol): Symbol =
if sym.flags.is(Flags.Package) then sym else nearestPackage(sym.owner)

def nearestMembered(sym: r.Symbol): r.Symbol =
if sym.isClassDef || sym.flags.is(r.Flags.Package) then sym else nearestMembered(sym.owner)
def nearestMembered(sym: Symbol): Symbol =
if sym.isClassDef || sym.flags.is(Flags.Package) then sym else nearestMembered(sym.owner)

val res =
ownerOpt match {
Expand All @@ -41,34 +42,35 @@ trait MemberLookup {
downwardLookup(rest.asList, nearestCls).map(_ -> rest.join)
case Query.QualifiedId(Query.Qual.Id(id), _, rest) if id == nearestPkg.name =>
downwardLookup(rest.asList, nearestPkg).map(_ -> rest.join)
case query: Query.QualifiedId => downwardLookup(query.asList, r.defn.RootPackage).map(_ -> query.join)
case query: Query.QualifiedId => downwardLookup(query.asList, defn.RootPackage).map(_ -> query.join)
}

case None =>
downwardLookup(query.asList, r.defn.RootPackage).map(_ -> query.join)
downwardLookup(query.asList, defn.RootPackage).map(_ -> query.join)
}

// println(s"looked up `$query` in ${owner.show}[${owner.flags.show}] as ${res.map(_.show)}")

res
}

private def hackMembersOf(using r: Reflection)(rsym: r.Symbol) = {
private def hackMembersOf(using QuoteContext)(rsym: qctx.reflect.Symbol) = {
import qctx.reflect._
import dotty.tools.dotc
given dotc.core.Contexts.Context = r.rootContext.asInstanceOf
given dotc.core.Contexts.Context = rootContext.asInstanceOf
val sym = rsym.asInstanceOf[dotc.core.Symbols.Symbol]
val members = sym.info.decls.iterator
// println(s"members of ${sym.show} : ${members.map(_.show).mkString(", ")}")
members.asInstanceOf[Iterator[r.Symbol]]
members.asInstanceOf[Iterator[Symbol]]
}

private def localLookup(using r: Reflection)(query: String, owner: r.Symbol): Option[r.Symbol] = {
import r._
private def localLookup(using QuoteContext)(query: String, owner: qctx.reflect.Symbol): Option[qctx.reflect.Symbol] = {
import qctx.reflect._

inline def whenExists(s: Symbol)(otherwise: => Option[r.Symbol]): Option[r.Symbol] =
inline def whenExists(s: Symbol)(otherwise: => Option[Symbol]): Option[Symbol] =
if s.exists then Some(s) else otherwise

def findMatch(syms: Iterator[r.Symbol]): Option[r.Symbol] = {
def findMatch(syms: Iterator[Symbol]): Option[Symbol] = {
// Scaladoc overloading support allows terminal * (and they're meaningless)
val cleanQuery = query.stripSuffix("*")
val (q, forceTerm, forceType) =
Expand All @@ -79,14 +81,14 @@ trait MemberLookup {
else
(cleanQuery, false, false)

def matches(s: r.Symbol): Boolean =
def matches(s: Symbol): Boolean =
s.name == q && (
if forceTerm then s.isTerm
else if forceType then s.isType
else true
)

def hackResolveModule(s: r.Symbol): r.Symbol =
def hackResolveModule(s: Symbol): Symbol =
if s.flags.is(Flags.Object) then s.moduleClass else s

val matched = syms.find(matches)
Expand All @@ -108,14 +110,14 @@ trait MemberLookup {
findMatch(hackMembersOf(owner))
else
owner.tree match {
case tree: r.ClassDef =>
findMatch(tree.body.iterator.collect { case t: r.Definition => t.symbol })
case tree: ClassDef =>
findMatch(tree.body.iterator.collect { case t: Definition => t.symbol })
case _ =>
findMatch(hackMembersOf(owner))
}
}

private def downwardLookup(using r: Reflection)(query: List[String], owner: r.Symbol): Option[r.Symbol] =
private def downwardLookup(using QuoteContext)(query: List[String], owner: qctx.reflect.Symbol): Option[qctx.reflect.Symbol] =
query match {
case Nil => None
case q :: Nil => localLookup(q, owner)
Expand Down
9 changes: 4 additions & 5 deletions scala3doc/src/dotty/dokka/tasty/comments/wiki/Converter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package dotty.dokka.tasty.comments
package wiki

import scala.jdk.CollectionConverters._
import scala.tasty.Reflection

import org.jetbrains.dokka.model.{doc => dkkd}

Expand All @@ -13,10 +12,10 @@ class Converter(val repr: Repr) extends BaseConverter {

// makeshift support for not passing an owner
// see same in MarkdownConverter
val r: repr.r.type = if repr == null then null else repr.r
val owner: r.Symbol = if repr == null then null.asInstanceOf[r.Symbol] else repr.sym
val qctx: repr.qctx.type = if repr == null then null else repr.qctx
val owner: qctx.reflect.Symbol = if repr == null then null.asInstanceOf[qctx.reflect.Symbol] else repr.sym

object SymOps extends SymOps[r.type](r)
object SymOps extends SymOps[qctx.type](qctx)
import SymOps._

def convertBody(body: Body): dkkd.DocTag = {
Expand Down Expand Up @@ -131,7 +130,7 @@ class Converter(val repr: Repr) extends BaseConverter {
}

withParsedQuery(queryStr) { query =>
MemberLookup.lookup(using r)(query, owner) match {
MemberLookup.lookup(using qctx)(query, owner) match {
case Some((sym, targetText)) =>
dkkd.DocumentationLink(sym.dri, resolveBody(default = targetText), kt.emptyMap)
case None =>
Expand Down
Loading