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

scala.quasiquotes.SymbolTableCompat.scala Maven / Gradle / Ivy

package scala.quasiquotes

import scala.language.implicitConversions
import scala.reflect.internal.SymbolTable
import scala.reflect.NameTransformer
import scala.reflect.ClassTag
import scala.reflect.internal.util.SourceFile
import scala.reflect.internal.util.{Position, OffsetPosition, RangePosition, TransparentPosition}
import scala.reflect.internal.Flags._

trait SymbolTableCompat { self =>
  val global: SymbolTable

  lazy val gen = new { val global: self.global.type = self.global } with TreeGen
  lazy val treeInfo: TreeInfo { val global: self.global.type } = new { val global: self.global.type = self.global } with TreeInfo
  lazy val build: ReificationSupport { val global: self.global.type } = new { val global: self.global.type = self.global } with ReificationSupport

  object symbolTable {
    import global.{nameToNameOps => _, _}
    import definitions._

    type Name = global.Name

    object TermName {
      def apply(s: String): TermName = newTermName(s)
      def unapply(name: TermName): Some[String] = Some(name.toString)
    }

    object TypeName {
      def apply(s: String): TypeName = newTypeName(s)
      def unapply(name: TypeName): Some[String] = Some(name.toString)
    }

    implicit class RichTree(tree: Tree) {
      def nonEmpty = !tree.isEmpty
      def hasExistingSymbol = (tree.symbol ne null) && (tree.symbol ne NoSymbol)
      def hasAttachment[T: ClassTag]: Boolean = tree.attachments.get[T].isDefined
    }

    implicit class RichSymbol(sym: Symbol) {
      def isDefaultGetter = sym.isTerm && (sym.name containsName nme.DEFAULT_GETTER_STRING)
      def setterName: TermName = sym.name.setterName
      def hasVolatileType = sym.tpe.isVolatile && !sym.hasAnnotation(uncheckedStableClass)
    }

    implicit class RichMirror(rb: RootsBase) {
      final def getPackageObjectWithMember(pre: Type, sym: Symbol): Symbol = {
        // The owner of a symbol which requires package qualification may be the
        // package object iself, but it also could be any superclass of the package
        // object.  In the latter case, we must go through the qualifier's info
        // to obtain the right symbol.
        if (sym.owner.isModuleClass) sym.owner.sourceModule // fast path, if the member is owned by a module class, that must be linked to the package object
        else pre member nme.PACKAGE                         // otherwise we have to findMember
      }
    }

    def copyTypeDef(tree: Tree)(
      mods: Modifiers        = null,
      name: Name             = null,
      tparams: List[TypeDef] = null,
      rhs: Tree              = null
    ): TypeDef = tree match {
      case TypeDef(mods0, name0, tparams0, rhs0) =>
        treeCopy.TypeDef(tree,
          if (mods eq null) mods0 else mods,
          if (name eq null) name0 else name,
          if (tparams eq null) tparams0 else tparams,
          if (rhs eq null) rhs0 else rhs
        )
      case t =>
        sys.error("Not a TypeDef: " + t + "/" + t.getClass)
    }

    def copyModuleDef(tree: Tree)(
      mods: Modifiers        = null,
      name: Name             = null,
      impl: Template         = null
    ): ModuleDef = tree match {
      case ModuleDef(mods0, name0, impl0) =>
        treeCopy.ModuleDef(tree,
          if (mods eq null) mods0 else mods,
          if (name eq null) name0 else name,
          if (impl eq null) impl0 else impl
        )
      case t =>
        sys.error("Not a ModuleDef: " + t + "/" + t.getClass)
    }

    object RefTree {
      def apply(qualifier: Tree, name: Name): RefTree = qualifier match {
        case EmptyTree =>
          Ident(name)
        case qual if qual.isTerm =>
          Select(qual, name)
        case qual if qual.isType =>
          assert(name.isTypeName, s"qual = $qual, name = $name")
          SelectFromTypeTree(qual, name.toTypeName)
      }
      def unapply(refTree: RefTree): Option[(Tree, Name)] = Some((refTree.qualifier, refTree.name))
    }

    implicit def enrichSymbolTable(symtab: SymbolTable): symbolTable.type = symbolTable

    implicit def nmeCompat(nme: global.nme.type): compatnme.type = compatnme
    object compatnme {
      val FRESH_TERM_NAME_PREFIX = "x$"
      val MINGT                  = TermName(NameTransformer.encode("->"))
      val QUASIQUOTE_PAT_DEF     = TermName("$quasiquote$pat$def$")
      val SETTER_SUFFIX_STRING   = "_$eq"
    }

    implicit def tpnmeCompat(nme: global.tpnme.type): compattpnme.type = compattpnme
    object compattpnme {
      final val unchecked = TypeName("unchecked")
    }

    implicit def AnyNameOps(name: Name): NameOps[Name]          = new NameOps(name)
    implicit def TermNameOps(name: TermName): NameOps[TermName] = new NameOps(name)
    implicit def TypeNameOps(name: TypeName): NameOps[TypeName] = new NameOps(name)

    /** FIXME: This is a good example of something which is pure "value class" but cannot
     *  reap the benefits because an (unused) $outer pointer so it is not single-field.
     */
    final class NameOps[T <: Name](name: T) {
      import NameTransformer._
      def stripSuffix(suffix: String): T = if (name endsWith suffix) dropRight(suffix.length) else name // OPT avoid creating a Name with `suffix`
      def stripSuffix(suffix: Name): T   = if (name endsWith suffix) dropRight(suffix.length) else name
      def take(n: Int): T                = name.subName(0, n).asInstanceOf[T]
      def drop(n: Int): T                = name.subName(n, name.length).asInstanceOf[T]
      def dropRight(n: Int): T           = name.subName(0, name.length - n).asInstanceOf[T]
      def dropLocal: TermName            = name.toTermName stripSuffix nme.LOCAL_SUFFIX_STRING
      def dropSetter: TermName           = name.toTermName stripSuffix nme.SETTER_SUFFIX_STRING
      def dropModule: T                  = this stripSuffix nme.MODULE_SUFFIX_STRING
      def localName: TermName            = getterName append nme.LOCAL_SUFFIX_STRING
      def setterName: TermName           = getterName append nme.SETTER_SUFFIX_STRING
      def getterName: TermName           = dropTraitSetterSeparator.dropSetter.dropLocal

      private def dropTraitSetterSeparator: TermName =
        name.toString indexOf nme.TRAIT_SETTER_SEPARATOR_STRING match {
          case -1  => name.toTermName
          case idx => name.toTermName drop idx drop nme.TRAIT_SETTER_SEPARATOR_STRING.length
        }
    }

    def currentFreshNameCreator = globalFreshNameCreator

    // create fresh term/type name using implicit fresh name creator
    def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX)(implicit creator: FreshNameCreator): TermName = newTermName(creator.newName(prefix))
    def freshTypeName(prefix: String)(implicit creator: FreshNameCreator): TypeName = newTypeName(creator.newName(prefix))

    // Extractor that matches names which were generated by some
    // FreshNameCreator with known prefix. Extracts user-specified
    // prefix that was used as a parameter to newName by stripping
    // global creator prefix and unique number in the end of the name.
    class FreshNameExtractor(creatorPrefix: String = "") {
      // quote prefix so that it can be used with replaceFirst
      // which expects regExp rather than simple string
      val quotedCreatorPrefix = java.util.regex.Pattern.quote(creatorPrefix)

      def unapply(name: Name): Option[String] = {
        val sname = name.toString
        // name should start with creatorPrefix and end with number
        if (!sname.startsWith(creatorPrefix) || !sname.matches("^.*\\d*$")) None
        else Some(NameTransformer.decode(sname.replaceFirst(quotedCreatorPrefix, "").replaceAll("\\d*$", "")))
      }
    }

    def rangePos(source: SourceFile, start: Int, point: Int, end: Int) =
      new OffsetPosition(source, point)

    implicit class RichPosition(position: Position.type) {
      private def validate[T <: Position](pos: T): T = {
        if (pos.isRange) assert(pos.start <= pos.end, s"bad position: ${pos.show}")
        pos
      }
      def offset(source: SourceFile, point: Int): Position                            = validate(new OffsetPosition(source, point))
      def range(source: SourceFile, start: Int, point: Int, end: Int): Position       = validate(new RangePosition(source, start, point, end))
      def transparent(source: SourceFile, start: Int, point: Int, end: Int): Position = validate(new TransparentPosition(source, start, point, end))
    }

    implicit class RichModifiers(mods: Modifiers) {
      def isLocalToThis = mods hasFlag LOCAL
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy