verify.sourcecode.Macros.scala Maven / Gradle / Ivy
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package verify
package sourcecode
import language.experimental.macros
trait NameMacros {
implicit def scalaVerifySourcecodeName: Name = macro Macros.nameImpl
}
trait NameMachineMacros {
implicit def scalaVerifySourcecodeNameMachine: Name.Machine = macro Macros.nameMachineImpl
}
trait FullNameMacros {
implicit def scalaVerifySourcecodeFullName: FullName = macro Macros.fullNameImpl
}
trait FullNameMachineMacros {
implicit def scalaVerifySourcecodeFullNameMachine: FullName.Machine = macro Macros.fullNameMachineImpl
}
trait SourceFileNameMacros {
implicit def scalaVerifySourcecodeSourceFileName: SourceFileName = macro Macros.sourceFileNameImpl
}
trait SourceFilePathMacros {
implicit def scalaVerifySourcecodeSourceFilePath: SourceFilePath = macro Macros.sourceFilePathImpl
}
trait LineMacros {
implicit def scalaVerifySourcecodeLine: sourcecode.Line = macro Macros.lineImpl
}
trait EnclosingMacros {
implicit def scalaVerifySourcecodeEnclosing: Enclosing = macro Macros.enclosingImpl
}
trait EnclosingMachineMacros {
implicit def scalaVerifySourcecodeEnclosingMachine: Enclosing.Machine = macro Macros.enclosingMachineImpl
}
trait PkgMacros {
implicit def scalaVerifySourcecodePkg: Pkg = macro Macros.pkgImpl
}
trait TextMacros {
import scala.language.implicitConversions
implicit def toScalaVerifySourcecodeText[T](v: T): Text[T] = macro Macros.text[T]
def apply[T](v: T): Text[T] = macro Macros.text[T]
}
/*
trait ArgsMacros {
implicit def scalaVerifySourcecodeArgs: Args = macro Macros.argsImpl
}
*/
object Util {
def isSynthetic(c: Compat.Context)(s: c.Symbol) = isSyntheticName(getName(c)(s))
def isSyntheticName(name: String) = {
name == "" || (name.startsWith(""))
}
def getName(c: Compat.Context)(s: c.Symbol) = s.name.decodedName.toString.trim
}
object Macros {
def nameImpl(c: Compat.Context): c.Expr[Name] = {
import c.universe._
var owner = Compat.enclosingOwner(c)
while (Util.isSynthetic(c)(owner)) owner = owner.owner
val simpleName = Util.getName(c)(owner)
c.Expr[sourcecode.Name](q"""${c.prefix}($simpleName)""")
}
def nameMachineImpl(c: Compat.Context): c.Expr[Name.Machine] = {
import c.universe._
val owner = Compat.enclosingOwner(c)
val simpleName = Util.getName(c)(owner)
c.Expr[Name.Machine](q"""${c.prefix}($simpleName)""")
}
def fullNameImpl(c: Compat.Context): c.Expr[FullName] = {
import c.universe._
val owner = Compat.enclosingOwner(c)
val fullName =
owner.fullName.trim
.split("\\.", -1)
.filterNot(Util.isSyntheticName)
.mkString(".")
c.Expr[sourcecode.FullName](q"""${c.prefix}($fullName)""")
}
def fullNameMachineImpl(c: Compat.Context): c.Expr[FullName.Machine] = {
import c.universe._
val owner = Compat.enclosingOwner(c)
val fullName = owner.fullName.trim
c.Expr[FullName.Machine](q"""${c.prefix}($fullName)""")
}
def sourceFileNameImpl(c: Compat.Context): c.Expr[SourceFileName] = {
import c.universe._
val name = c.enclosingPosition.source.file.name
c.Expr[SourceFileName](q"""${c.prefix}($name)""")
}
def sourceFilePathImpl(c: Compat.Context): c.Expr[SourceFilePath] = {
import c.universe._
val file = c.enclosingPosition.source.path
c.Expr[SourceFilePath](q"""${c.prefix}($file)""")
}
def lineImpl(c: Compat.Context): c.Expr[sourcecode.Line] = {
import c.universe._
val line = c.enclosingPosition.line
c.Expr[sourcecode.Line](q"""${c.prefix}($line)""")
}
def enclosingImpl(c: Compat.Context): c.Expr[Enclosing] = enclosing[Enclosing](c)(
!Util.isSynthetic(c)(_)
)
def enclosingMachineImpl(c: Compat.Context): c.Expr[Enclosing.Machine] =
enclosing[Enclosing.Machine](c)(_ => true)
def pkgImpl(c: Compat.Context): c.Expr[Pkg] = enclosing[Pkg](c)(_.isPackage)
/*
def argsImpl(c: Compat.Context): c.Expr[Args] = {
import c.universe._
val param = Compat.enclosingParamList(c)
val texts = param.map(_.map(p => c.Expr[Text[_]](q"""_root_.verify.sourcecode.Text($p, ${p.name.toString})""")))
val textSeqs = texts.map(s => c.Expr(q"""Seq(..$s)"""))
c.Expr[Args](q"""Seq(..$textSeqs)""")
}
*/
def text[T: c.WeakTypeTag](c: Compat.Context)(v: c.Expr[T]): c.Expr[sourcecode.Text[T]] = {
import c.universe._
val fileContent = new String(v.tree.pos.source.content)
val start = v.tree.collect {
case treeVal =>
treeVal.pos match {
case NoPosition => Int.MaxValue
case p => p.start
}
}.min
import scala.language.existentials
val g = c.asInstanceOf[reflect.macros.runtime.Context].global
val parser = g.newUnitParser(fileContent.drop(start))
parser.expr()
val end = parser.in.lastOffset
val txt = fileContent.slice(start, start + end)
val tree = q"""${c.prefix}(${v.tree}, $txt)"""
c.Expr[sourcecode.Text[T]](tree)
}
sealed trait Chunk
object Chunk {
case class Pkg(name: String) extends Chunk
case class Obj(name: String) extends Chunk
case class Cls(name: String) extends Chunk
case class Trt(name: String) extends Chunk
case class Val(name: String) extends Chunk
case class Var(name: String) extends Chunk
case class Lzy(name: String) extends Chunk
case class Def(name: String) extends Chunk
}
def enclosing[T](c: Compat.Context)(filter: c.Symbol => Boolean): c.Expr[T] = {
import c.universe._
var current = Compat.enclosingOwner(c)
var path = List.empty[Chunk]
while (current != NoSymbol && current.toString != "package ") {
if (filter(current)) {
val chunk = current match {
case x if x.isPackage => Chunk.Pkg
case x if x.isModuleClass => Chunk.Obj
case x if x.isClass && x.asClass.isTrait => Chunk.Trt
case x if x.isClass => Chunk.Cls
case x if x.isMethod => Chunk.Def
case x if x.isTerm && x.asTerm.isVar => Chunk.Var
case x if x.isTerm && x.asTerm.isLazy => Chunk.Lzy
case x if x.isTerm && x.asTerm.isVal => Chunk.Val
}
path = chunk(Util.getName(c)(current)) :: path
}
current = current.owner
}
val renderedPath = path
.map {
case Chunk.Pkg(s) => s + "."
case Chunk.Obj(s) => s + "."
case Chunk.Cls(s) => s + "#"
case Chunk.Trt(s) => s + "#"
case Chunk.Val(s) => s + " "
case Chunk.Var(s) => s + " "
case Chunk.Lzy(s) => s + " "
case Chunk.Def(s) => s + " "
}
.mkString
.dropRight(1)
c.Expr[T](q"""${c.prefix}($renderedPath)""")
}
}