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

dotty.tools.dotc.transform.SymUtils.scala Maven / Gradle / Ivy

package dotty.tools.dotc
package transform

import core._
import Types._
import Contexts._
import Symbols._
import SymDenotations._
import Decorators._
import Names._
import StdNames._
import NameOps._
import NameKinds._
import Flags._
import Annotations._

import language.implicitConversions
import scala.annotation.tailrec

object SymUtils {
  implicit def decorateSymbol(sym: Symbol): SymUtils = new SymUtils(sym)
  implicit def decorateSymDenot(d: SymDenotation): SymUtils = new SymUtils(d.symbol)
}

/** A decorator that provides methods on symbols
 *  that are needed in the transformer pipeline.
 */
class SymUtils(val self: Symbol) extends AnyVal {
  import SymUtils._

  /** All traits implemented by a class or trait except for those inherited through the superclass. */
  def directlyInheritedTraits(implicit ctx: Context): List[ClassSymbol] = {
    val superCls = self.asClass.superClass
    val baseClasses = self.asClass.baseClasses
    if (baseClasses.isEmpty) Nil
    else baseClasses.tail.takeWhile(_ ne superCls).reverse
  }

  /** All traits implemented by a class, except for those inherited through the superclass.
   *  The empty list if `self` is a trait.
   */
  def mixins(implicit ctx: Context): List[ClassSymbol] = {
    if (self is Trait) Nil
    else directlyInheritedTraits
  }

  def isTypeTest(implicit ctx: Context): Boolean =
    self == defn.Any_isInstanceOf || self == defn.Any_typeTest

  def isTypeTestOrCast(implicit ctx: Context): Boolean =
    self == defn.Any_asInstanceOf || isTypeTest

  def isVolatile(implicit ctx: Context): Boolean = self.hasAnnotation(defn.VolatileAnnot)

  def isAnyOverride(implicit ctx: Context): Boolean = self.is(Override) || self.is(AbsOverride)
    // careful: AbsOverride is a term only flag. combining with Override would catch only terms.

  def isSuperAccessor(implicit ctx: Context): Boolean = self.name.is(SuperAccessorName)

  /** A type or term parameter or a term parameter accessor */
  def isParamOrAccessor(implicit ctx: Context): Boolean =
    self.is(Param) || self.is(ParamAccessor)

  /** If this is a constructor, its owner: otherwise this. */
  final def skipConstructor(implicit ctx: Context): Symbol =
    if (self.isConstructor) self.owner else self

  /** The closest properly enclosing method or class of this symbol. */
  final def enclosure(implicit ctx: Context) = {
    self.owner.enclosingMethodOrClass
  }

  /** The closest enclosing method or class of this symbol */
  @tailrec final def enclosingMethodOrClass(implicit ctx: Context): Symbol =
    if (self.is(Method, butNot = Label) || self.isClass) self
    else if (self.exists) self.owner.enclosingMethodOrClass
    else NoSymbol

  /** Apply symbol/symbol substitution to this symbol */
  def subst(from: List[Symbol], to: List[Symbol]): Symbol = {
    @tailrec def loop(from: List[Symbol], to: List[Symbol]): Symbol =
      if (from.isEmpty) self
      else if (self eq from.head) to.head
      else loop(from.tail, to.tail)
    loop(from, to)
  }

  def accessorNamed(name: TermName)(implicit ctx: Context): Symbol =
    self.owner.info.decl(name).suchThat(_ is Accessor).symbol

  def caseAccessors(implicit ctx: Context): List[Symbol] =
    self.info.decls.filter(_ is CaseAccessor)

  def getter(implicit ctx: Context): Symbol =
    if (self.isGetter) self else accessorNamed(self.asTerm.name.getterName)

  def setter(implicit ctx: Context): Symbol =
    if (self.isSetter) self
    else accessorNamed(self.asTerm.name.setterName)

  def field(implicit ctx: Context): Symbol = {
    val thisName = self.name.asTermName
    val fieldName =
      if (self.hasAnnotation(defn.ScalaStaticAnnot)) thisName.getterName
      else thisName.fieldName
    self.owner.info.decl(fieldName).suchThat(!_.is(Method)).symbol
  }

  def isField(implicit ctx: Context): Boolean =
    self.isTerm && !self.is(Method)

  def implClass(implicit ctx: Context): Symbol =
    self.owner.info.decl(self.name.implClassName).symbol

  def traitOfImplClass(implicit ctx: Context): Symbol =
    self.owner.info.decl(self.name.traitOfImplClassName).symbol

  def annotationsCarrying(meta: ClassSymbol)(implicit ctx: Context): List[Annotation] =
    self.annotations.filter(_.symbol.hasAnnotation(meta))

  def withAnnotationsCarrying(from: Symbol, meta: ClassSymbol)(implicit ctx: Context): self.type = {
    self.addAnnotations(from.annotationsCarrying(meta))
    self
  }

  def registerCompanionMethod(name: Name, target: Symbol)(implicit ctx: Context) = {
    if (!self.unforcedDecls.lookup(name).exists) {
      val companionMethod = ctx.synthesizeCompanionMethod(name, target, self)
      if (companionMethod.exists) {
        companionMethod.entered
      }
    }
  }

  /** If this symbol is an enum value or a named class, register it as a child
   *  in all direct parent classes which are sealed.
   *   @param  @late  If true, register only inaccessible children (all others are already
   *                  entered at this point).
   */
  def registerIfChild(late: Boolean = false)(implicit ctx: Context): Unit = {
    def register(child: Symbol, parent: Type) = {
      val cls = parent.classSymbol
      if (cls.is(Sealed) && (!late || child.isInaccessibleChildOf(cls)))
        cls.addAnnotation(Annotation.Child(child))
    }
    if (self.isClass && !self.isAnonymousClass)
      self.asClass.classParents.foreach { parent =>
        val child = if (self.is(Module)) self.sourceModule else self
        register(child, parent)
      }
    else if (self.is(CaseVal, butNot = Method | Module))
      register(self, self.info)
  }

  /** Is this symbol defined locally (i.e. at some level owned by a term) and
   *  defined in a different toplevel class than its supposed parent class `cls`?
   *  Such children are not pickled, and have to be reconstituted manually.
   */
  def isInaccessibleChildOf(cls: Symbol)(implicit ctx: Context): Boolean =
    self.isLocal && !cls.topLevelClass.isLinkedWith(self.topLevelClass)

  /** If this is a sealed class, its known children */
  def children(implicit ctx: Context): List[Symbol] =
    self.annotations.collect {
      case Annotation.Child(child) => child
    }

  /** Is symbol directly or indirectly owned by a term symbol? */
  @tailrec final def isLocal(implicit ctx: Context): Boolean = {
    val owner = self.owner
    if (owner.isTerm) true
    else if (owner.is(Package)) false
    else owner.isLocal
  }

  /** Is symbol a quote operation? */
  def isQuote(implicit ctx: Context): Boolean =
    self == defn.QuotedExpr_apply || self == defn.QuotedType_apply

  /** Is symbol a splice operation? */
  def isSplice(implicit ctx: Context): Boolean =
    self == defn.QuotedExpr_~ || self == defn.QuotedType_~
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy