diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b0b847da4e4d..64d9bab3f17e 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -667,6 +667,12 @@ class Definitions { def Unpickler_liftedExpr = ctx.requiredMethod("scala.runtime.quoted.Unpickler.liftedExpr") def Unpickler_unpickleType = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleType") + lazy val TastyUniverseModule = ctx.requiredModule("scala.tasty.Universe") + def TastyUniverseModuleClass(implicit ctx: Context) = TastyUniverseModule.symbol.asClass + + lazy val TastyUniverse_compilationUniverseR = TastyUniverseModule.requiredMethod("compilationUniverse") + def TastyUniverse_compilationUniverse(implicit ctx: Context) = TastyUniverse_compilationUniverseR.symbol + lazy val EqType = ctx.requiredClassRef("scala.Eq") def EqClass(implicit ctx: Context) = EqType.symbol.asClass def EqModule(implicit ctx: Context) = EqClass.companionModule diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 717dc343e201..629edec591d6 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -16,7 +16,6 @@ import dotty.tools.dotc.core.tasty.{TastyPickler, TastyPrinter, TastyString} import scala.quoted.Types._ import scala.quoted.Exprs._ - import scala.reflect.ClassTag object PickledQuotes { @@ -53,11 +52,17 @@ object PickledQuotes { /** Transform the expression into its fully spliced Tree */ def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree = expr match { - case expr: TastyExpr[_] => unpickleExpr(expr) + case expr: TastyExpr[_] => + val unpickled = unpickleExpr(expr) + val force = new TreeTraverser { + def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = traverseChildren(tree) + } + force.traverse(unpickled) + unpickled case expr: LiftedExpr[T] => expr.value match { case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value)) - case value=> Literal(Constant(value)) + case value => Literal(Constant(value)) } case expr: TreeExpr[Tree] @unchecked => expr.tree case expr: FunctionAppliedTo[_, _] => @@ -68,7 +73,7 @@ object PickledQuotes { def quotedTypeToTree(expr: quoted.Type[_])(implicit ctx: Context): Tree = expr match { case expr: TastyType[_] => unpickleType(expr) case expr: TaggedType[_] => classTagToTypeTree(expr.ct) - case expr: TreeType[Tree] @unchecked => expr.tree + case expr: TreeType[Tree] @unchecked => expr.typeTree } /** Unpickle the tree contained in the TastyExpr */ diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 620dd44f84e9..ce0edf52a31c 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1149,7 +1149,10 @@ class TreeUnpickler(reader: TastyReader, val idx = readNat() val args = until(end)(readTerm()) val splice = splices(idx) - val reifiedArgs = args.map(arg => if (arg.isTerm) new TreeExpr(arg, PickledQuotes.pickleExpr(arg)) else new TreeType(arg)) + def wrap(arg: Tree) = + if (arg.isTerm) new TreeExpr(arg, PickledQuotes.pickleExpr(arg)) + else new TreeType(arg) + val reifiedArgs = args.map(wrap) if (isType) { val quotedType = splice.asInstanceOf[Seq[Any] => quoted.Type[_]](reifiedArgs) PickledQuotes.quotedTypeToTree(quotedType) diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala similarity index 82% rename from compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala rename to compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala index fb782c031543..1b478fcc4aba 100644 --- a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteCompiler.scala @@ -20,12 +20,12 @@ import dotty.tools.dotc.util.Positions.Position import dotty.tools.dotc.util.SourceFile import dotty.tools.io.{AbstractFile, Path, PlainFile} -import scala.quoted.Expr +import scala.quoted.{Expr, Type} /** Compiler that takes the contents of a quoted expression `expr` and produces * a class file with `class ' { def apply: Object = expr }`. */ -class ExprCompiler(directory: AbstractFile) extends Compiler { +class QuoteCompiler(directory: AbstractFile) extends Compiler { import tpd._ /** A GenBCode phase that outputs to a virtual directory */ @@ -35,7 +35,7 @@ class ExprCompiler(directory: AbstractFile) extends Compiler { } override protected def frontendPhases: List[List[Phase]] = - List(List(new ExprFrontend(putInClass = true))) + List(List(new QuotedFrontend(putInClass = true))) override protected def picklerPhases: List[List[Phase]] = List(List(new ReifyQuotes)) @@ -50,8 +50,8 @@ class ExprCompiler(directory: AbstractFile) extends Compiler { def outputClassName: TypeName = "Quoted".toTypeName - /** Frontend that receives scala.quoted.Expr as input */ - class ExprFrontend(putInClass: Boolean) extends FrontEnd { + /** Frontend that receives a scala.quoted.Expr or scala.quoted.Type as input */ + class QuotedFrontend(putInClass: Boolean) extends FrontEnd { import tpd._ override def isTyper = false @@ -64,6 +64,11 @@ class ExprCompiler(directory: AbstractFile) extends Compiler { else PickledQuotes.quotedExprToTree(exprUnit.expr) val source = new SourceFile("", Seq()) CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true) + case typeUnit: TypeCompilationUnit => + assert(!putInClass) + val tree = PickledQuotes.quotedTypeToTree(typeUnit.tpe) + val source = new SourceFile("", Seq()) + CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true) } } @@ -93,6 +98,10 @@ class ExprCompiler(directory: AbstractFile) extends Compiler { val units = new ExprCompilationUnit(expr) :: Nil compileUnits(units) } + def compileType(tpe: Type[_]): Unit = { + val units = new TypeCompilationUnit(tpe) :: Nil + compileUnits(units) + } } } diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprDecompiler.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteDecompiler.scala similarity index 57% rename from compiler/src/dotty/tools/dotc/quoted/ExprDecompiler.scala rename to compiler/src/dotty/tools/dotc/quoted/QuoteDecompiler.scala index 94813330815c..01d16a3bd596 100644 --- a/compiler/src/dotty/tools/dotc/quoted/ExprDecompiler.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteDecompiler.scala @@ -4,15 +4,15 @@ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Contexts.Context import dotty.tools.dotc.core.Phases.Phase -/** Compiler that takes the contents of a quoted expression `expr` and outputs it's tree. */ -class ExprDecompiler(output: tpd.Tree => Context => Unit) extends ExprCompiler(null) { +/** Compiler that takes the contents of a quoted expression (or type) and outputs it's tree. */ +class QuoteDecompiler(output: tpd.Tree => Context => Unit) extends QuoteCompiler(null) { override def phases: List[List[Phase]] = List( - List(new ExprFrontend(putInClass = false)), // Create class from Expr + List(new QuotedFrontend(putInClass = false)), // Create class from Expr List(new QuoteTreeOutput(output)) ) class QuoteTreeOutput(output: tpd.Tree => Context => Unit) extends Phase { - override def phaseName: String = "quotePrinter" + override def phaseName: String = "quoteOutput" override def run(implicit ctx: Context): Unit = output(ctx.compilationUnit.tpdTree)(ctx) } } diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala index d0077b540107..37361ba2008d 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteDriver.scala @@ -7,7 +7,7 @@ import dotty.tools.io.{AbstractFile, Directory, PlainDirectory, VirtualDirectory import dotty.tools.repl.AbstractFileClassLoader import dotty.tools.dotc.printing.DecompilerPrinter -import scala.quoted.Expr +import scala.quoted.{Expr, Type} import java.net.URLClassLoader @@ -28,7 +28,7 @@ class QuoteDriver extends Driver { new VirtualDirectory("(memory)", None) } - val driver = new ExprCompiler(outDir) + val driver = new QuoteCompiler(outDir) driver.newRun(ctx).compileExpr(expr) val classLoader = new AbstractFileClassLoader(outDir, this.getClass.getClassLoader) @@ -58,10 +58,22 @@ class QuoteDriver extends Driver { assert(output.isEmpty) output = Some(f(tree, ctx)) } - new ExprDecompiler(registerTree).newRun(ctx).compileExpr(expr) + new QuoteDecompiler(registerTree).newRun(ctx).compileExpr(expr) output.getOrElse(throw new Exception("Could not extract " + expr)) } + def withTypeTree[T](tpe: Type[_], f: (TypTree, Context) => T, settings: Settings[_]): T = { + val (_, ctx: Context) = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh) + + var output: Option[T] = None + def registerTree(tree: tpd.Tree)(ctx: Context): Unit = { + assert(output.isEmpty) + output = Some(f(tree.asInstanceOf[TypTree], ctx)) + } + new QuoteDecompiler(registerTree).newRun(ctx).compileType(tpe) + output.getOrElse(throw new Exception("Could not extract " + tpe)) + } + override def initCtx: Context = { val ictx = super.initCtx.fresh var classpath = System.getProperty("java.class.path") diff --git a/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala b/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala index b223f63bcb65..59ad7a5070be 100644 --- a/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala +++ b/compiler/src/dotty/tools/dotc/quoted/Toolbox.scala @@ -1,10 +1,7 @@ package dotty.tools.dotc.quoted -import dotty.tools.dotc.ast.Trees._ import dotty.tools.dotc.ast.tpd import dotty.tools.dotc.core.Constants._ -import dotty.tools.dotc.core.Contexts.Context -import dotty.tools.dotc.core.quoted.PickledQuotes import dotty.tools.dotc.printing.RefinedPrinter import scala.quoted.Expr @@ -48,19 +45,6 @@ object Toolbox { case _ => new QuoteDriver().show(expr, showSettings) } - def toConstantOpt(expr: Expr[T]): Option[T] = { - def toConstantOpt(tree: Tree): Option[T] = tree match { - case Literal(Constant(c)) => Some(c.asInstanceOf[T]) - case Block(Nil, e) => toConstantOpt(e) - case Inlined(_, Nil, e) => toConstantOpt(e) - case _ => None - } - expr match { - case expr: LiftedExpr[T] => Some(expr.value) - case _ => new QuoteDriver().withTree(expr, (tree, _) => toConstantOpt(tree), Settings.run()) - } - } - } class Settings[T] private (val outDir: Option[String], val rawTree: Boolean, val compilerArgs: List[String]) diff --git a/compiler/src/dotty/tools/dotc/quoted/TypeCompilationUnit.scala b/compiler/src/dotty/tools/dotc/quoted/TypeCompilationUnit.scala new file mode 100644 index 000000000000..d2090b9ec336 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/quoted/TypeCompilationUnit.scala @@ -0,0 +1,11 @@ +package dotty.tools.dotc.quoted + +import dotty.tools.dotc.CompilationUnit +import dotty.tools.dotc.util.NoSource + +import scala.quoted.Type + +/* Compilation unit containing the contents of a quoted type */ +class TypeCompilationUnit(val tpe: Type[_]) extends CompilationUnit(NoSource) { + override def toString = s"Type($tpe)" +} diff --git a/compiler/src/dotty/tools/dotc/tasty/CompilationUniverse.scala b/compiler/src/dotty/tools/dotc/tasty/CompilationUniverse.scala new file mode 100644 index 000000000000..f1863ead499a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tasty/CompilationUniverse.scala @@ -0,0 +1,7 @@ +package dotty.tools.dotc.tasty + +import dotty.tools.dotc.core.Contexts.Context + +class CompilationUniverse(val context: Context) extends scala.tasty.Universe { + val tasty: TastyImpl.type = TastyImpl +} diff --git a/compiler/src/dotty/tools/dotc/tasty/FlagSet.scala b/compiler/src/dotty/tools/dotc/tasty/FlagSet.scala new file mode 100644 index 000000000000..483bd3e242f7 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tasty/FlagSet.scala @@ -0,0 +1,66 @@ +package dotty.tools.dotc.tasty + +import dotty.tools.dotc.core.Flags +import dotty.tools.dotc.core.Flags._ + +class FlagSet(flags: Flags.FlagSet) extends scala.tasty.FlagSet { + + def isProtected: Boolean = flags.is(Protected) + def isAbstract: Boolean = flags.is(Abstract) + def isFinal: Boolean = flags.is(Final) + def isSealed: Boolean = flags.is(Sealed) + def isCase: Boolean = flags.is(Case) + def isImplicit: Boolean = flags.is(Implicit) + def isErased: Boolean = flags.is(Erased) + def isLazy: Boolean = flags.is(Lazy) + def isOverride: Boolean = flags.is(Override) + def isInline: Boolean = flags.is(Inline) + def isMacro: Boolean = flags.is(Macro) + def isStatic: Boolean = flags.is(JavaStatic) + def isObject: Boolean = flags.is(Module) + def isTrait: Boolean = flags.is(Trait) + def isLocal: Boolean = flags.is(Local) + def isSynthetic: Boolean = flags.is(Synthetic) + def isArtifact: Boolean = flags.is(Artifact) + def isMutable: Boolean = flags.is(Mutable) + def isLabel: Boolean = flags.is(Label) + def isFieldAccessor: Boolean = flags.is(Accessor) + def isCaseAcessor: Boolean = flags.is(CaseAccessor) + def isCovariant: Boolean = flags.is(Covariant) + def isContravariant: Boolean = flags.is(Contravariant) + def isScala2X: Boolean = flags.is(Scala2x) + def isDefaultParameterized: Boolean = flags.is(DefaultParameterized) + def isStable: Boolean = flags.is(Stable) + + override def toString: String = { + val flags = List.newBuilder[String] + if (isProtected) flags += "protected " + if (isAbstract) flags += "abstract" + if (isFinal) flags += "final" + if (isSealed) flags += "sealed" + if (isCase) flags += "case" + if (isImplicit) flags += "implicit" + if (isErased) flags += "erased" + if (isLazy) flags += "lazy" + if (isOverride) flags += "override" + if (isInline) flags += "inline" + if (isMacro) flags += "macro" + if (isStatic) flags += "javaStatic" + if (isObject) flags += "module" + if (isTrait) flags += "trait" + if (isLocal) flags += "local" + if (isSynthetic) flags += "synthetic" + if (isArtifact) flags += "artifact" + if (isMutable) flags += "mutable" + if (isLabel) flags += "label" + if (isFieldAccessor) flags += "accessor" + if (isCaseAcessor) flags += "caseAccessor" + if (isCovariant) flags += "covariant" + if (isContravariant) flags += "contravariant" + if (isScala2X) flags += "scala2x" + if (isDefaultParameterized) flags += "defaultParameterized" + if (isStable) flags += "stable" + flags.result().mkString("<", ",", ">") + } + +} diff --git a/compiler/src/dotty/tools/dotc/tasty/FromSymbol.scala b/compiler/src/dotty/tools/dotc/tasty/FromSymbol.scala new file mode 100644 index 000000000000..deaf5cfd9b9a --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tasty/FromSymbol.scala @@ -0,0 +1,35 @@ +package dotty.tools.dotc.tasty + +import dotty.tools.dotc.ast.tpd +import dotty.tools.dotc.core.Contexts.Context +import dotty.tools.dotc.core.Symbols._ +import dotty.tools.dotc.core.Flags._ + +object FromSymbol { + + def definition(sym: Symbol)(implicit ctx: Context): tpd.Tree = { + if (sym.is(Package)) packageDef(sym) + else if (sym == defn.AnyClass) tpd.EmptyTree // FIXME + else if (sym == defn.NothingClass) tpd.EmptyTree // FIXME + else if (sym.isClass) classDef(sym.asClass) + else if (sym.isType) typeDef(sym.asType) + else if (sym.is(Method)) defDef(sym.asTerm) + else valDef(sym.asTerm) + } + + def packageDef(sym: Symbol)(implicit ctx: Context): PackageDefinition = PackageDefinitionImpl(sym) + + def classDef(cls: ClassSymbol)(implicit ctx: Context): tpd.Tree = { + val constr = tpd.DefDef(cls.unforcedDecls.find(_.isPrimaryConstructor).asTerm) + val body = cls.unforcedDecls.filter(!_.isPrimaryConstructor).map(s => definition(s)) + val superArgs = Nil // TODO + tpd.ClassDef(cls, constr, body, superArgs) + } + + def typeDef(sym: TypeSymbol)(implicit ctx: Context): tpd.TypeDef = tpd.TypeDef(sym) + + def defDef(sym: TermSymbol)(implicit ctx: Context): tpd.DefDef = tpd.DefDef(sym) + + def valDef(sym: TermSymbol)(implicit ctx: Context): tpd.ValDef = tpd.ValDef(sym) + +} diff --git a/compiler/src/dotty/tools/dotc/tasty/TastyImpl.scala b/compiler/src/dotty/tools/dotc/tasty/TastyImpl.scala new file mode 100644 index 000000000000..5b52b37ab2cd --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tasty/TastyImpl.scala @@ -0,0 +1,920 @@ +package dotty.tools.dotc.tasty + +import dotty.tools.dotc.ast.{Trees, tpd, untpd} +import dotty.tools.dotc.core +import dotty.tools.dotc.core._ +import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Decorators._ +import dotty.tools.dotc.core.quoted.PickledQuotes +import dotty.tools.dotc.util.SourcePosition + +import scala.quoted +import scala.reflect.ClassTag + +object TastyImpl extends scala.tasty.Tasty { + + // ===== Quotes =================================================== + + def QuotedExprDeco[T](x: quoted.Expr[T]): AbstractQuotedExpr = new AbstractQuotedExpr { + def toTasty(implicit ctx: Context): Term = PickledQuotes.quotedExprToTree(x) + } + + def QuotedTypeDeco[T](x: quoted.Type[T]): AbstractQuotedType = new AbstractQuotedType { + def toTasty(implicit ctx: Context): TypeTree = PickledQuotes.quotedTypeToTree(x) + } + + // ===== Contexts ================================================= + + type Context = Contexts.Context + + def ContextDeco(ctx: Context): AbstractContext = new AbstractContext { + def owner: Definition = FromSymbol.definition(ctx.owner)(ctx) + } + + // ===== Id ======================================================= + + type Id = untpd.Ident + + def IdDeco(x: Id): AbstractId = new AbstractId { + def pos(implicit ctx: Context): Position = x.pos + } + + def idClassTag: ClassTag[Id] = implicitly[ClassTag[Id]] + + object Id extends IdExtractor { + def unapply(x: Id): Option[String] = x match { + case x: untpd.Ident => Some(x.name.toString) // TODO how to make sure it is not a Ident or TypeIdent? Check x.tpe? + case _ => None + } + } + + // ===== Trees ==================================================== + + type Tree = tpd.Tree + + def TreeDeco(t: Tree): AbstractTree = new AbstractTree { + def pos(implicit ctx: Context): Position = t.pos + } + + type PackageClause = tpd.PackageDef + + def packageClauseClassTag: ClassTag[PackageClause] = implicitly[ClassTag[PackageClause]] + + object PackageClause extends PackageClauseExtractor { + def unapply(x: PackageClause)(implicit ctx: Context): Option[(Term, List[Tree])] = x match { + case x: tpd.PackageDef @unchecked => Some((x.pid, x.stats)) + case _ => None + } + } + + def PackageClauseDeco(x: PackageClause): AbstractPackageClause = new AbstractPackageClause { + override def definition: Definition = ??? + } + + // ----- Statements ----------------------------------------------- + + type Statement = tpd.Tree + + type Import = tpd.Import + + def importClassTag: ClassTag[Import] = implicitly[ClassTag[Import]] + + object Import extends ImportExtractor { + def unapply(x: Import)(implicit ctx: Context): Option[(Term, List[ImportSelector])] = x match { + case x: tpd.Import @unchecked => Some((x.expr, x.selectors)) + case _ => None + } + } + + type ImportSelector = untpd.Tree + + def importSelectorClassTag: ClassTag[ImportSelector] = implicitly[ClassTag[ImportSelector]] + + object SimpleSelector extends SimpleSelectorExtractor { + def unapply(x: ImportSelector)(implicit ctx: Context): Option[Id] = x match { + case x: untpd.Ident => Some(x) // TODO make sure it will not match other idents + case _ => None + } + } + + object RenameSelector extends RenameSelectorExtractor { + def unapply(x: ImportSelector)(implicit ctx: Context): Option[(Id, Id)] = x match { + case Trees.Thicket((id1: untpd.Ident) :: (id2: untpd.Ident) :: Nil) if id2.name != nme.WILDCARD => Some(id1, id2) + case _ => None + } + } + + object OmitSelector extends OmitSelectorExtractor { + def unapply(x: ImportSelector)(implicit ctx: Context): Option[Id] = x match { + case Trees.Thicket((id: untpd.Ident) :: Trees.Ident(nme.WILDCARD) :: Nil) => Some(id) + case _ => None + } + } + + // ----- Definitions ---------------------------------------------- + + type Definition = tpd.Tree + + def DefinitionDeco(x: Definition): AbstractDefinition = new AbstractDefinition { + + def owner(implicit ctx: Context): Definition = FromSymbol.definition(x.symbol.owner) + + def mods(implicit ctx: Context): List[Modifier] = { + val privateWithin = x.symbol.privateWithin + val isProtected = x.symbol.is(core.Flags.Protected) + ModFlags(new FlagSet(x.symbol.flags)) :: + (if (privateWithin.exists) List(ModQual(privateWithin.typeRef, isProtected)) else Nil) ::: + x.symbol.annotations.map(t => ModAnnot(t.tree)) + } + + def localContext(implicit ctx: Context): Context = + if (x.hasType && x.symbol.exists) ctx.withOwner(x.symbol) + else ctx + } + + def definitionClassTag: ClassTag[Definition] = implicitly[ClassTag[Definition]] + + // ClassDef + + type ClassDef = tpd.TypeDef + + def classDefClassTag: ClassTag[ClassDef] = implicitly[ClassTag[ClassDef]] + + object ClassDef extends ClassDefExtractor { + def unapply(x: ClassDef)(implicit ctx: Context): Option[(String, DefDef, List[Parent], Option[ValDef], List[Statement])] = x match { + case x: tpd.TypeDef @unchecked if x.isClassDef => + val temp @ Trees.Template(constr, parents, self, _) = x.rhs + val selfVal = if (self.isEmpty) None else Some(self) + Some((x.name.toString, constr, parents, selfVal, temp.body)) + case _ => None + } + } + + // DefDef + + type DefDef = tpd.DefDef + + def defDefClassTag: ClassTag[DefDef] = implicitly[ClassTag[DefDef]] + + object DefDef extends DefDefExtractor { + def unapply(x: DefDef)(implicit ctx: Context): Option[(String, List[TypeDef], List[List[ValDef]], TypeTree, Option[Term])] = x match { + case x: tpd.DefDef @unchecked => + Some((x.name.toString, x.tparams, x.vparamss, x.tpt, if (x.rhs.isEmpty) None else Some(x.rhs))) + case _ => None + } + } + + // ValDef + + type ValDef = tpd.ValDef + + def valDefClassTag: ClassTag[ValDef] = implicitly[ClassTag[ValDef]] + + object ValDef extends ValDefExtractor { + def unapply(x: ValDef)(implicit ctx: Context): Option[(String, TypeTree, Option[Term])] = x match { + case x: tpd.ValDef @unchecked => + Some((x.name.toString, x.tpt, if (x.rhs.isEmpty) None else Some(x.rhs))) + case _ => None + } + } + + // TypeDef + + type TypeDef = tpd.TypeDef + + def typeDefClassTag: ClassTag[TypeDef] = implicitly[ClassTag[TypeDef]] + + object TypeDef extends TypeDefExtractor { + def unapply(x: TypeDef)(implicit ctx: Context): Option[(String, TypeOrBoundsTree /* TypeTree | TypeBoundsTree */)] = x match { + case x: tpd.TypeDef @unchecked if !x.symbol.isClass => Some((x.name.toString, x.rhs)) + case _ => None + } + } + + type PackageDef = PackageDefinition + + def packageDefClassTag: ClassTag[PackageDef] = implicitly[ClassTag[PackageDef]] + + object PackageDef extends PackageDefExtractor { + def unapply(x: PackageDef)(implicit ctx: Context): Option[(String, List[Statement])] = x match { + case x: PackageDefinition => + // FIXME Do not do this eagerly as it forces everithing in the package to be loaded. + // An alternative would be to add it as an extension method instead. + val definitions = + if (x.symbol.is(core.Flags.JavaDefined)) Nil // FIXME should also support java packages + else x.symbol.info.decls.iterator.map(FromSymbol.definition).toList + Some(x.symbol.name.toString, definitions) + case _ => None + } + } + + // ----- Parents -------------------------------------------------- + + type Parent = tpd.Tree + + // ----- Terms ---------------------------------------------------- + + type Term = tpd.Tree + + def TermDeco(t: Term): AbstractTerm = new AbstractTerm { + def pos(implicit ctx: Context): Position = t.pos + def tpe(implicit ctx: Context): Types.Type = t.tpe + } + + def termClassTag: ClassTag[Term] = implicitly[ClassTag[Term]] + + object Term extends TermModule { + + def unapply(x: Term)(implicit ctx: Context): Boolean = x.isTerm + + object Ident extends IdentExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[String] = x match { + case x: tpd.Ident @unchecked if x.isTerm => Some(x.name.show) + case _ => None + } + } + + object Select extends SelectExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, String, Option[Signature])] = x match { + case x: tpd.Select @unchecked if x.isTerm => + val sig = + if (x.symbol.signature == core.Signature.NotAMethod) None + else Some(x.symbol.signature) + Some((x.qualifier, x.name.toString, sig)) + case _ => None + } + } + + object Literal extends LiteralExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[Constant] = x match { + case Trees.Literal(const) => Some(const) + case _ => None + } + } + + object This extends ThisExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[Option[Id]] = x match { + case Trees.This(qual) => Some(if (qual.isEmpty) None else Some(qual)) + case _ => None + } + } + + object New extends NewExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[TypeTree] = x match { + case x: tpd.New @unchecked => Some(x.tpt) + case _ => None + } + } + + object NamedArg extends NamedArgExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(String, Term)] = x match { + case x: tpd.NamedArg @unchecked if x.name.isInstanceOf[Names.TermName] => Some((x.name.toString, x.arg)) + case _ => None + } + } + + object Apply extends ApplyExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[Term])] = x match { + case x: tpd.Apply @unchecked => Some((x.fun, x.args)) + case _ => None + } + } + + object TypeApply extends TypeApplyExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[TypeTree])] = x match { + case x: tpd.TypeApply @unchecked => Some((x.fun, x.args)) + case _ => None + } + } + + object Super extends SuperExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[Id])] = x match { + case x: tpd.Super @unchecked => Some((x.qual, if (x.mix.isEmpty) None else Some(x.mix))) + case _ => None + } + } + + object Typed extends TypedExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, TypeTree)] = x match { + case x: tpd.Typed @unchecked => Some((x.expr, x.tpt)) + case _ => None + } + } + + object Assign extends AssignExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match { + case x: tpd.Assign @unchecked => Some((x.lhs, x.rhs)) + case _ => None + } + } + + object Block extends BlockExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = x match { + case x: tpd.Block @unchecked => Some((x.stats, x.expr)) + case _ => None + } + } + + object Inlined extends InlinedExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[Statement], Term)] = x match { + case x: tpd.Inlined @unchecked => + Some((x.call, x.bindings, x.expansion)) + case _ => None + } + } + + object Lambda extends LambdaExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[TypeTree])] = x match { + case x: tpd.Closure @unchecked => Some((x.meth, if (x.tpt.isEmpty) None else Some(x.tpt))) + case _ => None + } + } + + object If extends IfExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term, Term)] = x match { + case x: tpd.If @unchecked => Some((x.cond, x.thenp, x.elsep)) + case _ => None + } + } + + object Match extends MatchExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef])] = x match { + case x: tpd.Match @unchecked => Some((x.selector, x.cases)) + case _ => None + } + } + + object Try extends TryExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef], Option[Term])] = x match { + case x: tpd.Try @unchecked => Some((x.expr, x.cases, if (x.finalizer.isEmpty) None else Some(x.finalizer))) + case _ => None + } + } + + object Return extends ReturnExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[Term] = x match { + case x: tpd.Return @unchecked => Some(x.expr) + case _ => None + } + } + + object Repeated extends RepeatedExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[List[Term]] = x match { + case x: tpd.SeqLiteral @unchecked => Some(x.elems) + case _ => None + } + } + + object SelectOuter extends SelectOuterExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Int, Type)] = x match { + case x: tpd.Select @unchecked => + x.name match { + case NameKinds.OuterSelectName(_, levels) => Some((x.qualifier, levels, x.tpe.stripTypeVar)) + case _ => None + } + case _ => None + } + } + + } + + // ----- CaseDef -------------------------------------------------- + + type CaseDef = tpd.CaseDef + + def caseDefClassTag: ClassTag[CaseDef] = implicitly[ClassTag[CaseDef]] + + object CaseDef extends CaseDefExtractor { + def unapply(x: CaseDef): Option[(Pattern, Option[Term], Term)] = x match { + case x: tpd.CaseDef @unchecked => + Some(x.pat, if (x.guard.isEmpty) None else Some(x.guard), x.body) + case _ => None + } + } + + // ----- Patterns ------------------------------------------------- + + type Pattern = tpd.Tree + + def PatternDeco(x: Pattern): AbstractPattern = new AbstractPattern { + def pos(implicit ctx: Context): Position = x.pos + def tpe(implicit ctx: Context): Types.Type = x.tpe.stripTypeVar + } + + def patternClassTag: ClassTag[Pattern] = implicitly[ClassTag[Pattern]] + + object Pattern extends PatternModule { + + object Value extends ValueExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[Term] = x match { + case lit: tpd.Literal @unchecked => Some(lit) + case ident: tpd.Ident @unchecked if ident.isTerm => Some(ident) + case _ => None + } + } + + object Bind extends BindExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[(String, Pattern)] = x match { + case x: tpd.Bind @unchecked if x.name.isInstanceOf[Names.TermName] => Some(x.name.toString, x.body) + case _ => None + } + } + + object Unapply extends UnapplyExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[(Term, List[Term], List[Pattern])] = x match { + case x: tpd.UnApply @unchecked => Some(x.fun, x.implicits, x.patterns) + case _ => None + } + } + + object Alternative extends AlternativeExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[List[Pattern]] = x match { + case x: tpd.Alternative @unchecked => Some(x.trees) + case _ => None + } + } + + object TypeTest extends TypeTestExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[TypeTree] = x match { + case x: tpd.Typed @unchecked => Some(x.tpt) + case _ => None + } + } + + } + + + // ----- TypeOrBoundsTree ------------------------------------------------ + + type TypeOrBoundsTree = tpd.Tree + + def TypeOrBoundsTreeDeco(x: TypeOrBoundsTree): AbstractTypeOrBoundsTree = new AbstractTypeOrBoundsTree { + def tpe(implicit ctx: Context): Type = x.tpe.stripTypeVar + } + + // ----- TypeTrees ------------------------------------------------ + + type TypeTree = tpd.Tree + + def TypeTreeDeco(x: TypeTree): AbstractTypeTree = new AbstractTypeTree { + def pos(implicit ctx: Context): Position = x.pos + def tpe(implicit ctx: Context): Types.Type = x.tpe.stripTypeVar + } + + def typeTreeClassTag: ClassTag[TypeTree] = implicitly[ClassTag[TypeTree]] + + object TypeTree extends TypeTreeModule { + + def unapply(x: TypeTree)(implicit ctx: Context): Boolean = x.isType + + object Synthetic extends SyntheticExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Boolean = x match { + case Trees.TypeTree() => true + case _ => false + } + } + + object TypeIdent extends TypeIdentExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[String] = x match { + case x: tpd.Ident @unchecked if x.isType => Some(x.name.toString) + case _ => None + } + } + + object TypeSelect extends TypeSelectExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(Term, String)] = x match { + case x: tpd.Select @unchecked if x.isType => Some(x.qualifier, x.name.toString) + case _ => None + } + } + + object Singleton extends SingletonExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[Term] = x match { + case x: tpd.SingletonTypeTree @unchecked => Some(x.ref) + case _ => None + } + } + + object Refined extends RefinedExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, List[Definition])] = x match { + case x: tpd.RefinedTypeTree @unchecked => Some(x.tpt, x.refinements) + case _ => None + } + } + + object Applied extends AppliedExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, List[TypeTree])] = x match { + case x: tpd.AppliedTypeTree @unchecked => Some(x.tpt, x.args) + case _ => None + } + } + + object Annotated extends AnnotatedExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, Term)] = x match { + case x: tpd.Annotated @unchecked => Some(x.arg, x.annot) + case _ => None + } + } + + object And extends AndExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, TypeTree)] = x match { + case x: tpd.AndTypeTree @unchecked => Some(x.left, x.right) + case _ => None + } + } + + object Or extends OrExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, TypeTree)] = x match { + case x: tpd.OrTypeTree @unchecked => Some(x.left, x.right) + case _ => None + } + } + + object ByName extends ByNameExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[TypeTree] = x match { + case x: tpd.ByNameTypeTree @unchecked => Some(x.result) + case _ => None + } + } + + } + + // ----- TypeBoundsTrees ------------------------------------------------ + + type TypeBoundsTree = tpd.TypeBoundsTree + + def TypeBoundsTreeDeco(x: TypeBoundsTree): AbstractTypeBoundsTree = ??? + + def typeBoundsTreeClassTag: ClassTag[TypeBoundsTree] = implicitly[ClassTag[TypeBoundsTree]] + + object TypeBoundsTree extends TypeBoundsTreeExtractor { + def unapply(x: TypeBoundsTree)(implicit ctx: Context): Option[(TypeTree, TypeTree)] = x match { + case x: tpd.TypeBoundsTree @unchecked => Some(x.lo, x.hi) + case _ => None + } + } + + // ===== Types ==================================================== + + type TypeOrBounds = Types.Type + + // ----- Types ---------------------------------------------------- + + type Type = Types.Type + type RecursiveType = Types.RecType + type LambdaType[ParamInfo <: TypeOrBounds] = Types.LambdaType { type PInfo = ParamInfo } + type MethodType = Types.MethodType + type PolyType = Types.PolyType + type TypeLambda = Types.TypeLambda + + def typeClassTag: ClassTag[Type] = implicitly[ClassTag[Type]] + def recursiveTypeClassTag: ClassTag[RecursiveType] = implicitly[ClassTag[RecursiveType]] + def methodTypeClassTag: ClassTag[MethodType] = implicitly[ClassTag[MethodType]] + def polyTypeClassTag: ClassTag[PolyType] = implicitly[ClassTag[PolyType]] + def typeLambdaClassTag: ClassTag[TypeLambda] = implicitly[ClassTag[TypeLambda]] + + def MethodTypeDeco(x: MethodType): AbstractMethodType = new AbstractMethodType { + def isErased: Boolean = x.isErasedMethod + def isImplicit: Boolean = x.isImplicitMethod + } + + object Type extends TypeModule { + + def unapply(x: Type)(implicit ctx: Context): Boolean = x match { + case x: Types.TypeBounds => false + case x => x != Types.NoPrefix + } + + object ConstantType extends ConstantTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[Constant] = x match { + case Types.ConstantType(value) => Some(value) + case _ => None + } + } + + object SymRef extends SymRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Definition, TypeOrBounds /* Type | NoPrefix */)] = x match { + case tp: Types.NamedType => + tp.designator match { + case sym: Symbol => Some((FromSymbol.definition(sym), tp.prefix)) + case _ => None + } + case _ => None + } + } + + object TermRef extends TermRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(String, TypeOrBounds /* Type | NoPrefix */)] = x match { + case tp: Types.NamedType => + tp.designator match { + case name: Names.TermName => Some(name.toString, tp.prefix) + case _ => None + } + case _ => None + } + } + + object TypeRef extends TypeRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(String, TypeOrBounds /* Type | NoPrefix */)] = x match { + case tp: Types.NamedType => + tp.designator match { + case name: Names.TypeName => Some(name.toString, tp.prefix) + case _ => None + } + case _ => None + } + } + + object SuperType extends SuperTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Type)] = x match { + case Types.SuperType(thistpe, supertpe) => Some(thistpe, supertpe) + case _ => None + } + } + + object Refinement extends RefinementExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, String, TypeOrBounds /* Type | TypeBounds */)] = x match { + case Types.RefinedType(parent, name, info) => Some(parent, name.toString, info) + case _ => None + } + } + + object AppliedType extends AppliedTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, List[TypeOrBounds /* Type | TypeBounds */])] = x match { + case Types.AppliedType(tycon, args) => Some((tycon.stripTypeVar, args.map(_.stripTypeVar))) + case _ => None + } + } + + object AnnotatedType extends AnnotatedTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Term)] = x match { + case Types.AnnotatedType(underlying, annot) => Some((underlying.stripTypeVar, annot.tree)) + case _ => None + } + } + + object AndType extends AndTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Type)] = x match { + case Types.AndType(left, right) => Some(left.stripTypeVar, right.stripTypeVar) + case _ => None + } + } + + object OrType extends OrTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Type)] = x match { + case Types.OrType(left, right) => Some(left.stripTypeVar, right.stripTypeVar) + case _ => None + } + } + + object ByNameType extends ByNameTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[Type] = x match { + case Types.ExprType(resType) => Some(resType.stripTypeVar) + case _ => None + } + } + + object ParamRef extends ParamRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(LambdaType[TypeOrBounds], Int)] = x match { + case Types.TypeParamRef(binder, idx) => + Some(( + binder.asInstanceOf[LambdaType[TypeOrBounds]], // Cast to tpd + idx)) + case _ => None + } + } + + object ThisType extends ThisTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[Type] = x match { + case Types.ThisType(tp) => Some(tp) + case _ => None + } + } + + object RecursiveThis extends RecursiveThisExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[RecursiveType] = x match { + case Types.RecThis(binder) => Some(binder) + case _ => None + } + } + + object RecursiveType extends RecursiveTypeExtractor { + def unapply(x: RecursiveType)(implicit ctx: Context): Option[Type] = x match { + case tp: Types.RecType => Some(tp.underlying.stripTypeVar) + case _ => None + } + } + + object MethodType extends MethodTypeExtractor { + def unapply(x: MethodType)(implicit ctx: Context): Option[(List[String], List[Type], Type)] = x match { + case x: MethodType => Some(x.paramNames.map(_.toString), x.paramInfos, x.resType) + case _ => None + } + } + + object PolyType extends PolyTypeExtractor { + def unapply(x: PolyType)(implicit ctx: Context): Option[(List[String], List[TypeBounds], Type)] = x match { + case x: PolyType => Some(x.paramNames.map(_.toString), x.paramInfos, x.resType) + case _ => None + } + } + + object TypeLambda extends TypeLambdaExtractor { + def unapply(x: TypeLambda)(implicit ctx: Context): Option[(List[String], List[TypeBounds], Type)] = x match { + case x: TypeLambda => Some(x.paramNames.map(_.toString), x.paramInfos, x.resType) + case _ => None + } + } + + } + + // ----- TypeBounds ------------------------------------------------ + + type TypeBounds = Types.TypeBounds + + def typeBoundsClassTag: ClassTag[TypeBounds] = implicitly[ClassTag[TypeBounds]] + + object TypeBounds extends TypeBoundsExtractor { + def unapply(x: TypeBounds)(implicit ctx: Context): Option[(Type, Type)] = x match { + case x: Types.TypeBounds => Some(x.lo, x.hi) + case _ => None + } + } + + // ----- NoPrefix -------------------------------------------------- + + type NoPrefix = Types.NoPrefix.type + + def noPrefixClassTag: ClassTag[NoPrefix] = implicitly[ClassTag[NoPrefix]] + + object NoPrefix extends NoPrefixExtractor { + def unapply(x: NoPrefix)(implicit ctx: Context): Boolean = x == Types.NoPrefix + } + + // ===== Constants ================================================ + + type Constant = Constants.Constant + + def ConstantDeco(x: Constant): AbstractConstant = new AbstractConstant { + def value: Any = x.value + } + + def constantClassTag: ClassTag[Constant] = implicitly[ClassTag[Constant]] + + object Constant extends ConstantModule { + + object Unit extends UnitExtractor { + def unapply(x: Constant): Boolean = x match { + case x: Constants.Constant => x.tag == Constants.UnitTag + case _ => false + } + } + + object Null extends NullExtractor { + def unapply(x: Constant): Boolean = x match { + case x: Constants.Constant => x.tag == Constants.NullTag + case _ => false + } + } + + object Boolean extends BooleanExtractor { + def unapply(x: Constant): Option[Boolean] = x match { + case x: Constants.Constant if x.tag == Constants.BooleanTag => Some(x.booleanValue) + case _ => None + } + } + + object Byte extends ByteExtractor { + def unapply(x: Constant): Option[Byte] = x match { + case x: Constants.Constant if x.tag == Constants.ByteTag => Some(x.byteValue) + case _ => None + } + } + + object Short extends ShortExtractor { + def unapply(x: Constant): Option[Short] = x match { + case x: Constants.Constant if x.tag == Constants.ShortTag => Some(x.shortValue) + case _ => None + } + } + + object Char extends CharExtractor { + def unapply(x: Constant): Option[Char] = x match { + case x: Constants.Constant if x.tag == Constants.CharTag => Some(x.charValue) + case _ => None + } + } + + object Int extends IntExtractor { + def unapply(x: Constant): Option[Int] = x match { + case x: Constants.Constant if x.tag == Constants.IntTag => Some(x.intValue) + case _ => None + } + } + + object Long extends LongExtractor { + def unapply(x: Constant): Option[Long] = x match { + case x: Constants.Constant if x.tag == Constants.LongTag => Some(x.longValue) + case _ => None + } + } + + object Float extends FloatExtractor { + def unapply(x: Constant): Option[Float] = x match { + case x: Constants.Constant if x.tag == Constants.FloatTag => Some(x.floatValue) + case _ => None + } + } + + object Double extends DoubleExtractor { + def unapply(x: Constant): Option[Double] = x match { + case x: Constants.Constant if x.tag == Constants.DoubleTag => Some(x.doubleValue) + case _ => None + } + } + + object String extends StringExtractor { + def unapply(x: Constant): Option[String] = x match { + case x: Constants.Constant if x.tag == Constants.StringTag => Some(x.stringValue) + case _ => None + } + } + + } + + + // ===== Modifier ================================================= + + type Modifier = ModImpl + + trait ModImpl + case class ModAnnot(tree: Term) extends ModImpl + case class ModFlags(flags: FlagSet) extends ModImpl + case class ModQual(tp: Type, protect: Boolean) extends ModImpl + + def modifierClassTag: ClassTag[Modifier] = implicitly[ClassTag[Modifier]] + + + object Modifier extends ModifierModule { + + object Annotation extends AnnotationExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[Term] = x match { + case ModAnnot(tree) => Some(tree) + case _ => None + } + } + + object Flags extends FlagsExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[FlagSet] = x match { + case ModFlags(flags) => Some(flags) + case _ => None + } + } + + object QualifiedPrivate extends QualifiedPrivateExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[Type] = x match { + case ModQual(tp, false) => Some(tp) + case _ => None + } + } + + object QualifiedProtected extends QualifiedProtectedExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[Type] = x match { + case ModQual(tp, true) => Some(tp) + case _ => None + } + } + + } + + // ===== Signature ================================================ + + type Signature = core.Signature + + def signatureClassTag: ClassTag[Signature] = implicitly[ClassTag[Signature]] + + object Signature extends SignatureExtractor { + def unapply(x: Signature)(implicit ctx: Context): Option[(List[String], String)] = { + Some((x.paramsSig.map(_.toString), x.resSig.toString)) + } + } + + // ===== Positions ================================================ + + type Position = SourcePosition + + def PositionDeco(pos: Position): AbstractPosition = new AbstractPosition { + def start = pos.start + def end = pos.end + + def sourceFile = pos.source.file.jpath + + def startLine = pos.startLine + def endLine = pos.endLine + + def startColumn = pos.startColumn + def endColumn = pos.endColumn + } + +} diff --git a/compiler/src/dotty/tools/dotc/tasty/package.scala b/compiler/src/dotty/tools/dotc/tasty/package.scala new file mode 100644 index 000000000000..106bae5f64a0 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/tasty/package.scala @@ -0,0 +1,16 @@ +package dotty.tools.dotc + +import dotty.tools.dotc.ast.Trees.{Tree, Untyped} +import dotty.tools.dotc.core.Symbols.Symbol +import dotty.tools.dotc.core.Types.Type + +package object tasty { + + type PackageDefinition = PackageDefinitionImpl[Type] + + /** Represents the symbol of a definition in tree form */ + case class PackageDefinitionImpl[-T >: Untyped] private[tasty] (symbol: Symbol) extends Tree[T] { + type ThisTree[-T >: Untyped] = PackageDefinitionImpl[T] + } + +} diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index de81b63c9691..c4a96e531189 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -509,11 +509,23 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { private def transformWithCapturer(tree: Tree)(capturer: mutable.Map[Symbol, Tree] => Tree => Tree)(implicit ctx: Context): Tree = { val captured = mutable.LinkedHashMap.empty[Symbol, Tree] val captured2 = capturer(captured) - outer.enteredSyms.foreach(s => capturers.put(s, captured2)) - if (ctx.owner.owner.is(Macro)) - outer.enteredSyms.reverse.foreach(s => captured2(ref(s))) + + def registerCapturer(sym: Symbol): Unit = capturers.put(sym, captured2) + def forceCapture(sym: Symbol): Unit = captured2(ref(sym)) + + outer.enteredSyms.foreach(registerCapturer) + + if (ctx.owner.owner.is(Macro)) { + registerCapturer(defn.TastyUniverse_compilationUniverse) + // Force a macro to have the context in first position + forceCapture(defn.TastyUniverse_compilationUniverse) + // Force all parameters of the macro to be created in the definition order + outer.enteredSyms.reverse.foreach(forceCapture) + } + val tree2 = transform(tree) capturers --= outer.enteredSyms + seq(captured.result().valuesIterator.toList, tree2) } @@ -636,7 +648,8 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { } private def isStage0Value(sym: Symbol)(implicit ctx: Context): Boolean = - sym.is(Inline) && sym.owner.is(Macro) && !defn.isFunctionType(sym.info) + (sym.is(Inline) && sym.owner.is(Macro) && !defn.isFunctionType(sym.info)) || + sym == defn.TastyUniverse_compilationUniverse // intrinsic value at stage 0 private def liftList(list: List[Tree], tpe: Type)(implicit ctx: Context): Tree = { list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) => diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index db624c7f25f1..9435843dd7de 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -14,6 +14,7 @@ import dotty.tools.dotc.core.quoted._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.Symbols._ import dotty.tools.dotc.core.TypeErasure +import dotty.tools.dotc.tasty.CompilationUniverse import scala.util.control.NonFatal import dotty.tools.dotc.util.Positions.Position @@ -36,9 +37,10 @@ object Splicer { val liftedArgs = getLiftedArgs(call, bindings) val interpreter = new Interpreter(pos, classLoader) val interpreted = interpreter.interpretCallToSymbol[Seq[Any] => Object](call.symbol) + val tctx = new tasty.CompilationUniverse(ctx) evaluateMacro(pos) { // Some parts of the macro are evaluated during the unpickling performed in quotedExprToTree - val evaluated = interpreted.map(lambda => lambda(liftedArgs).asInstanceOf[scala.quoted.Expr[Nothing]]) + val evaluated = interpreted.map(lambda => lambda(tctx :: liftedArgs).asInstanceOf[scala.quoted.Expr[Nothing]]) evaluated.fold(tree)(PickledQuotes.quotedExprToTree) } } @@ -50,7 +52,7 @@ object Splicer { */ private def getLiftedArgs(call: Tree, bindings: List[Tree])(implicit ctx: Context): List[Any] = { val bindMap = bindings.collect { - case vdef: ValDef => (vdef.rhs, ref(vdef.symbol)) + case vdef: ValDef => (vdef.rhs, ref(vdef.symbol).withPos(vdef.rhs.pos)) }.toMap def allArgs(call: Tree, acc: List[List[Tree]]): List[List[Tree]] = call match { case call: Apply => allArgs(call.fun, call.args :: acc) @@ -154,8 +156,8 @@ object Splicer { } } - private def extraMsg = ". The most common reason for that is that you cannot use inline macro implementations in the same compilation run that defines them" - + private def extraMsg = ". The most common reason for that is that you cannot use inline macro implementations in the same compilation run that defines them" + private def stopIfRuntimeException[T](thunk: => T): T = { try thunk catch { diff --git a/library/src/scala/quoted/Constant.scala b/library/src/scala/quoted/Constant.scala deleted file mode 100644 index 3e8ea180eaae..000000000000 --- a/library/src/scala/quoted/Constant.scala +++ /dev/null @@ -1,7 +0,0 @@ -package scala.quoted - -import scala.runtime.quoted.Toolbox - -object Constant { - def unapply[T](expr: Expr[T])(implicit runner: Toolbox[T]): Option[T] = runner.toConstantOpt(expr) -} diff --git a/library/src/scala/quoted/Type.scala b/library/src/scala/quoted/Type.scala index 99457263c79a..c340b3207e03 100644 --- a/library/src/scala/quoted/Type.scala +++ b/library/src/scala/quoted/Type.scala @@ -40,7 +40,7 @@ object Types { } /** An Type backed by a tree */ - final class TreeType[Tree](val tree: Tree) extends quoted.Type[Any] { + final class TreeType[Tree](val typeTree: Tree) extends quoted.Type[Any] { override def toString: String = s"Type()" } } diff --git a/library/src/scala/runtime/quoted/Toolbox.scala b/library/src/scala/runtime/quoted/Toolbox.scala index 9fd6c5a18fe4..d7982d09da13 100644 --- a/library/src/scala/runtime/quoted/Toolbox.scala +++ b/library/src/scala/runtime/quoted/Toolbox.scala @@ -3,9 +3,8 @@ package scala.runtime.quoted import scala.annotation.implicitNotFound import scala.quoted.Expr -@implicitNotFound("Could not find implicit Toolbox. Default runner can be imported with `import dotty.tools.dotc.quoted.Toolbox._`") +@implicitNotFound("Could not find implicit quoted.Toolbox. Default toolbox can be imported with `import dotty.tools.dotc.quoted.Toolbox._`") trait Toolbox[T] { def run(expr: Expr[T]): T def show(expr: Expr[T]): String - def toConstantOpt(expr: Expr[T]): Option[T] } diff --git a/library/src/scala/tasty/FlagSet.scala b/library/src/scala/tasty/FlagSet.scala new file mode 100644 index 000000000000..22cc2d245937 --- /dev/null +++ b/library/src/scala/tasty/FlagSet.scala @@ -0,0 +1,30 @@ +package scala.tasty + +trait FlagSet { + def isProtected: Boolean + def isAbstract: Boolean + def isFinal: Boolean + def isSealed: Boolean + def isCase: Boolean + def isImplicit: Boolean + def isErased: Boolean + def isLazy: Boolean + def isOverride: Boolean + def isInline: Boolean + def isMacro: Boolean // inline method containing toplevel splices + def isStatic: Boolean // mapped to static Java member + def isObject: Boolean // an object or its class (used for a ValDef or a ClassDef extends Modifier respectively) + def isTrait: Boolean // a trait (used for a ClassDef) + def isLocal: Boolean // used in conjunction with Private/private[Type] to mean private[this] extends Modifier proctected[this] + def isSynthetic: Boolean // generated by Scala compiler + def isArtifact: Boolean // to be tagged Java Synthetic + def isMutable: Boolean // when used on a ValDef: a var + def isLabel: Boolean // method generated as a label + def isFieldAccessor: Boolean // a getter or setter + def isCaseAcessor: Boolean // getter for class parameter + def isCovariant: Boolean // type parameter marked “+” + def isContravariant: Boolean // type parameter marked “-” + def isScala2X: Boolean // Imported from Scala2.x + def isDefaultParameterized: Boolean // Method with default parameters + def isStable: Boolean // Method that is assumed to be stable +} diff --git a/library/src/scala/tasty/Tasty.scala b/library/src/scala/tasty/Tasty.scala new file mode 100644 index 000000000000..b08c275533c8 --- /dev/null +++ b/library/src/scala/tasty/Tasty.scala @@ -0,0 +1,709 @@ +package scala.tasty + +import scala.reflect.ClassTag + +abstract class Tasty { + + // ===== Quotes =================================================== + + trait AbstractQuotedExpr { + def toTasty(implicit ctx: Context): Term + } + implicit def QuotedExprDeco[T](x: quoted.Expr[T]): AbstractQuotedExpr + + trait AbstractQuotedType { + def toTasty(implicit ctx: Context): TypeTree + } + implicit def QuotedTypeDeco[T](x: quoted.Type[T]): AbstractQuotedType + + // ===== Contexts ================================================= + + type Context + + trait AbstractContext { + def owner: Definition + } + implicit def ContextDeco(x: Context): AbstractContext + + // ===== Id ======================================================= + + type Id + + trait AbstractId extends Positioned + implicit def IdDeco(x: Id): AbstractId + + implicit def idClassTag: ClassTag[Id] + + val Id: IdExtractor + abstract class IdExtractor { + def unapply(x: Id): Option[String] + } + + // ===== Trees ==================================================== + + type Tree + + trait AbstractTree extends Positioned + implicit def TreeDeco(t: Tree): AbstractTree + + type PackageClause <: Tree + + implicit def packageClauseClassTag: ClassTag[PackageClause] + + val PackageClause: PackageClauseExtractor + abstract class PackageClauseExtractor { + def unapply(x: PackageClause)(implicit ctx: Context): Option[(Term, List[Tree])] + } + + trait AbstractPackageClause { + def definition: Definition + } + implicit def PackageClauseDeco(x: PackageClause): AbstractPackageClause + + // ----- Statements ----------------------------------------------- + + type Statement <: Tree + + type Import <: Statement + + implicit def importClassTag: ClassTag[Import] + + val Import: ImportExtractor + abstract class ImportExtractor { + def unapply(x: Import)(implicit ctx: Context): Option[(Term, List[ImportSelector])] + } + + type ImportSelector + + implicit def importSelectorClassTag: ClassTag[ImportSelector] + + val SimpleSelector: SimpleSelectorExtractor + abstract class SimpleSelectorExtractor { + def unapply(x: ImportSelector)(implicit ctx: Context): Option[Id] + } + + val RenameSelector: RenameSelectorExtractor + abstract class RenameSelectorExtractor { + def unapply(x: ImportSelector)(implicit ctx: Context): Option[(Id, Id)] + } + + val OmitSelector: OmitSelectorExtractor + abstract class OmitSelectorExtractor { + def unapply(x: ImportSelector)(implicit ctx: Context): Option[Id] + } + + // ----- Definitions ---------------------------------------------- + + type Definition <: Statement + + implicit def definitionClassTag: ClassTag[Definition] + + trait AbstractDefinition { + def mods(implicit ctx: Context): List[Modifier] + def owner(implicit ctx: Context): Definition + def localContext(implicit ctx: Context): Context + } + implicit def DefinitionDeco(x: Definition): AbstractDefinition + + // ClassDef + + type ClassDef <: Definition + + implicit def classDefClassTag: ClassTag[ClassDef] + + val ClassDef: ClassDefExtractor + abstract class ClassDefExtractor { + def unapply(x: ClassDef)(implicit ctx: Context): Option[(String, DefDef, List[Parent], Option[ValDef], List[Statement])] + } + + // DefDef + + type DefDef <: Definition + + implicit def defDefClassTag: ClassTag[DefDef] + + val DefDef: DefDefExtractor + abstract class DefDefExtractor { + def unapply(x: DefDef)(implicit ctx: Context): Option[(String, List[TypeDef], List[List[ValDef]], TypeTree, Option[Term])] + } + + // ValDef + + type ValDef <: Definition + + implicit def valDefClassTag: ClassTag[ValDef] + + val ValDef: ValDefExtractor + abstract class ValDefExtractor { + def unapply(x: ValDef)(implicit ctx: Context): Option[(String, TypeTree, Option[Term])] + } + + // TypeDef + + type TypeDef <: Definition + + implicit def typeDefClassTag: ClassTag[TypeDef] + + val TypeDef: TypeDefExtractor + abstract class TypeDefExtractor { + def unapply(x: TypeDef)(implicit ctx: Context): Option[(String, TypeOrBoundsTree /* TypeTree | TypeBoundsTree */)] + } + + // PackageDef + + type PackageDef <: Definition + + implicit def packageDefClassTag: ClassTag[PackageDef] + + val PackageDef: PackageDefExtractor + abstract class PackageDefExtractor { + def unapply(x: PackageDef)(implicit ctx: Context): Option[(String, List[Statement])] + } + + // ----- Parents -------------------------------------------------- + + type Parent /* Term | TypeTree */ + + // ----- Terms ---------------------------------------------------- + + type Term <: Statement with Parent + + trait AbstractTerm extends Typed with Positioned + implicit def TermDeco(t: Term): AbstractTerm + + implicit def termClassTag: ClassTag[Term] + + val Term: TermModule + abstract class TermModule { + + def unapply(x: Term)(implicit ctx: Context): Boolean + + val Ident: IdentExtractor + abstract class IdentExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[String] + } + + val Select: SelectExtractor + abstract class SelectExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, String, Option[Signature])] + } + + val Literal: LiteralExtractor + abstract class LiteralExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[Constant] + } + + val This: ThisExtractor + abstract class ThisExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[Option[Id]] + } + + val New: NewExtractor + abstract class NewExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[TypeTree] + } + + val NamedArg: NamedArgExtractor + abstract class NamedArgExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(String, Term)] + } + + val Apply: ApplyExtractor + abstract class ApplyExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[Term])] + } + + val TypeApply: TypeApplyExtractor + abstract class TypeApplyExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[TypeTree])] + } + + val Super: SuperExtractor + abstract class SuperExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[Id])] + } + + val Typed: TypedExtractor + abstract class TypedExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, TypeTree)] + } + + val Assign: AssignExtractor + abstract class AssignExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] + } + + val Block: BlockExtractor + abstract class BlockExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] + } + + val Inlined: InlinedExtractor + abstract class InlinedExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[Definition], Term)] + } + + val Lambda: LambdaExtractor + abstract class LambdaExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[TypeTree])] + } + + val If: IfExtractor + abstract class IfExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term, Term)] + } + + val Match: MatchExtractor + abstract class MatchExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef])] + } + + val Try: TryExtractor + abstract class TryExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef], Option[Term])] + } + + val Return: ReturnExtractor + abstract class ReturnExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[Term] + } + + val Repeated: RepeatedExtractor + abstract class RepeatedExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[List[Term]] + } + + val SelectOuter: SelectOuterExtractor + abstract class SelectOuterExtractor { + def unapply(x: Term)(implicit ctx: Context): Option[(Term, Int, Type)] + } + + } + + // ----- CaseDef -------------------------------------------------- + + type CaseDef + + implicit def caseDefClassTag: ClassTag[CaseDef] + + val CaseDef: CaseDefExtractor + abstract class CaseDefExtractor { + def unapply(x: CaseDef): Option[(Pattern, Option[Term], Term)] + } + + // ----- Patterns ------------------------------------------------- + + type Pattern + + trait AbstractPattern extends Typed with Positioned + implicit def PatternDeco(x: Pattern): AbstractPattern + + implicit def patternClassTag: ClassTag[Pattern] + + val Pattern: PatternModule + abstract class PatternModule { + + val Value: ValueExtractor + abstract class ValueExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[Term] + } + + val Bind: BindExtractor + abstract class BindExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[(String, Pattern)] + } + + val Unapply: UnapplyExtractor + abstract class UnapplyExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[(Term, List[Term], List[Pattern])] + } + + val Alternative: AlternativeExtractor + abstract class AlternativeExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[List[Pattern]] + } + + val TypeTest: TypeTestExtractor + abstract class TypeTestExtractor { + def unapply(x: Pattern)(implicit ctx: Context): Option[TypeTree] + } + + } + + // ----- TypeTrees ------------------------------------------------ + + type TypeOrBoundsTree + + trait AbstractTypeOrBoundsTree { + def tpe(implicit ctx: Context): TypeOrBounds + } + implicit def TypeOrBoundsTreeDeco(x: TypeOrBoundsTree): AbstractTypeOrBoundsTree + + + // ----- TypeTrees ------------------------------------------------ + + type TypeTree <: TypeOrBoundsTree with Parent + + trait AbstractTypeTree extends Typed with Positioned + implicit def TypeTreeDeco(x: TypeTree): AbstractTypeTree + + implicit def typeTreeClassTag: ClassTag[TypeTree] + + val TypeTree: TypeTreeModule + abstract class TypeTreeModule { + + def unapply(x: TypeTree)(implicit ctx: Context): Boolean + + val Synthetic: SyntheticExtractor + abstract class SyntheticExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Boolean + } + + val TypeIdent: TypeIdentExtractor + abstract class TypeIdentExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[String] + } + + val TypeSelect: TypeSelectExtractor + abstract class TypeSelectExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(Term, String)] + } + + val Singleton: SingletonExtractor + abstract class SingletonExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[Term] + } + + val Refined: RefinedExtractor + abstract class RefinedExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, List[Definition])] + } + + val Applied: AppliedExtractor + abstract class AppliedExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, List[TypeTree])] + } + + val Annotated: AnnotatedExtractor + abstract class AnnotatedExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, Term)] + } + + val And: AndExtractor + abstract class AndExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, TypeTree)] + } + + val Or: OrExtractor + abstract class OrExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[(TypeTree, TypeTree)] + } + + val ByName: ByNameExtractor + abstract class ByNameExtractor { + def unapply(x: TypeTree)(implicit ctx: Context): Option[TypeTree] + } + + } + + // ----- TypeBoundsTrees ------------------------------------------------ + + type TypeBoundsTree <: TypeOrBoundsTree + + trait AbstractTypeBoundsTree { + def tpe: TypeBounds + } + implicit def TypeBoundsTreeDeco(x: TypeBoundsTree): AbstractTypeBoundsTree + + implicit def typeBoundsTreeClassTag: ClassTag[TypeBoundsTree] + + val TypeBoundsTree: TypeBoundsTreeExtractor + abstract class TypeBoundsTreeExtractor { + def unapply(x: TypeBoundsTree)(implicit ctx: Context): Option[(TypeTree, TypeTree)] + } + + // ===== Types ==================================================== + + type TypeOrBounds + + trait Typed { + def tpe(implicit ctx: Context): Type + } + + // ----- Types ---------------------------------------------------- + + type Type <: TypeOrBounds + + type RecursiveType <: Type + + type LambdaType[ParamInfo <: TypeOrBounds] <: Type + type MethodType <: LambdaType[Type] + type PolyType <: LambdaType[TypeBounds] + type TypeLambda <: LambdaType[TypeBounds] + + implicit def typeClassTag: ClassTag[Type] + implicit def methodTypeClassTag: ClassTag[MethodType] + implicit def polyTypeClassTag: ClassTag[PolyType] + implicit def typeLambdaClassTag: ClassTag[TypeLambda] + implicit def recursiveTypeClassTag: ClassTag[RecursiveType] + + trait AbstractMethodType { + def isImplicit: Boolean + def isErased: Boolean + } + implicit def MethodTypeDeco(x: MethodType): AbstractMethodType + + val Type: TypeModule + abstract class TypeModule { + + def unapply(x: Type)(implicit ctx: Context): Boolean + + val ConstantType: ConstantTypeExtractor + abstract class ConstantTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[Constant] + } + + val SymRef: SymRefExtractor + abstract class SymRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Definition, TypeOrBounds /* Type | NoPrefix */)] + } + + val TermRef: TermRefExtractor + abstract class TermRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(String, TypeOrBounds /* Type | NoPrefix */)] + } + + val TypeRef: TypeRefExtractor + abstract class TypeRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(String, TypeOrBounds /* Type | NoPrefix */)] + } + + val SuperType: SuperTypeExtractor + abstract class SuperTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Type)] + } + + val Refinement: RefinementExtractor + abstract class RefinementExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, String, TypeOrBounds /* Type | TypeBounds */)] + } + + val AppliedType: AppliedTypeExtractor + abstract class AppliedTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, List[TypeOrBounds /* Type | TypeBounds */])] + } + + val AnnotatedType: AnnotatedTypeExtractor + abstract class AnnotatedTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Term)] + } + + val AndType: AndTypeExtractor + abstract class AndTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Type)] + } + + val OrType: OrTypeExtractor + abstract class OrTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(Type, Type)] + } + + val ByNameType: ByNameTypeExtractor + abstract class ByNameTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[Type] + } + + val ParamRef: ParamRefExtractor + abstract class ParamRefExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[(LambdaType[TypeOrBounds], Int)] + } + + val ThisType: ThisTypeExtractor + abstract class ThisTypeExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[Type] + } + + val RecursiveThis: RecursiveThisExtractor + abstract class RecursiveThisExtractor { + def unapply(x: Type)(implicit ctx: Context): Option[RecursiveType] + } + + val RecursiveType: RecursiveTypeExtractor + abstract class RecursiveTypeExtractor { + def unapply(x: RecursiveType)(implicit ctx: Context): Option[Type] + } + + val MethodType: MethodTypeExtractor + abstract class MethodTypeExtractor { + def unapply(x: MethodType)(implicit ctx: Context): Option[(List[String], List[Type], Type)] + } + + val PolyType: PolyTypeExtractor + abstract class PolyTypeExtractor { + def unapply(x: PolyType)(implicit ctx: Context): Option[(List[String], List[TypeBounds], Type)] + } + + val TypeLambda: TypeLambdaExtractor + abstract class TypeLambdaExtractor { + def unapply(x: TypeLambda)(implicit ctx: Context): Option[(List[String], List[TypeBounds], Type)] + } + + } + + // ----- TypeBounds ----------------------------------------------- + + type TypeBounds <: TypeOrBounds + + implicit def typeBoundsClassTag: ClassTag[TypeBounds] + + val TypeBounds: TypeBoundsExtractor + abstract class TypeBoundsExtractor { + def unapply(x: TypeBounds)(implicit ctx: Context): Option[(Type, Type)] + } + + // ----- NoPrefix ------------------------------------------------- + + type NoPrefix <: TypeOrBounds + + implicit def noPrefixClassTag: ClassTag[NoPrefix] + + val NoPrefix: NoPrefixExtractor + abstract class NoPrefixExtractor { + def unapply(x: NoPrefix)(implicit ctx: Context): Boolean + } + + // ===== Constants ================================================ + + type Constant + trait AbstractConstant { + def value: Any + } + implicit def ConstantDeco(x: Constant): AbstractConstant + + implicit def constantClassTag: ClassTag[Constant] + + val Constant: ConstantModule + abstract class ConstantModule { + + val Unit: UnitExtractor + abstract class UnitExtractor { + def unapply(x: Constant): Boolean + } + + val Null: NullExtractor + abstract class NullExtractor { + def unapply(x: Constant): Boolean + } + + val Boolean: BooleanExtractor + abstract class BooleanExtractor { + def unapply(x: Constant): Option[Boolean] + } + + val Byte: ByteExtractor + abstract class ByteExtractor { + def unapply(x: Constant): Option[Byte] + } + + val Short: ShortExtractor + abstract class ShortExtractor { + def unapply(x: Constant): Option[Short] + } + + val Char: CharExtractor + abstract class CharExtractor { + def unapply(x: Constant): Option[Char] + } + + val Int: IntExtractor + abstract class IntExtractor { + def unapply(x: Constant): Option[Int] + } + + val Long: LongExtractor + abstract class LongExtractor { + def unapply(x: Constant): Option[Long] + } + + val Float: FloatExtractor + abstract class FloatExtractor { + def unapply(x: Constant): Option[Float] + } + + val Double: DoubleExtractor + abstract class DoubleExtractor { + def unapply(x: Constant): Option[Double] + } + + val String: StringExtractor + abstract class StringExtractor { + def unapply(x: Constant): Option[String] + } + + } + + // ===== Modifiers ================================================ + + type Modifier + + implicit def modifierClassTag: ClassTag[Modifier] + + val Modifier: ModifierModule + abstract class ModifierModule { + + val Annotation: AnnotationExtractor + abstract class AnnotationExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[Term] + } + + val Flags: FlagsExtractor + abstract class FlagsExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[FlagSet] + } + + val QualifiedPrivate: QualifiedPrivateExtractor + abstract class QualifiedPrivateExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[Type] + } + + val QualifiedProtected: QualifiedProtectedExtractor + abstract class QualifiedProtectedExtractor { + def unapply(x: Modifier)(implicit ctx: Context): Option[Type] + } + + } + + + // ===== Signature ================================================ + + type Signature + + implicit def signatureClassTag: ClassTag[Signature] + + val Signature: SignatureExtractor + abstract class SignatureExtractor { + def unapply(x: Signature)(implicit ctx: Context): Option[(List[String], String)] + } + + // ===== Positions ================================================ + + type Position + + trait AbstractPosition { + def start: Int + def end: Int + + def sourceFile: java.nio.file.Path + + def startLine: Int + def startColumn: Int + def endLine: Int + def endColumn: Int + } + implicit def PositionDeco(pos: Position): AbstractPosition + + trait Positioned { + def pos(implicit ctx: Context): Position + } + +} diff --git a/library/src/scala/tasty/Universe.scala b/library/src/scala/tasty/Universe.scala new file mode 100644 index 000000000000..3f2f4fa678f4 --- /dev/null +++ b/library/src/scala/tasty/Universe.scala @@ -0,0 +1,11 @@ +package scala.tasty + +trait Universe { + implicit val tasty: Tasty + implicit val context: tasty.Context +} + +object Universe { + /** Compiler context available in a ~ at inline site */ + implicit def compilationUniverse: Universe = throw new Exception("Not in inline macro.") +} diff --git a/library/src/scala/tasty/util/ConstantExtractor.scala b/library/src/scala/tasty/util/ConstantExtractor.scala new file mode 100644 index 000000000000..a4c750721678 --- /dev/null +++ b/library/src/scala/tasty/util/ConstantExtractor.scala @@ -0,0 +1,30 @@ +package scala.tasty.util + +import scala.quoted.Expr +import scala.tasty.Tasty + +/** + * Usage: + * + * ``` + * val Constant = new ConstantExtractor(tasty) + * + * (x: Expr[B]) match { + * case Constant(value) => ... + * case x => ... + * } + * ``` + */ +class ConstantExtractor[T <: Tasty with Singleton](val tasty: T) { + import tasty._ + + def unapply[T](expr: Expr[T])(implicit ctx: Context): Option[T] = { + def const(tree: Term): Option[T] = tree match { + case Term.Literal(c) => Some(c.value.asInstanceOf[T]) + case Term.Block(Nil, e) => const(e) + case Term.Inlined(_, Nil, e) => const(e) + case _ => None + } + const(expr.toTasty) + } +} diff --git a/library/src/scala/tasty/util/TastyPrinter.scala b/library/src/scala/tasty/util/TastyPrinter.scala new file mode 100644 index 000000000000..a8191369a9a3 --- /dev/null +++ b/library/src/scala/tasty/util/TastyPrinter.scala @@ -0,0 +1,302 @@ +package scala.tasty.util + +import scala.tasty.Tasty + +class TastyPrinter[T <: Tasty with Singleton](val tasty: T) { + import tasty._ + + def stringOfTree(tree: Tree)(implicit ctx: Context): String = + new Buffer().visitTree(tree).result() + + def stringOfTypeTree(tree: TypeOrBoundsTree)(implicit ctx: Context): String = + new Buffer().visitTypeTree(tree).result() + + def stringOfType(tpe: TypeOrBounds)(implicit ctx: Context): String = + new Buffer().visitType(tpe).result() + + def stringOfModifier(mod: Modifier)(implicit ctx: Context): String = + new Buffer().visitModifier(mod).result() + + def stringOfConstant(const: Constant)(implicit ctx: Context): String = + new Buffer().visitConstant(const).result() + + private class Buffer(implicit ctx: Context) { self => + + private val sb: StringBuilder = new StringBuilder + + def result(): String = sb.result() + + def visitTree(x: Tree): Buffer = x match { + case Term.Ident(name) => + this += "Ident(" += name += ")" + case Term.Select(qualifier, name, signature) => + this += "Select(" += qualifier += ", " += name += ", " += signature += ")" + case Term.This(qual) => + this += "This(" += qual += ")" + case Term.Super(qual, mix) => + this += "TypeApply(" += qual += ", " += mix += ")" + case Term.Apply(fun, args) => + this += "Apply(" += fun += ", " ++= args += ")" + case Term.TypeApply(fun, args) => + this += "TypeApply(" += fun += ", " ++= args += ")" + case Term.Literal(const) => + this += "Literal(" += const += ")" + case Term.New(tpt) => + this += "New(" += tpt += ")" + case Term.Typed(expr, tpt) => + this += "Typed(" += expr += ", " += tpt += ")" + case Term.NamedArg(name, arg) => + this += "NamedArg(" += name += ", " += arg += ")" + case Term.Assign(lhs, rhs) => + this += "Assign(" += lhs += ", " += rhs += ")" + case Term.Block(stats, expr) => + this += "Block(" ++= stats += ", " += expr += ")" + case Term.If(cond, thenp, elsep) => + this += "If(" += cond += ", " += thenp += ", " += elsep += ")" + case Term.Lambda(meth, tpt) => + this += "Lambda(" += meth += ", " += tpt += ")" + case Term.Match(selector, cases) => + this += "Match(" += selector += ", " ++= cases += ")" + case Term.Return(expr) => + this += "Return(" += expr += ")" + case Term.Try(block, handlers, finalizer) => + this += "Try(" += block += ", " ++= handlers += ", " += finalizer += ")" + case Term.Repeated(elems) => + this += "Repeated(" ++= elems += ")" + case Term.Inlined(call, bindings, expansion) => + this += "Inlined(" += call += ", " ++= bindings += ", " += expansion += ")" + case ValDef(name, tpt, rhs) => + this += "ValDef(" += name += ", " += tpt += ", " += rhs += ")" + case DefDef(name, typeParams, paramss, returnTpt, rhs) => + this += "DefDef(" += name += ", " ++= typeParams += ", " +++= paramss += ", " += returnTpt += ", " += rhs += ")" + case TypeDef(name, rhs) => + this += "TypeDef(" += name += ", " += rhs += ")" + case ClassDef(name, constr, parents, self, body) => + this += "ClassDef(" += name += ", " += constr += ", " + visitList[Parent](parents, { + case parent @ Term() => this += parent + case parent @ TypeTree() => this += parent + }) + this += ", " += self += ", " ++= body += ")" + case PackageDef(name, members) => + this += "PackageDef(" += name += ", " ++= members += ")" + case Import(expr, selectors) => + this += "Import(" += expr += ", " ++= selectors += ")" + case PackageClause(pid, stats) => + this += "PackageClause(" += pid += ", " ++= stats += ")" + } + + def visitTypeTree(x: TypeOrBoundsTree): Buffer = x match { + case TypeTree.Synthetic() => + this += "Synthetic()" + case TypeTree.TypeIdent(name) => + this += "TypeIdent(" += name += ")" + case TypeTree.TypeSelect(qualifier, name) => + this += "TypeSelect(" += qualifier += ", " += name += ")" + case TypeTree.Singleton(ref) => + this += "Singleton(" += ref += ")" + case TypeTree.And(left, right) => + this += "And(" += left += ", " += right += ")" + case TypeTree.Or(left, right) => + this += "Or(" += left += ", " += right += ")" + case TypeTree.Refined(tpt, refinements) => + this += "Refined(" += tpt += ", " ++= refinements += ")" + case TypeTree.Applied(tpt, args) => + this += "Applied(" += tpt += ", " ++= args += ")" + case TypeTree.ByName(result) => + this += "ByName(" += result += ")" + case TypeTree.Annotated(arg, annot) => + this += "Annotated(" += arg += ", " += annot += ")" + case TypeBoundsTree(lo, hi) => + this += "TypeBoundsTree(" += lo += ", " += hi += ")" + } + + def visitCaseDef(x: CaseDef): Buffer = { + val CaseDef(pat, guard, body) = x + this += "CaseDef(" += pat += ", " += guard += ", " += body += ")" + } + + def visitPattern(x: Pattern): Buffer = x match { + case Pattern.Value(v) => + this += "Value(" += v += ")" + case Pattern.Bind(name, body) => + this += "Bind(" += name += ", " += body += ")" + case Pattern.Unapply(fun, implicits, patterns) => + this += "Unapply(" += fun += ", " ++= implicits += ", " ++= patterns += ")" + case Pattern.Alternative(patterns) => + this += "Alternative(" ++= patterns += ")" + case Pattern.TypeTest(tpt) => + this += "TypeTest(" += tpt += ")" + } + + def visitConstant(x: Constant): Buffer = x match { + case Constant.Unit() => this += "Unit()" + case Constant.Null() => this += "Null()" + case Constant.Boolean(value) => this += "Boolean(" += value += ")" + case Constant.Byte(value) => this += "Byte(" += value += ")" + case Constant.Short(value) => this += "Short(" += value += ")" + case Constant.Char(value) => this += "Char(" += value += ")" + case Constant.Int(value) => this += "Int(" += value.toString += ")" + case Constant.Long(value) => this += "Long(" += value += ")" + case Constant.Float(value) => this += "Float(" += value += ")" + case Constant.Double(value) => this += "Double(" += value += ")" + case Constant.String(value) => this += "String(" += value += ")" + } + + def visitType(x: TypeOrBounds): Buffer = x match { + case Type.ConstantType(value) => + this += "ConstantType(" += value += ")" + case Type.SymRef(sym, qual) => + def visitName(sym: Definition): Buffer = sym match { + case ValDef(name, _, _) => this += name + case DefDef(name, _, _, _, _) => this += name + case TypeDef(name, _) => this += name + case ClassDef(name, _, _, _, _) => this += name + case PackageDef(name, _) => this += name + case _ => this += "#" + } + this += "SymRef(" + visitName(sym) + this += ", " += qual += ")" + case Type.TermRef(name, qual) => + this += "TermRef(" += name += ", " += qual += ")" + case Type.TypeRef(name, qual) => + this += "TypeRef(" += name += ", " += qual += ")" + case Type.Refinement(parent, name, info) => + this += "Refinement(" += parent += ", " += name += ", " += info += ")" + case Type.AppliedType(tycon, args) => + this += "AppliedType(" += tycon += ", " ++= args += ")" + case Type.AnnotatedType(underlying, annot) => + this += "AnnotatedType(" += underlying += ", " += annot += ")" + case Type.AndType(left, right) => + this += "AndType(" += left += ", " += right += ")" + case Type.OrType(left, right) => + this += "OrType(" += left += ", " += right += ")" + case Type.ByNameType(underlying) => + this += "ByNameType(" += underlying += ")" + case Type.ParamRef(binder, idx) => + this += "ParamRef(" += binder+= ", " += idx += ")" + case Type.ThisType(tp) => + this += "ThisType(" += tp += ")" + case Type.RecursiveThis(binder) => + this += "RecursiveThis(" += binder += ")" + case Type.MethodType(argNames, argTypes, resType) => + this += "MethodType(" ++= argNames += ", " ++= argTypes += ", " += resType += ")" + case Type.PolyType(argNames, argBounds, resType) => + this += "PolyType(" ++= argNames += ", " ++= argBounds += ", " += resType += ")" + case Type.TypeLambda(argNames, argBounds, resType) => + this += "TypeLambda(" ++= argNames += ", " ++= argBounds += ", " += resType += ")" + case TypeBounds(lo, hi) => + this += "TypeBounds(" += lo += ", " += hi += ")" + case NoPrefix() => + this += "NoPrefix" + } + + def visitModifier(x: Modifier): Buffer = x match { + case Modifier.Flags(flags) => this += "Flags(" += flags.toString += ")" + case Modifier.QualifiedPrivate(tp) => this += "QualifiedPrivate(" += tp += ")" + case Modifier.QualifiedProtected(tp) => this += "QualifiedProtected(" += tp += ")" + case Modifier.Annotation(tree) => this += "Annotation(" += tree += ")" + } + + def visitId(x: Id): Buffer = { + val Id(name) = x + this += "Id(" += name += ")" + } + + def visitSignature(sig: Signature): Buffer = { + val Signature(params, res) = sig + this += "Signature(" ++= params += ", " += res += ")" + } + + def visitImportSelector(sel: ImportSelector): Buffer = sel match { + case SimpleSelector(id) => this += "SimpleSelector(" += id += ")" + case RenameSelector(id1, id2) => this += "RenameSelector(" += id1 += ", " += id2 += ")" + case OmitSelector(id) => this += "OmitSelector(" += id += ")" + } + + def +=(x: Boolean): Buffer = { sb.append(x); this } + def +=(x: Byte): Buffer = { sb.append(x); this } + def +=(x: Short): Buffer = { sb.append(x); this } + def +=(x: Int): Buffer = { sb.append(x); this } + def +=(x: Long): Buffer = { sb.append(x); this } + def +=(x: Float): Buffer = { sb.append(x); this } + def +=(x: Double): Buffer = { sb.append(x); this } + def +=(x: Char): Buffer = { sb.append(x); this } + def +=(x: String): Buffer = { sb.append(x); this } + + def ++=(xs: List[String]): Buffer = visitList[String](xs, +=) + + private implicit class TreeOps(buff: Buffer) { + def +=(x: Tree): Buffer = { visitTree(x); buff } + def +=(x: Option[Tree]): Buffer = { visitOption(x, visitTree); buff } + def ++=(x: List[Tree]): Buffer = { visitList(x, visitTree); buff } + def +++=(x: List[List[Tree]]): Buffer = { visitList(x, ++=); buff } + } + + private implicit class CaseDefOps(buff: Buffer) { + def +=(x: CaseDef): Buffer = { visitCaseDef(x); buff } + def ++=(x: List[CaseDef]): Buffer = { visitList(x, visitCaseDef); buff } + } + + private implicit class PatternOps(buff: Buffer) { + def +=(x: Pattern): Buffer = { visitPattern(x); buff } + def ++=(x: List[Pattern]): Buffer = { visitList(x, visitPattern); buff } + } + + private implicit class ConstantOps(buff: Buffer) { + def +=(x: Constant): Buffer = { visitConstant(x); buff } + } + + private implicit class TypeTreeOps(buff: Buffer) { + def +=(x: TypeOrBoundsTree): Buffer = { visitTypeTree(x); buff } + def +=(x: Option[TypeTree]): Buffer = { visitOption(x, visitTypeTree); buff } + def ++=(x: List[TypeTree]): Buffer = { visitList(x, visitTypeTree); buff } + } + + private implicit class TypeOps(buff: Buffer) { + def +=(x: TypeOrBounds): Buffer = { visitType(x); buff } + def ++=(x: List[TypeOrBounds]): Buffer = { visitList(x, visitType); buff } + } + + private implicit class IdOps(buff: Buffer) { + def +=(x: Id): Buffer = { visitId(x); buff } + def +=(x: Option[Id]): Buffer = { visitOption(x, visitId); buff } + } + + private implicit class SignatureOps(buff: Buffer) { + def +=(x: Option[Signature]): Buffer = { visitOption(x, visitSignature); buff } + } + + private implicit class ImportSelectorOps(buff: Buffer) { + def ++=(x: List[ImportSelector]): Buffer = { visitList(x, visitImportSelector); buff } + } + + private def visitOption[U](opt: Option[U], visit: U => Buffer): Buffer = opt match { + case Some(x) => + this += "Some(" + visit(x) + this += ")" + case _ => + this += "None" + } + + private def visitList[U](list: List[U], visit: U => Buffer): Buffer = list match { + case x0 :: xs => + this += "List(" + visit(x0) + def visitNext(xs: List[U]): Unit = xs match { + case y :: ys => + this += ", " + visit(y) + visitNext(ys) + case Nil => + } + visitNext(xs) + this += ")" + case Nil => + this += "Nil" + } + } + +} diff --git a/library/src/scala/tasty/util/TreeAccumulator.scala b/library/src/scala/tasty/util/TreeAccumulator.scala new file mode 100644 index 000000000000..e147340715c4 --- /dev/null +++ b/library/src/scala/tasty/util/TreeAccumulator.scala @@ -0,0 +1,112 @@ +package scala.tasty.util + +import scala.tasty.Tasty + +abstract class TreeAccumulator[X, T <: Tasty with Singleton](val tasty: T) { + import tasty._ + + // Ties the knot of the traversal: call `foldOver(x, tree))` to dive in the `tree` node. + def foldTree(x: X, tree: Tree)(implicit ctx: Context): X + def foldTypeTree(x: X, tree: TypeOrBoundsTree)(implicit ctx: Context): X + def foldCaseDef(x: X, tree: CaseDef)(implicit ctx: Context): X + def foldPattern(x: X, tree: Pattern)(implicit ctx: Context): X + + def foldTrees(x: X, trees: Iterable[Tree])(implicit ctx: Context): X = (x /: trees)(foldTree) + def foldTypeTrees(x: X, trees: Iterable[TypeOrBoundsTree])(implicit ctx: Context): X = (x /: trees)(foldTypeTree) + def foldCaseDefs(x: X, trees: Iterable[CaseDef])(implicit ctx: Context): X = (x /: trees)(foldCaseDef) + def foldPatterns(x: X, trees: Iterable[Pattern])(implicit ctx: Context): X = (x /: trees)(foldPattern) + private def foldParents(x: X, trees: Iterable[Parent])(implicit ctx: Context): X = (x /: trees)(foldOverParent) + + def foldOverTree(x: X, tree: Tree)(implicit ctx: Context): X = { + def localCtx(definition: Definition): Context = definition.localContext + tree match { + case Term.Ident(_) => + x + case Term.Select(qualifier, _, _) => + foldTree(x, qualifier) + case Term.This(qual) => + x + case Term.Super(qual, _) => + foldTree(x, qual) + case Term.Apply(fun, args) => + foldTrees(foldTree(x, fun), args) + case Term.TypeApply(fun, args) => + foldTypeTrees(foldTree(x, fun), args) + case Term.Literal(const) => + x + case Term.New(tpt) => + foldTypeTree(x, tpt) + case Term.Typed(expr, tpt) => + foldTypeTree(foldTree(x, expr), tpt) + case Term.NamedArg(_, arg) => + foldTree(x, arg) + case Term.Assign(lhs, rhs) => + foldTree(foldTree(x, lhs), rhs) + case Term.Block(stats, expr) => + foldTree(foldTrees(x, stats), expr) + case Term.If(cond, thenp, elsep) => + foldTree(foldTree(foldTree(x, cond), thenp), elsep) + case Term.Lambda(meth, tpt) => + val a = foldTree(x, meth) + tpt.fold(a)(b => foldTypeTree(a, b)) + case Term.Match(selector, cases) => + foldCaseDefs(foldTree(x, selector), cases) + case Term.Return(expr) => + foldTree(x, expr) + case Term.Try(block, handler, finalizer) => + foldTrees(foldCaseDefs(foldTree(x, block), handler), finalizer) + case Term.Repeated(elems) => + foldTrees(x, elems) + case Term.Inlined(call, bindings, expansion) => + foldTree(foldTrees(x, bindings), expansion) + case vdef @ ValDef(_, tpt, rhs) => + implicit val ctx = localCtx(vdef) + foldTrees(foldTypeTree(x, tpt), rhs) + case ddef @ DefDef(_, tparams, vparamss, tpt, rhs) => + implicit val ctx = localCtx(ddef) + foldTrees(foldTypeTree((foldTrees(x, tparams) /: vparamss)(foldTrees), tpt), rhs) + case tdef @ TypeDef(_, rhs) => + implicit val ctx = localCtx(tdef) + foldTypeTree(x, rhs) + case cdef @ ClassDef(_, constr, parents, self, body) => + implicit val ctx = localCtx(cdef) + foldTrees(foldTrees(foldParents(foldTree(x, constr), parents), self), body) + case Import(expr, selectors) => + foldTree(x, expr) + case clause @ PackageClause(pid, stats) => + foldTrees(foldTree(x, pid), stats)(localCtx(clause.definition)) + } + } + + def foldOverTypeTree(x: X, tree: TypeOrBoundsTree)(implicit ctx: Context): X = tree match { + case TypeTree.Synthetic() => x + case TypeTree.TypeIdent(_) => x + case TypeTree.TypeSelect(qualifier, _) => foldTree(x, qualifier) + case TypeTree.Singleton(ref) => foldTree(x, ref) + case TypeTree.And(left, right) => foldTypeTree(foldTypeTree(x, left), right) + case TypeTree.Or(left, right) => foldTypeTree(foldTypeTree(x, left), right) + case TypeTree.Refined(tpt, refinements) => foldTrees(foldTypeTree(x, tpt), refinements) + case TypeTree.Applied(tpt, args) => foldTypeTrees(foldTypeTree(x, tpt), args) + case TypeTree.ByName(result) => foldTypeTree(x, result) + case TypeTree.Annotated(arg, annot) => foldTree(foldTypeTree(x, arg), annot) + case TypeBoundsTree(lo, hi) => foldTypeTree(foldTypeTree(x, lo), hi) + } + + def foldOverCaseDef(x: X, tree: CaseDef)(implicit ctx: Context): X = tree match { + case CaseDef(pat, guard, body) => foldTree(foldTrees(foldPattern(x, pat), guard), body) + } + + def foldOverPattern(x: X, tree: Pattern)(implicit ctx: Context): X = tree match { + case Pattern.Value(v) => foldTree(x, v) + case Pattern.Bind(_, body) => foldPattern(x, body) + case Pattern.Unapply(fun, implicits, patterns) => foldPatterns(foldTrees(foldTree(x, fun), implicits), patterns) + case Pattern.Alternative(patterns) => foldPatterns(x, patterns) + case Pattern.TypeTest(tpt) => foldTypeTree(x, tpt) + } + + private def foldOverParent(x: X, tree: Parent)(implicit ctx: Context): X = tree match { + case tree @ Term() => foldOverTree(x, tree) + case tree @ TypeTree() => foldOverTypeTree(x, tree) + } + +} diff --git a/library/src/scala/tasty/util/TreeTraverser.scala b/library/src/scala/tasty/util/TreeTraverser.scala new file mode 100644 index 000000000000..c835b8fb2413 --- /dev/null +++ b/library/src/scala/tasty/util/TreeTraverser.scala @@ -0,0 +1,23 @@ +package scala.tasty.util + +import scala.tasty.Tasty + +abstract class TreeTraverser[T <: Tasty with Singleton](tasty0: T) extends TreeAccumulator[Unit, T](tasty0) { + import tasty._ + + def traverseTree(tree: Tree)(implicit ctx: Context): Unit = traverseTreeChildren(tree) + def traverseTypeTree(tree: TypeOrBoundsTree)(implicit ctx: Context): Unit = traverseTypeTreeChildren(tree) + def traverseCaseDef(tree: CaseDef)(implicit ctx: Context): Unit = traverseCaseDefChildren(tree) + def traversePattern(tree: Pattern)(implicit ctx: Context): Unit = traversePatternChildren(tree) + + def foldTree(x: Unit, tree: Tree)(implicit ctx: Context): Unit = traverseTree(tree) + def foldTypeTree(x: Unit, tree: TypeOrBoundsTree)(implicit ctx: Context) = traverseTypeTree(tree) + def foldCaseDef(x: Unit, tree: CaseDef)(implicit ctx: Context) = traverseCaseDef(tree) + def foldPattern(x: Unit, tree: Pattern)(implicit ctx: Context) = traversePattern(tree) + + protected def traverseTreeChildren(tree: Tree)(implicit ctx: Context): Unit = foldOverTree((), tree) + protected def traverseTypeTreeChildren(tree: TypeOrBoundsTree)(implicit ctx: Context): Unit = foldOverTypeTree((), tree) + protected def traverseCaseDefChildren(tree: CaseDef)(implicit ctx: Context): Unit = foldOverCaseDef((), tree) + protected def traversePatternChildren(tree: Pattern)(implicit ctx: Context): Unit = foldOverPattern((), tree) + +} diff --git a/tests/pos/tasty/definitions.scala b/tests/pos/tasty/definitions.scala index 04294712eb8f..1c0d85f7c291 100644 --- a/tests/pos/tasty/definitions.scala +++ b/tests/pos/tasty/definitions.scala @@ -4,14 +4,13 @@ object definitions { // ====== Trees ====================================== - trait Tree extends Positioned + sealed trait Tree // Top level statement // ------ Statements --------------------------------- - sealed trait TopLevelStatement extends Tree - sealed trait Statement extends TopLevelStatement + sealed trait Statement extends Tree - case class PackageClause(pkg: Term, body: List[TopLevelStatement]) extends TopLevelStatement + case class PackageClause(pkg: Term, body: List[Tree]) extends Tree case class Import(expr: Term, selector: List[ImportSelector]) extends Statement @@ -75,7 +74,7 @@ object definitions { } /** Trees denoting types */ - enum TypeTree extends Tree { + enum TypeTree extends Positioned { def tpe: Type = ??? case Synthetic() case Ident(name: String, override val tpe: Type) @@ -95,7 +94,7 @@ object definitions { } /** Trees denoting patterns */ - enum Pattern extends Tree { + enum Pattern extends Positioned { def tpe: Type = ??? case Value(v: Term) case Bind(name: String, pat: Pattern) @@ -116,8 +115,8 @@ object definitions { case class ConstantType(value: Constant) extends Type case class SymRef(sym: Definition, qualifier: Type | NoPrefix = NoPrefix) extends Type - case class TypeNameRef(name: String, qualifier: Type | NoPrefix = NoPrefix) extends Type // NoPrefix means: select from _root_ - case class TermNameRef(name: String, qualifier: Type | NoPrefix = NoPrefix) extends Type // NoPrefix means: select from _root_ + case class TypeRef(name: String, qualifier: Type | NoPrefix = NoPrefix) extends Type // NoPrefix means: select from _root_ + case class TermRef(name: String, qualifier: Type | NoPrefix = NoPrefix) extends Type // NoPrefix means: select from _root_ case class SuperType(thistp: Type, underlying: Type) extends Type case class Refinement(underlying: Type, name: String, tpe: Type | TypeBounds) extends Type case class AppliedType(tycon: Type, args: List[Type | TypeBounds]) extends Type diff --git a/tests/run-with-compiler/quote-run-constants-extract-1.scala b/tests/run-with-compiler/quote-run-constants-extract-1.scala deleted file mode 100644 index e019defeba11..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-1.scala +++ /dev/null @@ -1,15 +0,0 @@ -import scala.quoted._ - -import dotty.tools.dotc.quoted.Toolbox._ - -object Test { - - def main(args: Array[String]): Unit = { - 3.toExpr match { case Constant(n) => println(n) } - '(4) match { case Constant(n) => println(n) } - '("abc") match { case Constant(n) => println(n) } - '(null) match { case Constant(n) => println(n) } - - '(new Object) match { case Constant(n) => println(n); case _ => println("OK") } - } -} diff --git a/tests/run-with-compiler/quote-run-constants-extract-2.check b/tests/run-with-compiler/quote-run-constants-extract-2.check deleted file mode 100644 index 1cc77395177a..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-2.check +++ /dev/null @@ -1,5 +0,0 @@ -{ - val y: Double = 3.0.*(3.0) - y -} -9.0 \ No newline at end of file diff --git a/tests/run-with-compiler/quote-run-constants-extract-2.scala b/tests/run-with-compiler/quote-run-constants-extract-2.scala deleted file mode 100644 index 8c3edbb72cfd..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-2.scala +++ /dev/null @@ -1,30 +0,0 @@ -import scala.quoted._ - -import dotty.tools.dotc.quoted.Toolbox._ - -object Test { - - def main(args: Array[String]): Unit = { - // 2 is a lifted constant - println(power(2.toExpr, 3.0.toExpr).show) - println(power(2.toExpr, 3.0.toExpr).run) - } - - def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { - n match { - case Constant(n1) => powerCode(n1, x) - case _ => '{ dynamicPower(~n, ~x) } - } - } - - private def powerCode(n: Int, x: Expr[Double]): Expr[Double] = - if (n == 0) '(1.0) - else if (n == 1) x - else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } } - else '{ ~x * ~powerCode(n - 1, x) } - - def dynamicPower(n: Int, x: Double): Double = - if (n == 0) 1.0 - else if (n % 2 == 0) dynamicPower(n / 2, x * x) - else x * dynamicPower(n - 1, x) -} diff --git a/tests/run-with-compiler/quote-run-constants-extract-3.check b/tests/run-with-compiler/quote-run-constants-extract-3.check deleted file mode 100644 index fadb88febacc..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-3.check +++ /dev/null @@ -1,5 +0,0 @@ -{ - val y: Double = 3.0.*(3.0) - y -} -9.0 diff --git a/tests/run-with-compiler/quote-run-constants-extract-3.scala b/tests/run-with-compiler/quote-run-constants-extract-3.scala deleted file mode 100644 index 8c3edbb72cfd..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-3.scala +++ /dev/null @@ -1,30 +0,0 @@ -import scala.quoted._ - -import dotty.tools.dotc.quoted.Toolbox._ - -object Test { - - def main(args: Array[String]): Unit = { - // 2 is a lifted constant - println(power(2.toExpr, 3.0.toExpr).show) - println(power(2.toExpr, 3.0.toExpr).run) - } - - def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { - n match { - case Constant(n1) => powerCode(n1, x) - case _ => '{ dynamicPower(~n, ~x) } - } - } - - private def powerCode(n: Int, x: Expr[Double]): Expr[Double] = - if (n == 0) '(1.0) - else if (n == 1) x - else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } } - else '{ ~x * ~powerCode(n - 1, x) } - - def dynamicPower(n: Int, x: Double): Double = - if (n == 0) 1.0 - else if (n % 2 == 0) dynamicPower(n / 2, x * x) - else x * dynamicPower(n - 1, x) -} diff --git a/tests/run-with-compiler/quote-run-constants-extract-4.check b/tests/run-with-compiler/quote-run-constants-extract-4.check deleted file mode 100644 index a446942980e1..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-4.check +++ /dev/null @@ -1,5 +0,0 @@ -{ - val y: Double = 4.0.*(4.0) - y -} -16.0 diff --git a/tests/run-with-compiler/quote-run-constants-extract-4.scala b/tests/run-with-compiler/quote-run-constants-extract-4.scala deleted file mode 100644 index 330549fb6f8e..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-4.scala +++ /dev/null @@ -1,31 +0,0 @@ -import scala.quoted._ - -import dotty.tools.dotc.quoted.Toolbox._ - -object Test { - - def main(args: Array[String]): Unit = { - // n is a lifted constant - val n = 2 - println(power(n.toExpr, 4.0.toExpr).show) - println(power(n.toExpr, 4.0.toExpr).run) - } - - def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { - n match { - case Constant(n1) => powerCode(n1, x) - case _ => '{ dynamicPower(~n, ~x) } - } - } - - private def powerCode(n: Int, x: Expr[Double]): Expr[Double] = - if (n == 0) '(1.0) - else if (n == 1) x - else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } } - else '{ ~x * ~powerCode(n - 1, x) } - - def dynamicPower(n: Int, x: Double): Double = - if (n == 0) 1.0 - else if (n % 2 == 0) dynamicPower(n / 2, x * x) - else x * dynamicPower(n - 1, x) -} diff --git a/tests/run-with-compiler/quote-run-constants-extract-5.check b/tests/run-with-compiler/quote-run-constants-extract-5.check deleted file mode 100644 index 4f9d9a7de996..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-5.check +++ /dev/null @@ -1,5 +0,0 @@ -{ - val y: Double = 5.0.*(5.0) - y -} -25.0 diff --git a/tests/run-with-compiler/quote-run-constants-extract-5.scala b/tests/run-with-compiler/quote-run-constants-extract-5.scala deleted file mode 100644 index 3362a82ee4fe..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-5.scala +++ /dev/null @@ -1,30 +0,0 @@ -import scala.quoted._ - -import dotty.tools.dotc.quoted.Toolbox._ - -object Test { - - def main(args: Array[String]): Unit = { - // n is a constant in a quote - println(power('(2), 5.0.toExpr).show) - println(power('(2), 5.0.toExpr).run) - } - - def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { - n match { - case Constant(n1) => powerCode(n1, x) - case _ => '{ dynamicPower(~n, ~x) } - } - } - - private def powerCode(n: Int, x: Expr[Double]): Expr[Double] = - if (n == 0) '(1.0) - else if (n == 1) x - else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } } - else '{ ~x * ~powerCode(n - 1, x) } - - def dynamicPower(n: Int, x: Double): Double = - if (n == 0) 1.0 - else if (n % 2 == 0) dynamicPower(n / 2, x * x) - else x * dynamicPower(n - 1, x) -} diff --git a/tests/run-with-compiler/quote-run-constants-extract-6.check b/tests/run-with-compiler/quote-run-constants-extract-6.check deleted file mode 100644 index da9e8c9732aa..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-6.check +++ /dev/null @@ -1,8 +0,0 @@ -Test.dynamicPower( - { - println("foo") - 2 - } -, 6.0) -foo -36.0 diff --git a/tests/run-with-compiler/quote-run-constants-extract-6.scala b/tests/run-with-compiler/quote-run-constants-extract-6.scala deleted file mode 100644 index 50110602c844..000000000000 --- a/tests/run-with-compiler/quote-run-constants-extract-6.scala +++ /dev/null @@ -1,31 +0,0 @@ -import scala.quoted._ - -import dotty.tools.dotc.quoted.Toolbox._ - -object Test { - - def main(args: Array[String]): Unit = { - // n2 is clearly not a constant - val n2 = '{ println("foo"); 2 } - println(power(n2, 6.0.toExpr).show) - println(power(n2, 6.0.toExpr).run) - } - - def power(n: Expr[Int], x: Expr[Double]): Expr[Double] = { - n match { - case Constant(n1) => powerCode(n1, x) - case _ => '{ dynamicPower(~n, ~x) } - } - } - - private def powerCode(n: Int, x: Expr[Double]): Expr[Double] = - if (n == 0) '(1.0) - else if (n == 1) x - else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } } - else '{ ~x * ~powerCode(n - 1, x) } - - def dynamicPower(n: Int, x: Double): Double = - if (n == 0) 1.0 - else if (n % 2 == 0) dynamicPower(n / 2, x * x) - else x * dynamicPower(n - 1, x) -} diff --git a/tests/run/tasty-eval.check b/tests/run/tasty-eval.check new file mode 100644 index 000000000000..4866bcf63c1b --- /dev/null +++ b/tests/run/tasty-eval.check @@ -0,0 +1,5 @@ +Some(1) +Some(8) +Some(5) +Some(6) +None diff --git a/tests/run/tasty-eval/quoted_1.scala b/tests/run/tasty-eval/quoted_1.scala new file mode 100644 index 000000000000..11617ff8761d --- /dev/null +++ b/tests/run/tasty-eval/quoted_1.scala @@ -0,0 +1,36 @@ +import scala.quoted._ + +import scala.tasty.Universe +import scala.tasty.util.{TastyPrinter, TreeTraverser} + +object Macros { + + implicit inline def foo(i: Int): String = + ~impl('(i))(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl(i: Expr[Int])(implicit u: Universe): Expr[String] = { + value(i).toString.toExpr + } + + inline implicit def value[X](e: Expr[X])(implicit u: Universe, ev: Valuable[X]): Option[X] = ev.value(e) + + trait Valuable[X] { + def value(e: Expr[X])(implicit u: Universe): Option[X] + } + + implicit def intIsEvalable: Valuable[Int] = new Valuable[Int] { + override def value(e: Expr[Int])(implicit u: Universe): Option[Int] = { + import u._ + import u.tasty._ + e.toTasty.tpe match { + case Type.SymRef(ValDef(_, tpt, _), pre) => + tpt.tpe match { + case Type.ConstantType(Constant.Int(i)) => Some(i) + case _ => None + } + case Type.ConstantType(Constant.Int(i)) => Some(i) + case _ => None + } + } + } +} diff --git a/tests/run/tasty-eval/quoted_2.scala b/tests/run/tasty-eval/quoted_2.scala new file mode 100644 index 000000000000..208bcca87708 --- /dev/null +++ b/tests/run/tasty-eval/quoted_2.scala @@ -0,0 +1,14 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + println(foo(1)) // "Some(1)" + println(foo(1 + 7)) // "Some(8)" + final val y = 5 + println(foo(y)) // "Some(5)" + println(foo(y + 1)) + val x = 4 + println(foo(x)) // "None" + } +} diff --git a/tests/run/tasty-extractors-1.check b/tests/run/tasty-extractors-1.check new file mode 100644 index 000000000000..1b8005b7c0a4 --- /dev/null +++ b/tests/run/tasty-extractors-1.check @@ -0,0 +1,120 @@ +Literal(Boolean(true)) +ConstantType(Boolean(true)) + +Literal(Int(1)) +ConstantType(Int(1)) + +Literal(Long(2)) +ConstantType(Long(2)) + +Literal(Float(2.1)) +ConstantType(Float(2.1)) + +Literal(Double(2.2)) +ConstantType(Double(2.2)) + +Literal(String(abc)) +ConstantType(String(abc)) + +Apply(Ident(println), List(Literal(String(abc)))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Typed(Literal(Int(8)), TypeIdent(Int)) +SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +Typed(Literal(Byte(8)), TypeIdent(Byte)) +SymRef(Byte, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +Typed(Literal(Short(8)), TypeIdent(Short)) +SymRef(Short, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +Literal(Char(a)) +ConstantType(Char(a)) + +Block(List(Literal(Int(1)), Literal(Int(2))), Literal(Int(3))) +ConstantType(Int(3)) + +If(Typed(Literal(Boolean(true)), TypeIdent(Boolean)), Literal(Int(1)), Literal(Int(2))) +SymRef(Int, ThisType(SymRef(scala, NoPrefix))) + +Match(Literal(String(a)), List(CaseDef(Value(Literal(String(a))), None, Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Match(Literal(String(b)), List(CaseDef(Bind(n, Value(Ident(_))), None, Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Match(Literal(String(c)), List(CaseDef(Bind(n, TypeTest(TypeIdent(String))), None, Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Match(Literal(String(e)), List(CaseDef(Value(Ident(_)), None, Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Match(Literal(String(f)), List(CaseDef(TypeTest(TypeIdent(String)), None, Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Match(Typed(Literal(String(g)), TypeIdent(Any)), List(CaseDef(Alternative(List(TypeTest(TypeIdent(String)), TypeTest(TypeIdent(Int)))), None, Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Match(Literal(String(h)), List(CaseDef(Value(Ident(_)), Some(Literal(Boolean(false))), Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ValDef(a, Synthetic(), Some(Literal(String(o))))), Match(Literal(String(i)), List(CaseDef(Bind(a, Value(Ident(_))), None, Block(Nil, Literal(Unit())))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Match(Ident(Nil), List(CaseDef(Unapply(TypeApply(Select(Ident(List), unapplySeq, Some(Signature(List(scala.collection.Seq), scala.Some))), List(Synthetic())), Nil, List(Bind(a, Value(Ident(_))), Bind(b, Value(Ident(_))), Bind(c, Value(Ident(_))))), None, Block(Nil, Literal(Unit()))))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Try(Literal(Int(1)), List(CaseDef(Value(Ident(_)), None, Block(Nil, Literal(Unit())))), None) +OrType(SymRef(Int, ThisType(SymRef(scala, NoPrefix))), SymRef(Unit, ThisType(SymRef(scala, NoPrefix)))) + +Try(Literal(Int(2)), Nil, Some(Literal(Unit()))) +ConstantType(Int(2)) + +Try(Literal(Int(3)), List(CaseDef(Value(Ident(_)), None, Block(Nil, Literal(Unit())))), Some(Literal(Unit()))) +OrType(SymRef(Int, ThisType(SymRef(scala, NoPrefix))), SymRef(Unit, ThisType(SymRef(scala, NoPrefix)))) + +Apply(Select(Literal(String(a)), ==, Some(Signature(List(java.lang.Object), scala.Boolean))), List(Literal(String(b)))) +SymRef(Boolean, ThisType(SymRef(scala, NoPrefix))) + +Apply(Select(New(TypeIdent(Object)), , Some(Signature(Nil, java.lang.Object))), Nil) +SymRef(Object, ThisType(SymRef(lang, NoPrefix))) + +Apply(Select(Ident(Int), box, Some(Signature(List(scala.Int), java.lang.Integer))), List(NamedArg(x, Literal(Int(9))))) +SymRef(Integer, ThisType(SymRef(lang, NoPrefix))) + +Apply(TypeApply(Select(Ident(Ordering), apply, Some(Signature(List(scala.math.Ordering), scala.math.Ordering))), List(TypeIdent(Int))), List(Ident(Int))) +AppliedType(SymRef(Ordering, ThisType(SymRef(math, NoPrefix))), List(SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))))) + +Block(List(ValDef(a, TypeIdent(Int), Some(Literal(Int(3))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ValDef(b, TypeIdent(Int), Some(Literal(Int(3))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f1, Nil, Nil, TypeIdent(Int), Some(Literal(Int(3))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f2, Nil, Nil, TypeIdent(Int), Some(Return(Literal(Int(4)))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f3, Nil, List(List(ValDef(i, TypeIdent(Int), None))), TypeIdent(Int), Some(Ident(i)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f4, Nil, List(List(ValDef(i, TypeIdent(Int), None)), List(ValDef(j, TypeIdent(Int), None))), TypeIdent(Int), Some(Apply(Select(Ident(i), +, Some(Signature(List(scala.Int), scala.Int))), List(Ident(j)))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f5, Nil, List(List(ValDef(i, TypeIdent(Int), None))), TypeIdent(Int), Some(Ident(i))), DefDef(f5$default$1, Nil, Nil, Synthetic(), Some(Literal(Int(9))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f6, List(TypeDef(T, TypeBoundsTree(Synthetic(), Synthetic()))), List(List(ValDef(x, TypeIdent(T), None))), TypeIdent(T), Some(Ident(x)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f7, List(TypeDef(T, TypeBoundsTree(Synthetic(), Synthetic()))), List(List(ValDef(x, TypeIdent(T), None))), Singleton(Ident(x)), Some(Ident(x)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef(f8, Nil, List(List(ValDef(i, Annotated(Applied(Synthetic(), List(TypeIdent(Int))), Apply(Select(New(Synthetic()), , Some(Signature(Nil, scala.annotation.internal.Repeated))), Nil)), None))), TypeIdent(Int), Some(Literal(Int(9))))), Apply(Ident(f8), List(Typed(Repeated(List(Literal(Int(1)), Literal(Int(2)), Literal(Int(3)))), Synthetic())))) +SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +Block(List(DefDef(f9, Nil, List(List(ValDef(i, ByName(TypeIdent(Int)), None))), TypeIdent(Int), Some(Ident(i)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + diff --git a/tests/run/tasty-extractors-1/quoted_1.scala b/tests/run/tasty-extractors-1/quoted_1.scala new file mode 100644 index 000000000000..c1055a1dcabe --- /dev/null +++ b/tests/run/tasty-extractors-1/quoted_1.scala @@ -0,0 +1,26 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.util.TastyPrinter + +object Macros { + + implicit inline def printTree[T](x: => T): Unit = + ~impl('(x))(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl[T](x: Expr[T])(implicit u: Universe): Expr[Unit] = { + import u._ + import tasty._ + val tree = x.toTasty + val printer = new TastyPrinter(tasty) + val treeStr = printer.stringOfTree(tree) + val treeTpeStr = printer.stringOfType(tree.tpe) + + '{ + println(~treeStr.toExpr) + println(~treeTpeStr.toExpr) + println() + } + } +} diff --git a/tests/run/tasty-extractors-1/quoted_2.scala b/tests/run/tasty-extractors-1/quoted_2.scala new file mode 100644 index 000000000000..b0c052366225 --- /dev/null +++ b/tests/run/tasty-extractors-1/quoted_2.scala @@ -0,0 +1,48 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + printTree(true) + printTree(1) + printTree(2L) + printTree(2.1f) + printTree(2.2d) + printTree("abc") + printTree(println("abc")) + printTree(8: Int) + printTree(8: Byte) + printTree(8: Short) + printTree('a') + printTree { 1; 2; 3 } + printTree(if (true: Boolean) 1 else 2) + printTree("a" match { case "a" => () }) + printTree("b" match { case n => () }) + printTree("c" match { case n: String => () }) + printTree("e" match { case _ => () }) + printTree("f" match { case _: String => () }) + printTree(("g": Any) match { case _: String | _: Int => () }) + printTree("h" match { case _ if false => () }) + printTree { val a = "o"; "i" match { case a => () } } + // printTree(Option(4) match { case Some(a) => a; case None => 1 }) + printTree(Nil match { case List(a, b, c) => }) + printTree(try 1 catch { case _ => }) + printTree(try 2 finally ()) + printTree(try 3 catch { case _ => } finally ()) + printTree("a" == "b") + printTree(new Object) + printTree(Int.box(x = 9)) + printTree(Ordering.apply[Int]) + printTree { val a: Int = 3 } + printTree { lazy val b: Int = 3 } + printTree { def f1: Int = 3 } + printTree { def f2: Int = return 4 } + printTree { def f3(i: Int): Int = i } + printTree { def f4(i: Int)(j: Int): Int = i + j } + printTree { def f5(i: Int = 9): Int = i } + printTree { def f6[T](x: T): T = x } + printTree { def f7[T](x: T): x.type = x } + printTree { def f8(i: Int*): Int = 9; f8(1, 2, 3) } + printTree { def f9(i: => Int): Int = i } + } +} diff --git a/tests/run/tasty-extractors-2.check b/tests/run/tasty-extractors-2.check new file mode 100644 index 000000000000..ef60d7330f8f --- /dev/null +++ b/tests/run/tasty-extractors-2.check @@ -0,0 +1,111 @@ +Block(List(ValDef(x, Synthetic(), Some(Literal(Int(1))))), Assign(Ident(x), Literal(Int(2)))) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(DefDef($anonfun, Nil, List(List(ValDef(x, TypeIdent(Int), None))), Synthetic(), Some(Ident(x)))), Lambda(Ident($anonfun), None)) +AppliedType(SymRef(Function1, ThisType(SymRef(scala, NoPrefix))), List(SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))), SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))))) + +Ident(???) +SymRef(???, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))) + +Typed(Literal(Int(1)), Singleton(Literal(Int(1)))) +ConstantType(Int(1)) + +Typed(Literal(Int(1)), TypeIdent(Int)) +SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +Typed(Ident(Nil), Applied(TypeIdent(List), List(TypeIdent(Int)))) +AppliedType(SymRef(List, ThisType(SymRef(immutable, NoPrefix))), List(SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))))) + +Typed(Apply(Select(New(TypeIdent(Baz)), , Some(Signature(Nil, Baz))), Nil), And(TypeIdent(Foo), TypeIdent(Bar))) +AndType(SymRef(Foo, ThisType(SymRef(, NoPrefix))), SymRef(Bar, ThisType(SymRef(, NoPrefix)))) + +Typed(Literal(Int(1)), Or(TypeIdent(Int), TypeIdent(String))) +OrType(SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))), SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix))))) + +Block(List(Import(Select(Ident(scala), collection, None), List(SimpleSelector(Id(mutable))))), Literal(Int(1))) +ConstantType(Int(1)) + +Block(List(Import(Select(Ident(scala), collection, None), List(SimpleSelector(Id(mutable)), SimpleSelector(Id(immutable))))), Literal(Int(2))) +ConstantType(Int(2)) + +Block(List(Import(Select(Ident(scala), collection, None), List(RenameSelector(Id(mutable), Id(mut))))), Literal(Int(3))) +ConstantType(Int(3)) + +Block(List(Import(Select(Ident(scala), collection, None), List(OmitSelector(Id(mutable))))), Literal(Int(4))) +ConstantType(Int(4)) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, Nil)), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ValDef(Foo, TypeIdent(Foo$), Some(Apply(Select(New(TypeIdent(Foo$)), , Some(Signature(Nil, Test$._$Foo$))), Nil))), ClassDef(Foo$, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), Some(ValDef(_, Singleton(Ident(Foo)), None)), Nil)), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(TypeDef(Foo, TypeBoundsTree(Synthetic(), Synthetic()))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(TypeDef(Foo, TypeIdent(Int))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(TypeDef(Foo, TypeBoundsTree(TypeIdent(Null), TypeIdent(Object)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), Some(Literal(Int(0)))), DefDef(a_=, Nil, List(List(ValDef(x$1, Synthetic(), None))), Synthetic(), Some(Literal(Unit())))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(DefDef(a, Nil, Nil, Synthetic(), Some(Literal(Int(0))))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(DefDef(a, Nil, Nil, Synthetic(), Some(Literal(Int(0))))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(DefDef(a, Nil, Nil, Synthetic(), Some(Literal(Int(0))))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo1, DefDef(, Nil, List(List(ValDef(a, TypeIdent(Int), None))), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), None)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo2, DefDef(, Nil, List(List(ValDef(b, TypeIdent(Int), None))), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(b, Synthetic(), None)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo3, DefDef(, Nil, List(List(ValDef(a, TypeIdent(Int), None))), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), None))), ValDef(Foo3, TypeIdent(Foo3$), Some(Apply(Select(New(TypeIdent(Foo3$)), , Some(Signature(Nil, Test$._$Foo3$))), Nil))), ClassDef(Foo3$, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), Some(ValDef(_, Singleton(Ident(Foo3)), None)), List(DefDef($lessinit$greater$default$1, Nil, Nil, Synthetic(), Some(Literal(Int(5))))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo4, DefDef(, Nil, List(List(ValDef(a, TypeIdent(Int), None)), List(ValDef(b, TypeIdent(Int), None))), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), None), ValDef(b, Synthetic(), None)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo5, DefDef(, Nil, List(List(ValDef(a, TypeIdent(Int), None)), List(ValDef(b, TypeIdent(Int), None))), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), None), ValDef(b, Synthetic(), None))), ValDef(Foo5, TypeIdent(Foo5$), Some(Apply(Select(New(TypeIdent(Foo5$)), , Some(Signature(Nil, Test$._$Foo5$))), Nil))), ClassDef(Foo5$, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), Some(ValDef(_, Singleton(Ident(Foo5)), None)), List(DefDef($lessinit$greater$default$2, Nil, List(List(ValDef(a, TypeIdent(Int), None))), Synthetic(), Some(Ident(a)))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo6, DefDef(, Nil, List(List(ValDef(a, TypeIdent(Int), None)), List(ValDef(b, Singleton(Ident(a)), None))), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), None), ValDef(b, Synthetic(), None)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo8, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(Apply(Ident(println), List(Literal(Int(0))))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo10, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), Some(Literal(Int(9))))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo11, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), Some(Literal(Int(10)))), DefDef(a_=, Nil, List(List(ValDef(x$1, Synthetic(), None))), Synthetic(), Some(Literal(Unit())))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo12, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(a, Synthetic(), Some(Literal(Int(11))))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, Nil), ClassDef(Bar, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, Test$._$Foo))), Nil)), None, Nil)), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo2, DefDef(, Nil, List(Nil), Synthetic(), None), List(Synthetic()), None, Nil), ClassDef(Bar, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil), TypeIdent(Foo2)), None, Nil)), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(List(ValDef(i, TypeIdent(Int), None))), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(ValDef(i, Synthetic(), None))), ClassDef(Bar, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(TypeIdent(Foo)), , Some(Signature(List(scala.Int), Test$._$Foo))), List(Literal(Int(1))))), None, Nil)), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef(X, TypeIdent(Int)))), DefDef(f, Nil, List(List(ValDef(a, TypeIdent(Foo), None))), TypeSelect(Ident(a), X), Some(Ident(???)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ClassDef(Foo, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef(X, TypeBoundsTree(Synthetic(), Synthetic())))), DefDef(f, Nil, List(List(ValDef(a, Refined(TypeIdent(Foo), List(TypeDef(X, TypeIdent(Int)))), None))), TypeSelect(Ident(a), X), Some(Ident(???)))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +Block(List(ValDef(lambda, Applied(Synthetic(), List(TypeIdent(Int), TypeIdent(Int))), Some(Block(List(DefDef($anonfun, Nil, List(List(ValDef(x, Synthetic(), None))), Synthetic(), Some(Ident(x)))), Lambda(Ident($anonfun), None))))), Literal(Unit())) +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + diff --git a/tests/run/tasty-extractors-2/quoted_1.scala b/tests/run/tasty-extractors-2/quoted_1.scala new file mode 100644 index 000000000000..c1055a1dcabe --- /dev/null +++ b/tests/run/tasty-extractors-2/quoted_1.scala @@ -0,0 +1,26 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.util.TastyPrinter + +object Macros { + + implicit inline def printTree[T](x: => T): Unit = + ~impl('(x))(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl[T](x: Expr[T])(implicit u: Universe): Expr[Unit] = { + import u._ + import tasty._ + val tree = x.toTasty + val printer = new TastyPrinter(tasty) + val treeStr = printer.stringOfTree(tree) + val treeTpeStr = printer.stringOfType(tree.tpe) + + '{ + println(~treeStr.toExpr) + println(~treeTpeStr.toExpr) + println() + } + } +} diff --git a/tests/run/tasty-extractors-2/quoted_2.scala b/tests/run/tasty-extractors-2/quoted_2.scala new file mode 100644 index 000000000000..d55a9daa7000 --- /dev/null +++ b/tests/run/tasty-extractors-2/quoted_2.scala @@ -0,0 +1,50 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + printTree { var x = 1; x = 2 } + printTree((x: Int) => x) + printTree(???) + printTree(1: 1) + printTree(1: Int) + printTree(Nil: List[Int]) + printTree(new Baz: Foo & Bar) + printTree(1: Int | String) + printTree { import scala.collection.mutable; 1 } + printTree { import scala.collection.{mutable, immutable}; 2 } + printTree { import scala.collection.{mutable => mut}; 3 } + printTree { import scala.collection.{mutable => _}; 4 } + printTree { class Foo } + printTree { object Foo } + printTree { type Foo } + printTree { type Foo = Int } + printTree { type Foo >: Null <: Object } + printTree { class Foo { @volatile var a = 0 } } + printTree { class Foo { final def a = 0 } } + printTree { class Foo { private[Foo] def a = 0 } } + printTree { class Foo { protected[Foo] def a = 0 } } + // printTree { case class Foo() } // FIXME: issue #4396 + printTree { class Foo1(a: Int) } + printTree { class Foo2(val b: Int) } + printTree { class Foo3(a: Int = 5) } + printTree { class Foo4(a: Int)(b: Int) } + printTree { class Foo5(a: Int)(b: Int = a) } + printTree { class Foo6(a: Int)(b: a.type) } + // printTree { class Foo7(a: Int) { def this() = this(6) } } + printTree { class Foo8 { println(0) } } + printTree { class Foo10 { val a = 9 } } + printTree { class Foo11 { var a = 10 } } + printTree { class Foo12 { lazy val a = 11 } } + printTree { class Foo; class Bar extends Foo } + printTree { trait Foo2; class Bar extends Foo2 } + printTree { class Foo(i: Int); class Bar extends Foo(1) } + printTree { class Foo { type X = Int }; def f(a: Foo): a.X = ??? } + printTree { class Foo { type X }; def f(a: Foo { type X = Int }): a.X = ??? } + printTree { val lambda: Int => Int = x => x } + } +} + +trait Foo +trait Bar +class Baz extends Foo with Bar diff --git a/tests/run/tasty-extractors-3.check b/tests/run/tasty-extractors-3.check new file mode 100644 index 000000000000..5b8f72f7c560 --- /dev/null +++ b/tests/run/tasty-extractors-3.check @@ -0,0 +1,42 @@ +SymRef(Int, ThisType(SymRef(scala, NoPrefix))) + +SymRef(x, NoPrefix) + +TypeBounds(SymRef(#, ThisType(SymRef(scala, NoPrefix))), SymRef(#, ThisType(SymRef(scala, NoPrefix)))) + +SymRef(#, ThisType(SymRef(scala, NoPrefix))) + +SymRef(#, ThisType(SymRef(scala, NoPrefix))) + +SymRef(T, NoPrefix) + +TypeBounds(SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))), SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix))))) + +SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +SymRef(T, NoPrefix) + +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +SymRef(Object, ThisType(SymRef(lang, NoPrefix))) + +TypeBounds(SymRef(#, ThisType(SymRef(scala, NoPrefix))), SymRef(#, ThisType(SymRef(scala, NoPrefix)))) + +SymRef(#, ThisType(SymRef(scala, NoPrefix))) + +SymRef(#, ThisType(SymRef(scala, NoPrefix))) + +Refinement(SymRef(Foo, NoPrefix), X, TypeBounds(SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))), SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))))) + +SymRef(Unit, ThisType(SymRef(scala, NoPrefix))) + +SymRef(Foo, NoPrefix) + +SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))) + +SymRef($anon, NoPrefix) + +Refinement(SymRef(Foo, NoPrefix), X, TypeBounds(SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))), SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))))) + diff --git a/tests/run/tasty-extractors-3/quoted_1.scala b/tests/run/tasty-extractors-3/quoted_1.scala new file mode 100644 index 000000000000..7a3b00f08b71 --- /dev/null +++ b/tests/run/tasty-extractors-3/quoted_1.scala @@ -0,0 +1,31 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.util.{TastyPrinter, TreeTraverser} + +object Macros { + + implicit inline def printTypes[T](x: => T): Unit = + ~impl('(x))(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl[T](x: Expr[T])(implicit u: Universe): Expr[Unit] = { + import u._ + import u.tasty._ + + val printer = new TastyPrinter(tasty) + + val buff = new StringBuilder + val traverser = new TreeTraverser(u.tasty) { + override def traverseTypeTree(tree: TypeOrBoundsTree)(implicit ctx: Context): Unit = { + buff.append(printer.stringOfType(tree.tpe)) + buff.append("\n\n") + traverseTypeTreeChildren(tree) + } + } + + val tree = x.toTasty + traverser.traverseTree(tree) + '(print(~buff.result().toExpr)) + } +} diff --git a/tests/run/tasty-extractors-3/quoted_2.scala b/tests/run/tasty-extractors-3/quoted_2.scala new file mode 100644 index 000000000000..1f4d1de11855 --- /dev/null +++ b/tests/run/tasty-extractors-3/quoted_2.scala @@ -0,0 +1,19 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + printTypes { + val x = 1 + val y: x.type = x + def f1[T](): T = ??? + def f2[T >: Int <: Int](): T = ??? + class Foo { type X } + val foo = new Foo { type X = String } + } + } +} + +trait Foo +trait Bar +class Baz extends Foo with Bar diff --git a/tests/run-with-compiler/quote-run-constants-extract-1.check b/tests/run/tasty-extractors-constants-1.check similarity index 100% rename from tests/run-with-compiler/quote-run-constants-extract-1.check rename to tests/run/tasty-extractors-constants-1.check diff --git a/tests/run/tasty-extractors-constants-1/quoted_1.scala b/tests/run/tasty-extractors-constants-1/quoted_1.scala new file mode 100644 index 000000000000..96c38edc9204 --- /dev/null +++ b/tests/run/tasty-extractors-constants-1/quoted_1.scala @@ -0,0 +1,30 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.util._ + +object Macros { + + implicit inline def testMacro: Unit = + ~impl(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl(implicit u: Universe): Expr[Unit] = { + import u._ + import u.tasty._ + + val buff = new StringBuilder + def stagedPrintln(x: Any): Unit = buff append java.util.Objects.toString(x) append "\n" + + val Constant = new ConstantExtractor(tasty) + + 3.toExpr match { case Constant(n) => stagedPrintln(n) } + '(4) match { case Constant(n) => stagedPrintln(n) } + '("abc") match { case Constant(n) => stagedPrintln(n) } + '(null) match { case Constant(n) => stagedPrintln(n) } + + '(new Object) match { case Constant(n) => println(n); case _ => stagedPrintln("OK") } + + '(print(~buff.result().toExpr)) + } +} diff --git a/tests/run/tasty-extractors-constants-1/quoted_2.scala b/tests/run/tasty-extractors-constants-1/quoted_2.scala new file mode 100644 index 000000000000..197b61bb5c98 --- /dev/null +++ b/tests/run/tasty-extractors-constants-1/quoted_2.scala @@ -0,0 +1,4 @@ + +object Test { + def main(args: Array[String]): Unit = Macros.testMacro +} diff --git a/tests/run/tasty-extractors-constants-2.check b/tests/run/tasty-extractors-constants-2.check new file mode 100644 index 000000000000..00657feabf5c --- /dev/null +++ b/tests/run/tasty-extractors-constants-2.check @@ -0,0 +1,17 @@ +{ + val y: Double = 3.0.*(3.0) + y +} +9.0 + +{ + val y: Double = 4.0.*(4.0) + y +} +16.0 + +{ + val y: Double = 5.0.*(5.0) + y +} +25.0 diff --git a/tests/run/tasty-extractors-constants-2/quoted_1.scala b/tests/run/tasty-extractors-constants-2/quoted_1.scala new file mode 100644 index 000000000000..5ffc8278f494 --- /dev/null +++ b/tests/run/tasty-extractors-constants-2/quoted_1.scala @@ -0,0 +1,67 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.util._ + +object Macros { + + inline def testMacro: Unit = + ~impl(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl(implicit u: Universe): Expr[Unit] = { + // 2 is a lifted constant + val show1 = power(2.toExpr, 3.0.toExpr)(u).show + val run1 = power(2.toExpr, 3.0.toExpr)(u).run + + // n is a lifted constant + val n = 2 + val show2 = power(n.toExpr, 4.0.toExpr)(u).show + val run2 = power(n.toExpr, 4.0.toExpr)(u).run + + // n is a constant in a quote + val show3 = power('(2), 5.0.toExpr)(u).show + val run3 = power('(2), 5.0.toExpr)(u).run + + // n2 is clearly not a constant + // FIXME +// val n2 = '{ println("foo"); 2 } +// val show4 = (power(n2, 6.0.toExpr).show) +// val run4 = (power(n2, 6.0.toExpr).run) + + '{ + println(~show1.toExpr) + println(~run1.toExpr) + println() + println(~show2.toExpr) + println(~run2.toExpr) + println() + println(~show3.toExpr) + println(~run3.toExpr) +// println() +// println(~show4.toExpr) +// println(~run4.toExpr) + } + } + + def power(n: Expr[Int], x: Expr[Double])(implicit u: Universe): Expr[Double] = { + import u._ + import u.tasty._ + val Constant = new ConstantExtractor(u.tasty) + n match { + case Constant(n1) => powerCode(n1, x) + case _ => '{ dynamicPower(~n, ~x) } + } + } + + def powerCode(n: Int, x: Expr[Double]): Expr[Double] = + if (n == 0) '(1.0) + else if (n == 1) x + else if (n % 2 == 0) '{ { val y = ~x * ~x; ~powerCode(n / 2, '(y)) } } + else '{ ~x * ~powerCode(n - 1, x) } + + def dynamicPower(n: Int, x: Double): Double = + if (n == 0) 1.0 + else if (n % 2 == 0) dynamicPower(n / 2, x * x) + else x * dynamicPower(n - 1, x) +} diff --git a/tests/run/tasty-extractors-constants-2/quoted_2.scala b/tests/run/tasty-extractors-constants-2/quoted_2.scala new file mode 100644 index 000000000000..197b61bb5c98 --- /dev/null +++ b/tests/run/tasty-extractors-constants-2/quoted_2.scala @@ -0,0 +1,4 @@ + +object Test { + def main(args: Array[String]): Unit = Macros.testMacro +} diff --git a/tests/run/tasty-extractors-owners.check b/tests/run/tasty-extractors-owners.check new file mode 100644 index 000000000000..7712d05c567d --- /dev/null +++ b/tests/run/tasty-extractors-owners.check @@ -0,0 +1,27 @@ +foo +DefDef(main, Nil, List(List(ValDef(args, Synthetic(), None))), Synthetic(), None) + +bar +DefDef(foo, Nil, Nil, Synthetic(), None) + +bar2 +DefDef(foo, Nil, Nil, Synthetic(), None) + +foo2 +DefDef(main, Nil, List(List(ValDef(args, Synthetic(), None))), Synthetic(), None) + +baz +ValDef(foo2, Synthetic(), None) + +baz2 +ValDef(foo2, Synthetic(), None) + + +ClassDef(A, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef(B, Synthetic()), DefDef(b, Nil, Nil, Synthetic(), None), ValDef(b2, Synthetic(), None))) + +b +ClassDef(A, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef(B, Synthetic()), DefDef(b, Nil, Nil, Synthetic(), None), ValDef(b2, Synthetic(), None))) + +b2 +ClassDef(A, DefDef(, Nil, List(Nil), Synthetic(), None), List(Apply(Select(New(Synthetic()), , Some(Signature(Nil, java.lang.Object))), Nil)), None, List(TypeDef(B, Synthetic()), DefDef(b, Nil, Nil, Synthetic(), None), ValDef(b2, Synthetic(), None))) + diff --git a/tests/run/tasty-extractors-owners/quoted_1.scala b/tests/run/tasty-extractors-owners/quoted_1.scala new file mode 100644 index 000000000000..10d2260401c8 --- /dev/null +++ b/tests/run/tasty-extractors-owners/quoted_1.scala @@ -0,0 +1,45 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.Tasty +import scala.tasty.util.{TastyPrinter, TreeTraverser} + +object Macros { + + implicit inline def printOwners[T](x: => T): Unit = + ~impl('(x))(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl[T](x: Expr[T])(implicit u: Universe): Expr[Unit] = { + import u._ + import u.tasty._ + val printer = new TastyPrinter(tasty) + + val buff = new StringBuilder + + val output = new TreeTraverser(u.tasty) { + import tasty._ + override def traverseTree(tree: Tree)(implicit ctx: Context): Unit = { + tree match { + case tree @ DefDef(name, _, _, _, _) => + buff.append(name) + buff.append("\n") + buff.append(printer.stringOfTree(tree.owner)) + buff.append("\n\n") + case tree @ ValDef(name, _, _) => + buff.append(name) + buff.append("\n") + buff.append(printer.stringOfTree(tree.owner)) + buff.append("\n\n") + case _ => + } + traverseTreeChildren(tree) + } + } + + val tree = x.toTasty + output.traverseTree(tree) + '(print(~buff.result().toExpr)) + } + +} diff --git a/tests/run/tasty-extractors-owners/quoted_2.scala b/tests/run/tasty-extractors-owners/quoted_2.scala new file mode 100644 index 000000000000..cc13d81ae03b --- /dev/null +++ b/tests/run/tasty-extractors-owners/quoted_2.scala @@ -0,0 +1,24 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + printOwners { + def foo = { + def bar = 1 + val bar2 = 2 + bar + } + val foo2 = { + def baz = 3 + val baz2 = 4 + baz + } + class A { + type B = Int + def b = 5 + val b2 = 6 + } + } + } +} diff --git a/tests/run/tasty-extractors-types.check b/tests/run/tasty-extractors-types.check new file mode 100644 index 000000000000..0cb6690578ab --- /dev/null +++ b/tests/run/tasty-extractors-types.check @@ -0,0 +1,12 @@ +TypeIdent(Int) +SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))) + +Applied(TypeIdent(List), List(TypeIdent(String))) +AppliedType(SymRef(List, ThisType(SymRef(immutable, NoPrefix))), List(SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))))) + +Applied(TypeIdent(Map), List(TypeIdent(String), TypeIdent(Int))) +AppliedType(SymRef(Map, ThisType(SymRef(immutable, NoPrefix))), List(SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))), SymRef(Int, SymRef(scala, ThisType(SymRef(, NoPrefix)))))) + +Applied(TypeIdent(Map), List(TypeIdent(String), TypeIdent(I))) +AppliedType(SymRef(Map, ThisType(SymRef(immutable, NoPrefix))), List(SymRef(String, SymRef(Predef, ThisType(SymRef(scala, NoPrefix)))), SymRef(I, NoPrefix))) + diff --git a/tests/run/tasty-extractors-types/quoted_1.scala b/tests/run/tasty-extractors-types/quoted_1.scala new file mode 100644 index 000000000000..198e6bc7f604 --- /dev/null +++ b/tests/run/tasty-extractors-types/quoted_1.scala @@ -0,0 +1,23 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.util.{TastyPrinter, TreeTraverser} + +object Macros { + + implicit inline def printType[T]: Unit = + ~impl('[T])(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl[T](x: Type[T])(implicit u: Universe): Expr[Unit] = { + import u._ + import u.tasty._ + val tree = x.toTasty + val printer = new TastyPrinter(tasty) + '{ + println(~printer.stringOfTypeTree(tree).toExpr) + println(~printer.stringOfType(tree.tpe).toExpr) + println() + } + } +} diff --git a/tests/run/tasty-extractors-types/quoted_2.scala b/tests/run/tasty-extractors-types/quoted_2.scala new file mode 100644 index 000000000000..6e19f6cb53a8 --- /dev/null +++ b/tests/run/tasty-extractors-types/quoted_2.scala @@ -0,0 +1,12 @@ + +import Macros._ + +object Test { + def main(args: Array[String]): Unit = { + type I = Int + printType[Int] + printType[List[String]] + printType[Map[String, Int]] + printType[Map[String, I]] + } +} diff --git a/tests/run/tasty-indexed-map/quoted_1.scala b/tests/run/tasty-indexed-map/quoted_1.scala new file mode 100644 index 000000000000..f41cd00bcccf --- /dev/null +++ b/tests/run/tasty-indexed-map/quoted_1.scala @@ -0,0 +1,50 @@ + +import scala.quoted._ + +import scala.tasty.Universe +import scala.tasty.util.TastyPrinter + +class MyMap[Keys](private val underlying: Array[Int]) extends AnyVal { + def get[K <: String](implicit i: Index[K, Keys]): Int = underlying(i.index) + def set[K <: String](value: Int)(implicit i: Index[K, Keys]): Unit = underlying(i.index) = value +} + +object MyMap { + def create[Keys](implicit s: Size[Keys]): MyMap[Keys] = new MyMap[Keys](new Array[Int](s.size)) +} + +trait Size[Keys] { def size: Int } +object Size { + def apply[Keys](v: Int): Size[Keys] = new Size { def size = v } + implicit val unit: Size[Unit] = Size(0) + implicit def cons[H, T](implicit s: Size[T]): Size[(H, T)] = Size(s.size + 1) +} + +class Index[K, Keys](val index: Int) extends AnyVal +object Index { + + implicit def zero[K, T]: Index[K, (K, T)] = new Index(0) + + implicit inline def succ[K, H, T](implicit prev: => Index[K, T]): Index[K, (H, T)] = ~succImpl(Universe.compilationUniverse)('[K], '[H], '[T]) + + def succImpl[K, H, T](u: Universe)(implicit k: Type[K], h: Type[H], t: Type[T]): Expr[Index[K, (H, T)]] = { + import u._ + import u.tasty._ + + def name(tp: TypeOrBounds): String = tp match { + case Type.ConstantType(Constant.String(str)) => str + } + + def names(tp: TypeOrBounds): List[String] = tp match { + case Type.AppliedType(_, x1 :: x2 :: Nil) => name(x1) :: names(x2) + case _ => Nil + } + + val key = name(k.toTasty.tpe) + val keys = name(h.toTasty.tpe) :: names(t.toTasty.tpe) + + val index = keys.indexOf(key) + + '(new Index(~index.toExpr)) + } +} diff --git a/tests/run/tasty-indexed-map/quoted_2.scala b/tests/run/tasty-indexed-map/quoted_2.scala new file mode 100644 index 000000000000..3392cef7a281 --- /dev/null +++ b/tests/run/tasty-indexed-map/quoted_2.scala @@ -0,0 +1,18 @@ +object Test { + + def main(args: Array[String]): Unit = { + val map = MyMap.create[("foo", ("bar", ("baz", Unit)))] + + map.set["foo"](9) + assert(map.get["foo"] == 9) + + map.set["bar"](42) + assert(map.get["bar"] == 42) + + map.set["baz"](42) + assert(map.get["baz"] == 42) + +// map.set["banana"](42) // error +// assert(map.get["banana"] == 42) // error + } +} \ No newline at end of file diff --git a/tests/run/tasty-linenumber.check b/tests/run/tasty-linenumber.check new file mode 100644 index 000000000000..7f936437beb7 --- /dev/null +++ b/tests/run/tasty-linenumber.check @@ -0,0 +1,4 @@ +foo 5 +foo 6 +foo 8 +foo 9 \ No newline at end of file diff --git a/tests/run/tasty-linenumber/quoted_1.scala b/tests/run/tasty-linenumber/quoted_1.scala new file mode 100644 index 000000000000..5d4e6ab51837 --- /dev/null +++ b/tests/run/tasty-linenumber/quoted_1.scala @@ -0,0 +1,22 @@ +import scala.quoted._ + +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe + +class LineNumber(val value: Int) { + override def toString: String = value.toString +} + +object LineNumber { + + implicit inline def line[T >: Unit <: Unit]: LineNumber = + ~lineImpl('[T])(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def lineImpl(x: Type[Unit])(implicit u: Universe): Expr[LineNumber] = { + import u._ + import u.tasty._ + '(new LineNumber(~x.toTasty.pos.startLine.toExpr)) + } + +} diff --git a/tests/run/tasty-linenumber/quoted_2.scala b/tests/run/tasty-linenumber/quoted_2.scala new file mode 100644 index 000000000000..253f4f85ead9 --- /dev/null +++ b/tests/run/tasty-linenumber/quoted_2.scala @@ -0,0 +1,16 @@ + +import LineNumber._ + +object Test { + def main(args: Array[String]): Unit = { + foo(line) + foo + + foo + foo + } + + def foo(implicit line: LineNumber): Unit = { + println("foo " + line) + } +} diff --git a/tests/run/tasty-location.check b/tests/run/tasty-location.check new file mode 100644 index 000000000000..adaee27a91a3 --- /dev/null +++ b/tests/run/tasty-location.check @@ -0,0 +1,6 @@ +foo Location(List(Test$, loc1)) +foo Location(List(Test$, main)) +foo Location(List(Test$, main)) +foo Location(List(Test$, main, bar)) +foo Location(List(Test$, main, bar, baz)) +foo Location(List(Test$, main, f, $anonfun)) diff --git a/tests/run/tasty-location/quoted_1.scala b/tests/run/tasty-location/quoted_1.scala new file mode 100644 index 000000000000..2ed101d91bd3 --- /dev/null +++ b/tests/run/tasty-location/quoted_1.scala @@ -0,0 +1,33 @@ +import scala.quoted._ + +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe + +case class Location(owners: List[String]) + +object Location { + + implicit inline def location: Location = + ~impl(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl(implicit u: Universe): Expr[Location] = { + import u._ + import u.tasty._ + + def listOwnerNames(definition: Definition, acc: List[String]): List[String] = definition match { + case ValDef(name, _, _) => listOwnerNames(definition.owner, name :: acc) + case DefDef(name, _, _, _, _) => listOwnerNames(definition.owner, name :: acc) + case ClassDef(name, _, _, _, _) => listOwnerNames(definition.owner, name :: acc) + case _ => acc + } + + val list = listOwnerNames(u.context.owner, Nil) + '(new Location(~list.toExpr)) + } + + private implicit def ListIsLiftable[T : Liftable : Type]: Liftable[List[T]] = { + case x :: xs => '{ ~x.toExpr :: ~xs.toExpr } + case Nil => '{ List.empty[T] } + } +} diff --git a/tests/run/tasty-location/quoted_2.scala b/tests/run/tasty-location/quoted_2.scala new file mode 100644 index 000000000000..42589dc32db5 --- /dev/null +++ b/tests/run/tasty-location/quoted_2.scala @@ -0,0 +1,24 @@ + +import Location._ + +object Test { + val loc1 = location + def main(args: Array[String]): Unit = { + foo(loc1) + foo(location) + foo + + def bar = { + foo + val baz = foo + } + bar + + val f = (i: Int) => foo + f(0) + } + + def foo(implicit location: Location): Unit = { + println("foo " + location) + } +} diff --git a/tests/run/tasty-macro-assert.check b/tests/run/tasty-macro-assert.check new file mode 100644 index 000000000000..d8efe99f6d02 --- /dev/null +++ b/tests/run/tasty-macro-assert.check @@ -0,0 +1,7 @@ +Condition was false +Error left did not equal right: + left = Literal(String(acb)) + right = Literal(String(cde)) +Error left was equal to right: + left = Literal(String(acb)) + right = Literal(String(acb)) diff --git a/tests/run/tasty-macro-assert/quoted_1.scala b/tests/run/tasty-macro-assert/quoted_1.scala new file mode 100644 index 000000000000..c74f7bc675f7 --- /dev/null +++ b/tests/run/tasty-macro-assert/quoted_1.scala @@ -0,0 +1,79 @@ +import scala.quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe +import scala.tasty.util.TastyPrinter + +object Asserts { + + implicit class Ops[T](left: T) { + def ===(right: T): Boolean = left == right + def !==(right: T): Boolean = left != right + } + + object Ops + + inline def macroAssert(cond: Boolean): Unit = + ~impl('(cond))(Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl(cond: Expr[Boolean])(implicit u: Universe): Expr[Unit] = { + import u._ + import u.tasty._ + + val tree = cond.toTasty + + def isOps(tpe: TypeOrBounds): Boolean = tpe match { + case Type.SymRef(DefDef("Ops", _, _, _, _), _) => true // TODO check that the parent is Asserts + case _ => false + } + + object OpsTree { + def unapply(arg: Term): Option[Term] = arg match { + case Term.Apply(Term.TypeApply(term, _), left :: Nil) if isOps(term.tpe) => + Some(left) + case _ => None + } + } + + tree match { + case Term.Apply(Term.Select(OpsTree(left), op, _), right :: Nil) => + // FIXME splice the threes directly + val printer = new TastyPrinter(tasty) + val lExpr = printer.stringOfTree(left).toExpr + val rExpr = printer.stringOfTree(right).toExpr + op match { + case "===" => '(assertEquals(~lExpr, ~rExpr)) + case "!==" => '(assertNotEquals(~lExpr, ~rExpr)) + } + case _ => + '(assertTrue(~cond)) + } + + } + + def assertEquals[T](left: T, right: T): Unit = { + if (left != right) { + println( + s"""Error left did not equal right: + | left = $left + | right = $right""".stripMargin) + } + + } + + def assertNotEquals[T](left: T, right: T): Unit = { + if (left == right) { + println( + s"""Error left was equal to right: + | left = $left + | right = $right""".stripMargin) + } + + } + + def assertTrue(cond: Boolean): Unit = { + if (!cond) + println("Condition was false") + } + +} diff --git a/tests/run/tasty-macro-assert/quoted_2.scala b/tests/run/tasty-macro-assert/quoted_2.scala new file mode 100644 index 000000000000..06fb973ca4bd --- /dev/null +++ b/tests/run/tasty-macro-assert/quoted_2.scala @@ -0,0 +1,11 @@ + +import Asserts._ + +object Test { + def main(args: Array[String]): Unit = { + macroAssert("acb" == "cde") + macroAssert("acb" === "cde") + macroAssert("acb" !== "acb") + } + +} diff --git a/tests/run/tasty-positioned.check b/tests/run/tasty-positioned.check new file mode 100644 index 000000000000..0c43c2d3a4f1 --- /dev/null +++ b/tests/run/tasty-positioned.check @@ -0,0 +1,8 @@ +0 columns:24-25 lines:9-9 +10 columns:13-15 lines:10-10 +4530 columns:13-17 lines:11-11 +acbvasdfa columns:24-35 lines:12-12 +acbvasdfa columns:13-24 lines:13-13 +a +b columns:6-25 lines:15-16 +Foo columns:16-19 lines:17-17 diff --git a/tests/run/tasty-positioned/quoted_1.scala b/tests/run/tasty-positioned/quoted_1.scala new file mode 100644 index 000000000000..f0945503f6d8 --- /dev/null +++ b/tests/run/tasty-positioned/quoted_1.scala @@ -0,0 +1,33 @@ +import scala.quoted._ + +import dotty.tools.dotc.quoted.Toolbox._ + +import scala.tasty.Universe + +case class Position(path: String, start: Int, end: Int, + startLine: Int, startColumn: Int, endLine: Int, endColumn: Int) + +case class Positioned[T](value: T, position: Position) + +object Positioned { + + implicit inline def apply[T](x: T): Positioned[T] = + ~impl('(x))('[T], Universe.compilationUniverse) // FIXME infer Universe.compilationUniverse within top level ~ + + def impl[T](x: Expr[T])(implicit ev: Type[T], u: Universe): Expr[Positioned[T]] = { + import u._ + import u.tasty.{Position => _, _} + + val pos = x.toTasty.pos + + val path = pos.sourceFile.toString.toExpr + val start = pos.start.toExpr + val end = pos.end.toExpr + val startLine = pos.startLine.toExpr + val endLine = pos.endLine.toExpr + val startColumn = pos.startColumn.toExpr + val endColumn = pos.endColumn.toExpr + + '(Positioned[T](~x, new Position(~path, ~start, ~end, ~startLine, ~startColumn, ~endLine, ~endColumn))) + } +} diff --git a/tests/run/tasty-positioned/quoted_2.scala b/tests/run/tasty-positioned/quoted_2.scala new file mode 100644 index 000000000000..4026b53b3ff2 --- /dev/null +++ b/tests/run/tasty-positioned/quoted_2.scala @@ -0,0 +1,23 @@ + +import Positioned._ + +object Test { + def main(args: Array[String]): Unit = { + def printPos[T](x: Positioned[T]) = { + val pos = x.position + println(s"${x.value} columns:${pos.startColumn}-${pos.endColumn} lines:${pos.startLine}-${pos.endLine}") + } + printPos(Positioned(0)) + printPos(10) + printPos(4530) + printPos(Positioned("acbvasdfa")) + printPos("acbvasdfa") + printPos( + """a + |b""".stripMargin) + printPos(new Foo) + } + class Foo { + override def toString: String = "Foo" + } +}