All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.meta.internal.trees.NamerMacros.scala Maven / Gradle / Ivy

Go to download

Bag of private and public helpers used in scala.meta's APIs and implementations

The newest version!
package scala.meta
package internal
package trees

import org.scalameta.internal.MacroHelpers

import scala.collection.mutable

object CommonNamerMacros {

  private val quasiName = "Quasi"

}

trait CommonNamerMacros extends MacroHelpers {
  import c.universe._

  lazy val TreeClass = tq"_root_.scala.meta.Tree"
  lazy val ClassifierClass = tq"_root_.scala.meta.classifiers.Classifier"
  lazy val ArrayClassMethod = q"_root_.scala.meta.internal.trees.`package`.arrayClass"
  lazy val ClassOfMethod = q"_root_.scala.Predef.classOf"
  lazy val TokensClass = tq"_root_.scala.meta.tokens.Tokens"
  lazy val AstAnnotation = tq"_root_.scala.meta.internal.trees.ast"
  lazy val PositionClass = tq"_root_.scala.meta.inputs.Position"
  lazy val PositionModule = q"_root_.scala.meta.inputs.Position"
  lazy val PointClass = tq"_root_.scala.meta.inputs.Point"
  lazy val PointModule = q"_root_.scala.meta.inputs.Point"
  lazy val OriginClass = tq"_root_.scala.meta.trees.Origin"
  lazy val OriginModule = q"_root_.scala.meta.trees.Origin"
  lazy val DialectClass = tq"_root_.scala.meta.Dialect"

  def mkClassifier(name: TypeName): List[Tree] = {
    val q"..$classifierBoilerplate" = q"""
      private object sharedClassifier extends $ClassifierClass[$TreeClass, $name] {
        def apply(x: $TreeClass): _root_.scala.Boolean = x.isInstanceOf[$name]
      }
      implicit def ClassifierClass[T <: $TreeClass]: $ClassifierClass[T, $name] = {
        sharedClassifier.asInstanceOf[$ClassifierClass[T, $name]]
      }
    """
    classifierBoilerplate
  }

  private val quasiTypeName = TypeName(CommonNamerMacros.quasiName)
  @inline
  private[trees] def isQuasiClass(cdef: ClassDef) = isQuasiName(cdef.name)
  @inline
  private[trees] def isQuasiName(name: TypeName) = name.toString == CommonNamerMacros.quasiName

  private def mkQuasiParent(parent: Tree): Tree = parent match {
    case Ident(name) => Select(Ident(name.toTermName), quasiTypeName)
    case Select(qual, name) => Select(Select(qual, name.toTermName), quasiTypeName)
    case unsupported => c.abort(unsupported.pos, s"implementation restriction: unsupported parent")
  }

  def mkQuasi(
      name: TypeName,
      parents: List[Tree],
      params: List[ValDef],
      copyParamss: Iterable[List[ValDef]],
      extraAbstractDefs: Iterable[Tree],
      extraStubs: String*
  ): ClassDef = {
    val qmods = Modifiers(NoFlags, TypeName("meta"), List(q"new $AstAnnotation"))
    val qparents = tq"$name" +: tq"$QuasiClass" +: parents.map(mkQuasiParent)

    val qstats = mutable.ListBuffer[Tree]()
    qstats +=
      q"""
      def pt: _root_.java.lang.Class[_] = {
        $ArrayClassMethod($ClassOfMethod[$name], this.rank)
      }
    """

    val stub = {
      val unsupportedUnquotingPosition = "unsupported unquoting position"
      val unsupportedSplicingPosition = "unsupported splicing position"
      val message =
        q"if (this.rank == 0) $unsupportedUnquotingPosition else $unsupportedSplicingPosition"
      q"throw new $UnsupportedOperationException($message)"
    }
    val stubbedMembers = new mutable.HashSet[String]
    def markStubbedMemberName(name: TermName): Boolean = stubbedMembers.add(name.toString)
    def addStubbedMemberWithName(name: TermName): Unit =
      if (markStubbedMemberName(name)) qstats += q"def $name = $stub"
    def addStubbedOverrideMember(tree: ValOrDefDefApi): Unit = if (markStubbedMemberName(tree.name)) {
      val stat = tree match {
        case x: ValDefApi => q"override def ${x.name} = $stub"
        case x: DefDefApi => q"override def ${x.name}[..${x.tparams}](...${x.vparamss}) = $stub"
        case _ => c.abort(tree.pos, s"Can't stub, not a 'val' or 'def': $tree")
      }
      qstats += stat
    }

    params.foreach(x => addStubbedMemberWithName(x.name))
    extraStubs.foreach(x => addStubbedMemberWithName(TermName(x)))
    copyParamss.foreach(x => qstats += q"final override def copy(..$x): $name = $stub")

    extraAbstractDefs.foreach {
      case x: ValOrDefDefApi
          if x.mods.hasFlag(Flag.ABSTRACT) || x.mods.hasFlag(Flag.OVERRIDE) || x.rhs.isEmpty =>
        addStubbedOverrideMember(x)
      case _ =>
    }

    q"$qmods class $quasiTypeName(rank: $IntClass, tree: $TreeClass) extends ..$qparents { ..$qstats }"
  }

  def mkAstInfo(name: TypeName): Tree = {
    val termName = name.toTermName
    val ctor = if (isQuasiName(name)) q"$termName" else q"$termName.${quasiTypeName.toTermName}"
    q"""
      implicit def astInfo: $AstInfoClass[$name] = new $AstInfoClass[$name] {
        def quasi(rank: $IntClass, tree: $TreeClass): $name with $QuasiClass = $ctor.apply(rank, tree)
      }
    """
  }

  protected case class PrivateField(field: ValOrDefDef, persist: Boolean = false)
  protected case class PrivateFields(
      prototype: PrivateField,
      parent: PrivateField,
      origin: PrivateField
  ) {
    def asList = prototype :: parent :: origin :: Nil
  }

  protected def getPrivateFields(iname: TypeName): PrivateFields = PrivateFields(
    PrivateField(q"@$TransientAnnotation private[meta] override val privatePrototype: $iname = null"),
    PrivateField(q"private[meta] override val privateParent: $TreeClass = null"),
    PrivateField(q"override val origin: $OriginClass = $OriginModule.None", true)
  )

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy