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

retypecheck.Typer.scala Maven / Gradle / Ivy

package retypecheck

import org.scalamacros.resetallattrs._
import scala.collection.mutable
import scala.reflect.internal.util
import scala.reflect.macros.blackbox
import scala.reflect.macros.TypecheckException

object ReTyper {
  def apply(c: blackbox.Context) = new ReTyper[c.type](c)
}

/**
 * heavy wizardry to fight the dark forces of Scala type-checking in macros
 */
class ReTyper[+C <: blackbox.Context](val c: C) {
  import c.universe._
  import Flag._

  /**
   * Re-type-checks the given tree, i.e., first un-type-checks it and then
   * type-checks it again using [[untypecheck]] and [[typecheck]], respectively.
   */
  def retypecheck(tree: Tree): Tree =
    typecheck(untypecheck(tree))

  /**
   * Re-type-checks the given tree resetting all symbols using the
   * `org.scalamacros.resetallattrs` library, i.e., first un-type-checks it and
   * then type-checks it again using [[untypecheckAll]] and [[typecheck]],
   * respectively.
   */
  def retypecheckAll(tree: Tree): Tree =
    typecheck(untypecheckAll(tree))

  /**
   * Type-checks the given tree. If type-checking fails, aborts the macro
   * expansion issuing the type-checking error.
   *
   * The type-checking process distorts certain ASTs (such as representations of
   * extractors, lazy values or case classes) in a way that they cannot be
   * type-checked again. The issue is described in
   * [[https://issues.scala-lang.org/browse/SI-5464 SI-5464]].
   */
  def typecheck(tree: Tree): Tree =
    try {
      val typecheckedTree = if (tree.tpe != null) tree else c typecheck tree
      assignMissingTypes(
        maskUnusedImports(
          fixTypecheck(
            selfReferenceFixer transform typecheckedTree)))
    }
    catch {
      case TypecheckException(pos: Position @unchecked, msg) =>
        c.abort(pos, msg)
      case TypecheckException(_, msg) =>
        c.abort(c.enclosingPosition, msg)
    }

  /**
   * Un-type-checks the given tree.
   *
   * The type-checking process distorts certain ASTs (such as representations of
   * extractors, lazy values or case classes) in a way that they cannot be
   * type-checked again. The issue is described in
   * [[https://issues.scala-lang.org/browse/SI-5464 SI-5464]].
   *
   * This method tries to restore the AST to a form, which can be type-checked
   * again.
   */
  def untypecheck(tree: Tree): Tree =
    fixUntypecheck(
      c untypecheck
        maskUnusedImports(
          fixTypesAndSymbols(
            fixCaseClasses(
              fixTypecheck(tree)))))

  /**
   * Un-type-checks the given tree resetting all symbols using the
   * `org.scalamacros.resetallattrs` library.
   *
   * The type-checking process distorts certain ASTs (such as representations of
   * extractors, lazy values or case classes) in a way that they cannot be
   * type-checked again. The issue is described in
   * [[https://issues.scala-lang.org/browse/SI-5464 SI-5464]].
   *
   * This method tries to restore the AST to a form, which can be type-checked
   * again.
   */
  def untypecheckAll(tree: Tree): Tree =
    fixUntypecheck(
      c resetAllAttrs
        (selfReferenceFixer transform
          maskUnusedImports(
            fixTypesAndSymbols(
              fixCaseClasses(
                fixTypecheck(tree))))))

  /**
   * Cleans the flag set of the given modifiers.
   *
   * The type-checking process annotates definitions with various flags. Some
   * of them can also be inserted by user-code or even have a corresponding
   * Scala language construct, but others are only used by the type-checker.
   * Certain flags can interfere with type-checking and cause it to fail. Those
   * flags can be safely removed and will be re-inserted during type-checking
   * when needed.
   *
   * This method eliminates some problematic cases.
   */
  def cleanModifiers(mods: Modifiers, removeFlags: FlagSet = NoFlags): Modifiers = {
    val possibleFlags = Seq(ABSOVERRIDE, ABSTRACT, ARTIFACT, BYNAMEPARAM,
      CASE, CASEACCESSOR, CONTRAVARIANT, COVARIANT, DEFAULTINIT, DEFAULTPARAM,
      DEFERRED, FINAL, IMPLICIT, INTERFACE, LAZY, LOCAL, MACRO, MUTABLE,
      OVERRIDE, PARAM, PARAMACCESSOR, PRESUPER, PRIVATE, PROTECTED,
      SEALED, STABLE, SYNTHETIC, TRAIT)

    val flags = possibleFlags.foldLeft(NoFlags) { (flags, flag) =>
      if ((removeFlags != (removeFlags | flag)) && (mods hasFlag flag))
        flags | flag
      else
        flags
    }

    Modifiers(flags, mods.privateWithin, mods.annotations)
  }


  /**
   * Creates an AST representing the given type.
   *
   * The type-checking process creates synthetic type trees and it is possible
   * to insert trees with type information, but it is not easily possible to
   * create an AST for a given type.
   *
   * This method attempts to create such an AST, which is persistent across
   * type-checking and un-type-checking.
   */
  def createTypeTree(tpe: Type, pos: Position): Tree =
    createTypeTree(tpe, pos, Set.empty)

  def createTypeTree(tpe: Type, pos: Position,
      owners: collection.Set[Symbol]): Tree = {
    val singleton = typeOf[Singleton]

    val allOwners = ownerChain(c.internal.enclosingOwner).toSet union owners

    def expandPrefix(pre: Type, sym: Symbol) =
      if (pre != NoPrefix) {
        val preTree = expandType(pre)

        val preSymbolSingle = pre match {
          case SingleType(_, sym) => sym
          case _ => pre.typeSymbol
        }

        val prePathTree = preTree match {
          case SingletonTypeTree(Select(qualifier, name)) =>
            Some(Select(qualifier, name.toTermName))
          case SingletonTypeTree(Ident(name)) =>
            Some(Ident(name.toTermName))
          case Select(qualifier, name) =>
            Some(Select(qualifier, name.toTermName))
          case Ident(name) =>
            Some(Ident(name.toTermName))
          case _ =>
            None
        }

        val pathTree = prePathTree match {
          case Some(Select(tree, termNames.PACKAGE)) if tree.symbol.isPackage =>
            tree

          case Some(tree) =>
            val preSymbol = pre.typeSymbol
            if (preSymbol.isModuleClass) {
              val symbol = preSymbol.asClass.module
              val owner = symbol.owner
              if (symbol.name == termNames.PACKAGE && owner.isModuleClass) {
                val symbol = owner.asClass.module
                tree.withAttrs(symbol, symbol.typeSignature, pos)
              }
              else if (preSymbol == preSymbolSingle)
                tree.withAttrs(symbol, symbol.typeSignature, pos)
              else
                tree.withAttrs(preSymbolSingle, pre, pos)
            }
            else
              tree.withAttrs(preSymbolSingle, pre, pos)

          case _ =>
            preTree
        }

        val typeProjection = preTree match {
          case This(_) =>
            false
          case _ =>
            preSymbolSingle.isType &&
              !preSymbolSingle.isModule &&
              !preSymbolSingle.isModuleClass &&
              !preSymbolSingle.isPackage &&
              !preSymbolSingle.isPackageClass &&
              sym.isType
        }

        if (typeProjection)
          SelectFromTypeTree(preTree, sym.asType.name)
        else
          Select(pathTree, sym.name)
      }
      else
        Ident(sym.name)

    def expandType(tpe: Type): Tree = {
      val tree = tpe match {
        case ThisType(pre)
            if pre.isType && !pre.isModule && !pre.isPackage &&
               pre.name.toString != "" =>
          This(pre.asType.name).withAttrs(tpe.typeSymbol, tpe, pos)

        case ThisType(pre)
            if isAccessible(pre, allOwners) &&
               pre.name.toString != "" =>
          expandSymbol(pre, pos)

        case TypeRef(pre, sym, args) if sym.name.toString != "" =>
          val module = if (sym.isModuleClass) sym.asClass.module else sym
          val prefix = expandPrefix(pre, module)
          val symbol = if (isNonRepresentableType(tpe)) NoSymbol else module
          if (args.nonEmpty)
            AppliedTypeTree(
                prefix.withAttrs(
                  symbol, internal.typeRef(pre, sym, List.empty), pos),
                args map expandType).withAttrs(
              symbol, tpe, pos)
          else if (sym.isModuleClass)
            SingletonTypeTree(prefix.withAttrs(symbol, tpe, pos))
          else
            prefix.withAttrs(symbol, tpe, pos)

        case SingleType(pre, sym) if sym.name.toString != "" =>
          val module = if (sym.isModuleClass) sym.asClass.module else sym
          expandPrefix(pre, module) match {
            case Select(prefix, termNames.PACKAGE) if pre.typeSymbol.isPackage =>
              prefix
            case prefix =>
              SingletonTypeTree(prefix.withAttrs(module, tpe, pos))
          }

        case TypeBounds(lo, hi) =>
          TypeBoundsTree(
            if (lo =:= definitions.NothingTpe) EmptyTree else expandType(lo),
            if (hi =:= definitions.AnyTpe) EmptyTree else expandType(hi))

        case ExistentialType(quantified, underlying) =>
          val singletons = mutable.Map.empty[String, TermName]

          val whereClauses = quantified map { quantified =>
            quantified.typeSignature match {
              case TypeBounds(_, _) =>
                val mods = Modifiers(
                  DEFERRED | (if (quantified.isSynthetic) SYNTHETIC else NoFlags))

                val name = quantified.name.toString

                val valueType =
                  quantified.typeSignature match {
                    case TypeBounds(definitions.NothingTpe, RefinedType(parents, scope))
                        if name endsWith ".type" =>
                      
                      val (singletonType, types) = parents partition { _ =:= singleton }

                      if (singletonType.nonEmpty && types.nonEmpty) {
                        if (types.size > 1 || scope.nonEmpty)
                          Some(internal.refinedType(types, scope))
                        else
                          Some(types.head)
                      }
                      else
                        None
                    case _ =>
                      None
                  }

                if (valueType.nonEmpty) {
                  val termName = TermName(name.substring(0, name.length - 5))
                  singletons += name -> termName

                  Some(ValDef(
                    mods,
                    termName,
                    expandType(valueType.get),
                    EmptyTree))
                }
                else
                  Some(TypeDef(
                    mods,
                    quantified.name.toTypeName,
                    List.empty,
                    expandType(quantified.typeSignature)))

              case _ =>
                None
            }
          }

          if (whereClauses forall { _.nonEmpty }) {
            object singletonReferenceFixer extends Transformer {
              override def transform(tree: Tree) = tree match {
                case Ident(name) =>
                  (singletons get name.toString
                    map { name => SingletonTypeTree(Ident(name)) }
                    getOrElse tree)
                case Select(Ident(name), selectedName) =>
                  (singletons get name.toString
                    map { name => Select(Ident(name), selectedName).withAttrs(
                      tree.symbol, tree.tpe, tree.pos) }
                    getOrElse tree)
                case _ =>
                  super.transform(tree)
              }
            }

            singletonReferenceFixer transform
              ExistentialTypeTree(expandType(underlying), whereClauses.flatten)
          }
          else
            TypeTree(tpe)

        case ClassInfoType(parents, decls, typeSymbol) =>
          val publicDecls = internal.newScopeWith(decls.toSeq filter { symbol =>
            symbol.isPublic && !symbol.isConstructor
          }: _*)
          expandType(internal.refinedType(parents, publicDecls, typeSymbol))

        case RefinedType(parents, scope) =>
          def refiningType(sym: TypeSymbol): TypeDef = {
            val anyTree = Select(
              Select(
                Ident(termNames.ROOTPKG),
                TermName("scala")),
              TypeName("Any"))

            val (tree, deferred) = expandType(sym.typeSignature) match {
              case `anyTree` if sym.isClass =>
                TypeBoundsTree(EmptyTree, EmptyTree) -> true
              case tree if sym.isClass =>
                TypeBoundsTree(EmptyTree, tree) -> true
              case tree @ TypeBoundsTree(_, _) =>
                tree -> true
              case tree =>
                tree -> false
            }

            TypeDef(
              if (deferred) Modifiers(DEFERRED) else Modifiers(),
              sym.name,
              sym.typeParams map { param => refiningType(param.asType) },
              tree)
          }

          def refiningVal(sym: TermSymbol, flags: FlagSet): ValDef =
            ValDef(
              Modifiers(flags),
              sym.name,
              expandType(sym.typeSignature.finalResultType),
              EmptyTree)

          def refiningDef(sym: MethodSymbol, flags: FlagSet): DefDef =
            DefDef(
              Modifiers(flags),
              sym.name,
              sym.typeParams map { param => refiningType(param.asType) },
              sym.paramLists map {
                _ map { param => refiningVal(param.asTerm, PARAM) }
              },
              expandType(sym.typeSignature.finalResultType),
              EmptyTree)

          val body = scope map { symbol =>
            if (symbol.isMethod) {
              val method = symbol.asMethod
              if (method.isStable)
                Some(refiningVal(method, DEFERRED))
              else
                Some(refiningDef(method, DEFERRED))
            }
            else if (symbol.isType)
              Some(refiningType(symbol.asType))
            else
              None
          }

          if (body exists { _.isEmpty })
            TypeTree(tpe)
          else if (body.isEmpty && parents.size == 1)
            expandType(parents.head)
          else
            CompoundTypeTree(
              Template(parents map expandType, noSelfType, body.toList.flatten))

        case _ =>
          TypeTree(tpe)
      }

      if (tree.tpe == null)
        internal.setType(tree, tpe)
      internal.setPos(tree, pos)
    }

    expandType(tpe)
  }

  /**
   * Corrects the symbols for the given tree that has a macro annotation which
   * invoked the type-checker on the tree.
   *
   * Type-checking ASTs that represent Scala objects in a macro annotation may
   * lead to recursive expansion of the same tree (which the macro annotation
   * needs to handle). The symbols that the type-checker associates to the tree
   * can then originate from different expansions.
   *
   * This method tries to correct the symbols in the tree, such that the same
   * entities are associated to the same symbol.
   */
  def fixTypedAnnotteeSymbols(annottee: Tree) = {
    val fixingSymbols = mutable.Set.empty[Symbol]

    def modulePath(symbol: Symbol): Option[List[Symbol]] =
      if (annottee.symbol.info.baseClasses contains symbol.owner)
        None
      else if (symbol.owner == annottee.symbol.owner)
        Some(List(symbol))
      else if (symbol.owner.isTerm && (!symbol.owner.isMethod || symbol.owner.asMethod.paramLists.isEmpty))
        modulePath(symbol.owner) map { symbol :: _ }
      else if (symbol.owner.isModuleClass)
        modulePath(symbol.owner.asClass.module) map { symbol :: _ }
      else
        None

    def sameSignature(symbol0: Symbol, symbol1: Symbol) =
      (symbol0.isType && symbol1.isType) ||
      (symbol0.isTerm && symbol1.isTerm &&
       !symbol0.isMethod && !symbol1.isMethod) ||
      (symbol0.isMethod && symbol1.isMethod &&
       (symbol0.asMethod.paramLists map { _  map { _.fullName } }) ==
       (symbol1.asMethod.paramLists map { _  map { _.fullName } }))

    def fixSymbol(symbol: Symbol): Option[Symbol] = {
      fixSymbolInfo(symbol)

      modulePath(symbol) flatMap { list =>
        if (list.last.name.toString == annottee.symbol.name.toString) {
          val fixedSymbol = list.dropRight(1).foldRight(annottee.symbol) { (treeSymbol, symbol) =>
            ((symbol.info member treeSymbol.name).alternatives
                collectFirst { case symbol if sameSignature(symbol, treeSymbol) => symbol }
                getOrElse NoSymbol)
          }

          if (fixedSymbol != NoSymbol) {
            val fixedFlippedSymbol =
              if (symbol.isModuleClass && fixedSymbol.isModule)
                fixedSymbol.asModule.moduleClass
              else if (symbol.isModule && fixedSymbol.isModuleClass)
                fixedSymbol.asClass.module
              else
                fixedSymbol

            if (fixedFlippedSymbol != symbol) {
              fixSymbolInfo(fixedFlippedSymbol)
              Some(fixedFlippedSymbol)
            }
            else
              None
          }
          else
            None
        }
        else
          None
      }
    }

    def fixSymbolInfo(symbol: Symbol): Unit =
      if (fixingSymbols.add(symbol)) {
        fixType(symbol.info) foreach { internal.setInfo(symbol, _) }
        symbol.overrides foreach fixSymbolInfo
      }

    def fixType(tpe: Type): Option[Type] = tpe match {
      case AnnotatedType(annotations, underlying) =>
        annotations foreach { annotation => traverser traverse annotation.tree }
        fixType(underlying) map { internal.annotatedType(annotations, _) }

      case BoundedWildcardType(TypeBounds(lo, hi)) =>
        val opLo = fixType(lo)
        val opHi = fixType(hi)
        if (opLo.nonEmpty || opHi.nonEmpty)
          Some(internal.boundedWildcardType(internal.typeBounds(opLo getOrElse lo, opHi getOrElse hi)))
        else
          None

      case ClassInfoType(parents, decls, typeSymbol) =>
        val opParents = parents map { parent => fixType(parent) -> parent }
        val opTypeSymbol = fixSymbol(typeSymbol)
        if ((opParents exists { case (opParent, _) => opParent.nonEmpty }) || opTypeSymbol.nonEmpty)
          Some(internal.classInfoType(
            opParents map { case (opParent, parent) => opParent getOrElse parent },
            decls,
            opTypeSymbol getOrElse typeSymbol))
        else
          None

      case ExistentialType(quantified, underlying) =>
        val opQuantified = quantified map { sym => fixSymbol(sym) -> sym }
        val opUnderlying = fixType(underlying)
        if ((opQuantified exists { case (opSym, sym) => opSym.nonEmpty }) || opQuantified.nonEmpty)
          Some(internal.existentialType(
            opQuantified map { case (opSym, sym) => opSym getOrElse sym },
            opUnderlying getOrElse underlying))
        else
          None

      case MethodType(params, resultType) =>
        val opParams = params map { param => fixSymbol(param) -> param }
        val opResultType = fixType(resultType)
        if ((opParams exists { case (opParam, _) => opParam.nonEmpty }) || opResultType.nonEmpty)
          Some(internal.methodType(
            opParams map { case (opParam, param) => opParam getOrElse param },
            opResultType getOrElse resultType))
        else
          None

      case NullaryMethodType(resultType) =>
        fixType(resultType) map internal.nullaryMethodType

      case PolyType(typeParams, resultType) =>
        val opTypeParams = typeParams map { typeParam => fixSymbol(typeParam) -> typeParam }
        val opResultType = fixType(resultType)
        if ((opTypeParams exists { case (opTypeParam, _) => opTypeParam.nonEmpty }) || opResultType.nonEmpty)
          Some(internal.polyType(
            opTypeParams map { case (opTypeParam, typeParam) => opTypeParam getOrElse typeParam },
            opResultType getOrElse resultType))
        else
          None

      case RefinedType(parents, scope) =>
        val opParents = parents map { parent => fixType(parent) -> parent }
        if (opParents exists { case (opParent, _) => opParent.nonEmpty })
          Some(internal.refinedType(
            opParents map { case (opParent, parent) => opParent getOrElse parent },
            scope))
        else
          None

      case SingleType(pre, sym) =>
        val opPre = fixType(pre)
        val opSym = fixSymbol(sym)
        if (opPre.nonEmpty || opSym.nonEmpty)
          Some(internal.singleType(opPre getOrElse pre, opSym getOrElse sym))
        else
          None

      case SuperType(thistpe: Type, supertpe: Type) =>
        val opThistpe = fixType(thistpe)
        val opSupertpe = fixType(supertpe)
        if (opThistpe.nonEmpty || opSupertpe.nonEmpty)
          Some(internal.superType(opThistpe getOrElse thistpe, opSupertpe getOrElse supertpe))
        else
          None

      case ThisType(pre) =>
        fixSymbol(pre) map internal.thisType

      case TypeBounds(lo, hi) =>
        val opLo = fixType(lo)
        val opHi = fixType(hi)
        if (opLo.nonEmpty || opHi.nonEmpty)
          Some(internal.typeBounds(opLo getOrElse lo, opHi getOrElse hi))
        else
          None

      case TypeRef(pre, sym, args) =>
        val opPre = fixType(pre)
        val opSym = fixSymbol(sym)
        val opArgs = args map { arg => fixType(arg) -> arg }
        if (opPre.nonEmpty || opSym.nonEmpty || (opArgs exists { case (opArg, _) => opArg.nonEmpty }))
          Some(internal.typeRef(
            opPre getOrElse pre,
            opSym getOrElse sym,
            opArgs map { case (opArg, arg) => opArg getOrElse arg }))
        else
          None

      case _ =>
        None
    }

    object traverser extends Traverser {
      override def traverse(tree: Tree) = {
        tree match {
          case tree: TypeTree =>
            if (tree.original != null)
              traverse(tree.original)
          case _ =>
            if (tree.symbol != null)
              fixSymbol(tree.symbol) foreach { internal.setSymbol(tree, _) }
        }

        if (tree.tpe != null)
          fixType(tree.tpe) foreach { internal.setType(tree, _) }

        super.traverse(tree)
      }
    }

    if (annottee.symbol != null && annottee.symbol.isModule)
      traverser traverse annottee
  }


  private def assignMissingTypes(tree: Tree): tree.type = {
    def typeDef(tree: TypeDef, symbol: TypeSymbol): Unit = {
      if (tree.tparams.size == symbol.typeParams.size)
        (tree.tparams zip symbol.typeParams) foreach { case (tree, symbol) =>
          if (tree.name == symbol.name)
            typeDef(tree, symbol.asType)
        }

      if (tree.rhs.tpe == null)
        internal.setType(tree.rhs, symbol.typeSignature)
    }

    def valDef(tree: ValDef, symbol: TermSymbol): Unit = {
      if (tree.tpt.tpe == null)
        internal.setType(tree.tpt, symbol.typeSignature.finalResultType)
    }

    def defDef(tree: DefDef, symbol: MethodSymbol): Unit = {
      val method = symbol.asMethod

      if (tree.tparams.size == method.typeParams.size)
        (tree.tparams zip method.typeParams) foreach { case (tree, symbol) =>
          if (tree.name == symbol.name)
            typeDef(tree, symbol.asType)
        }

      if (tree.vparamss.size == method.paramLists.size)
        (tree.vparamss zip method.paramLists) foreach { case (trees, symbols) =>
          (trees zip symbols) foreach { case (tree, symbol) =>
            if (tree.name == symbol.name)
              valDef(tree, symbol.asTerm)
          }
        }

      if (tree.tpt.tpe == null)
        internal.setType(tree.tpt, symbol.typeSignature.finalResultType)
    }

    def members(trees: Iterable[Tree], symbols: Iterable[Symbol]): Unit =
      if (trees.size == symbols.size)
        (trees zip symbols) foreach {
          case (tree: TypeDef, symbol)
              if symbol.isType && tree.name == symbol.name =>
            typeDef(tree, symbol.asType)
          case (tree: ValDef, symbol)
              if symbol.isTerm && tree.name == symbol.name =>
            valDef(tree, symbol.asTerm)
          case (tree: DefDef, symbol)
              if symbol.isMethod && tree.name == symbol.name =>
            defDef(tree, symbol.asMethod)
          case _ =>
        }

    object traverser extends Traverser {
      override def traverse(tree: Tree): Unit = {
        (tree, tree.tpe) match {
          case (tree: TypeTree, tpe) if tree.original != null =>
            if (tree.original.tpe == null)
              internal.setType(tree.original, tpe)
            traverse(tree.original)

          case (CompoundTypeTree(Template(parents, _, body)),
                RefinedType(parentTypes, decls)) =>
            members(body, decls)
            if (parents.size == parentTypes.size)
              (parents zip parentTypes) foreach { case (tree, tpe) =>
                if (tree.tpe == null)
                  internal.setType(tree, tpe)
              }

          case (ExistentialTypeTree(tpt, whereClauses),
                ExistentialType(quantified, underlying)) =>
            members(whereClauses, quantified)
            if (tpt.tpe == null)
              internal.setType(tpt, underlying)

          case (TypeBoundsTree(lo, hi),
                TypeBounds(loType, hiType)) =>
            if (lo.tpe == null)
              internal.setType(lo, loType)
            if (hi.tpe == null)
              internal.setType(hi, hiType)

          case (AppliedTypeTree(tpt, args),
                TypeRef(pre, sym, typeArgs)) =>
            if (args.size == typeArgs.size)
              (args zip typeArgs) foreach { case (tree, tpe) =>
                if (tree.tpe == null)
                  internal.setType(tree, tpe)
              }
            if (tpt.tpe == null)
              internal.setType(tpt, internal.typeRef(pre, sym, List.empty))

          case (Select(qualifier, _),
                TypeRef(pre, _, _)) if qualifier.tpe == null =>
            internal.setType(qualifier, pre)

          case _ =>
        }

        super.traverse(tree)
      }
    }

    traverser traverse tree

    tree
  }


  private def expandSymbol(symbol: Symbol, pos: Position): Tree = {
    val tree =
      if (symbol == c.mirror.RootClass)
        Ident(termNames.ROOTPKG)
      else if (!symbol.owner.isModule &&
               !symbol.owner.isModuleClass &&
               !symbol.owner.isPackage &&
               !symbol.owner.isPackageClass)
        Ident(symbol.name.toTermName)
      else
        Select(expandSymbol(symbol.owner, pos), symbol.name.toTermName)

    val sym = if (symbol.isModuleClass) symbol.asClass.module else symbol
    tree.withAttrs(sym, sym.typeSignature, pos)
  }

  private def ownerChain(symbol: Symbol): List[Symbol] =
    if (symbol.owner == NoSymbol)
      Nil
    else
      symbol :: ownerChain(symbol.owner)

  private def isAccessible(symbol: Symbol, owners: collection.Set[Symbol]) =
    ownerChain(symbol).toSet diff owners forall { symbol =>
      !symbol.isPrivate && !symbol.isProtected
    }

  private def isNonRepresentableType(tpe: Type) = tpe match {
    case TypeRef(NoPrefix, name, _) => name.toString endsWith ".type"
    case _ => false
  }


  private def fixTypesAndSymbols(tree: Tree): Tree = {
    val extractNamedArg =
      try {
        val namedArgClass = Class.forName(s"scala.reflect.internal.Trees$$NamedArg")
        val namedArg = c.universe.getClass.getMethod("NamedArg").invoke(c.universe)
        val unapply = namedArg.getClass.getMethod("unapply", namedArgClass)

        Some({ tree: Tree =>
          if (namedArgClass isInstance tree)
            try unapply.invoke(namedArg, tree) match {
              case Some((lhs: Tree, rhs: Tree)) => Some((lhs, rhs))
              case _ => None
            }
            catch { case _: ReflectiveOperationException => None }
          else
            None
        })
      }
      catch { case _: ReflectiveOperationException => None }

    object NamedArg {
      def unapply(tree: Tree): Option[(Tree, Tree)] =
        extractNamedArg flatMap { _(tree) }
    }

    val nowarn =
      try Some(c.mirror.staticClass("_root_.scala.annotation.nowarn").toType)
      catch { case _: ScalaReflectionException => None }

    val scala2134plus = c.classPath exists { url =>
      val file = url.getFile
      val index = file indexOf "/scala-library-"
      val start = index + 15
      val end = index + 22
      val version =
        if (index != -1 && end <= file.length)
          file.substring(start, end)
        else
          ""
      (version startsWith "2.13.") &&
      version != "2.13.0." && version != "2.13.0-" &&
      version != "2.13.1." && version != "2.13.1-" &&
      version != "2.13.2." && version != "2.13.2-" &&
      version != "2.13.3." && version != "2.13.3-"
    }

    val definedTypeSymbols = {
      var symbols = Set.empty[TypeSymbol]

      var currentSymbols = (tree collect {
        case tree @ TypeDef(_, _, _, _) if tree.symbol.isType =>
          tree.symbol.asType
        case tree @ ClassDef(_, _, _, _) if tree.symbol.isClass =>
          tree.symbol.asType
        case tree @ ModuleDef(_, _, _) if tree.symbol.isModule =>
          tree.symbol.asModule.moduleClass.asType
      }).toSet

      var foundAdditionals = true
      while (foundAdditionals) {
        symbols ++= currentSymbols
        currentSymbols = currentSymbols flatMap {
          _.toType.members collect {
            case symbol if symbol.isType => symbol.asType
          }
        }
        foundAdditionals = (currentSymbols -- symbols).nonEmpty
      }

      symbols
    }

    val owners = ownerChain(c.internal.enclosingOwner)

    val classNestingSet = mutable.Set(owners: _*)

    val classNestingList =
      mutable.ListBuffer(owners.reverse map { _.name.toTypeName }: _*)

    val shadowedNames = mutable.ListBuffer.empty[Set[Name]]

    def prependRootPackage(tree: Tree): Tree = tree match {
      case Ident(name) if tree.symbol.owner == c.mirror.RootClass =>
        Select(Ident(termNames.ROOTPKG), name)
      case Select(qualifier, name) =>
        Select(prependRootPackage(qualifier), name)
      case _ =>
        tree
    }

    def isTypeUnderExpansion(tpe: Type) = {
      val tpes = mutable.ListBuffer(tpe)
      val seen = mutable.Set.empty[Type]
      var underExpansion = false

      while (!underExpansion && tpes.nonEmpty)
        tpes.remove(0) exists { tpe =>
          if (!(seen contains tpe)) {
            if (!(definedTypeSymbols exists { tpe contains _ })) {
              seen += tpe
              val dealias = tpe.dealias
              val widen = tpe.widen
              if (tpe ne dealias)
                tpes += dealias
              if (tpe ne widen)
                tpes += widen
            }
            else
              underExpansion = true
          }

          underExpansion
        }

      underExpansion
    }

    def hasNonRepresentableType(trees: List[Tree]) = trees exists { tree =>
      val isWildcardType = tree match {
        case tree: TypeTree
            if tree.tpe != null &&
               tree.original == null &&
               tree.symbol.isType &&
               tree.symbol.asType.isExistential =>
          val str = tree.tpe.toString
          str == "_" || (str startsWith "_$")
        case _ =>
          false
      }

      isWildcardType ||
      tree.tpe != null && (tree.tpe exists isNonRepresentableType)
    }

    def removeAnnotationsToBeRemoved(tpe: Type): Type = tpe map {
      case AnnotatedType(annotations, underlying) =>
        val annots = annotations filterNot { annotation =>
          isAnnotationToBeRemoved(annotation.tree)
        }

        if (annots.nonEmpty)
          internal.annotatedType(annots, underlying)
        else
          underlying

      case tpe =>
        tpe
    }

    def isAnnotationToBeRemoved(tree: Tree): Boolean = tree match {
      case Apply(
          Select(New(tpt), termNames.CONSTRUCTOR),
          List(arg)) =>
        val condition = arg match {
          case Literal(Constant(value: String)) => value
          case NamedArg(_, Literal(Constant(value: String))) => value
          case _ => ""
        }

        def hasCondition = Seq(
          "early initializers",
          "initializers are deprecated",
          "trait parameters",
          "avoiding var",
          "val in traits") exists { condition contains _ }

        def isNowarn = nowarn exists { nowarn =>
          val isNowarn = tree.tpe match {
            case null if scala2134plus =>
              val name = tpt.toString
              name == "nowarn" || (name endsWith ".nowarn")
            case tpe if tpe != null && tpe <:< nowarn =>
              true
            case AnnotatedType(annotation :: _, _) =>
              annotation.tree.tpe != null && annotation.tree.tpe <:< nowarn
            case _ =>
              false
          }

          if (tree.tpe != null)
            internal.setType(tree, removeAnnotationsToBeRemoved(tree.tpe))

          isNowarn
        }

        hasCondition && isNowarn

      case _ =>
        false
    }

    object typesAndSymbolsFixer extends Transformer {
      def templateNames(impl: Template) = {
        (impl.parents flatMap { parent =>
          if (parent.symbol != null && parent.symbol.isType)
            parent.symbol.asType.toType.members collect {
              case symbol if symbol.isTerm => symbol.name
            }
          else
            Iterable.empty
        }) ++
        (impl.body collect {
          case defTree: DefTree if defTree.isTerm => defTree.name
        }) ++
        (if (impl.self.name != termNames.EMPTY) Some(impl.self.name) else None)
      }.toSet

      override def transformModifiers(mods: Modifiers) = {
        val annotations = mods.annotations filterNot isAnnotationToBeRemoved
        if (annotations.size != mods.annotations.size)
          Modifiers(mods.flags, mods.privateWithin, annotations)
        else
          mods
      }

      override def transform(tree: Tree) = tree match {
        case tree: TypeTree =>
          if (tree.original != null)
            transform(prependRootPackage(tree.original))
          else if (tree.tpe != null && isTypeUnderExpansion(tree.tpe)) {
            val typeTree = createTypeTree(tree.tpe, tree.pos, classNestingSet)

            val hasNonRepresentableType = typeTree exists {
              case _: TypeTree => true
              case _ => false
            }

            if (hasNonRepresentableType)
              TypeTree()
            else
              transform(typeTree)
          }
          else if (tree.tpe != null)
            TypeTree(removeAnnotationsToBeRemoved(tree.tpe))
          else
            TypeTree()

        case Annotated(annot, arg) if isAnnotationToBeRemoved(annot) =>
          if (arg.tpe != null)
            transform(internal.setType(
              arg,
              removeAnnotationsToBeRemoved(arg.tpe)))
          else
            transform(arg)

        case ClassDef(_, tpname, _, impl) =>
          classNestingList.prepend(tpname)
          if (tree.symbol.isClass)
            classNestingSet += tree.symbol

          shadowedNames.prepend(templateNames(impl))

          val classDef = super.transform(tree)

          shadowedNames.remove(0)

          if (tree.symbol.isClass)
            classNestingSet -= tree.symbol
          classNestingList.remove(0)

          classDef

        case ModuleDef(_, tname, impl) =>
          classNestingList.prepend(tname.toTypeName)
          if (tree.symbol.isModule)
            classNestingSet += tree.symbol.asModule.moduleClass

          shadowedNames.prepend(templateNames(impl))

          val moduleDef = super.transform(tree)

          shadowedNames.remove(0)

          if (tree.symbol.isModule)
            classNestingSet -= tree.symbol.asModule.moduleClass
          classNestingList.remove(0)

          moduleDef

        case DefDef(mods, termNames.CONSTRUCTOR, tparams, vparamss, _, rhs) =>
          shadowedNames.prepend((vparamss.flatten map { _.name }).toSet)

          val defDef = DefDef(
            transformModifiers(mods), termNames.CONSTRUCTOR,
            transformTypeDefs(tparams), transformValDefss(vparamss),
            TypeTree(), transform(rhs))

          shadowedNames.remove(0)

          defDef.withAttrs(
            tree.symbol,
            tree.tpe,
            if (tree.symbol.pos != NoPosition) tree.symbol.pos else tree.pos)

        case DefDef(mods, name, tparams, vparamss, tpt, EmptyTree) =>
          val typeSignature = tree.symbol.typeSignature
          val emptyTypeTree = tpt match {
            case tree: TypeTree => tree.tpe == null && tree.original == null
            case EmptyTree => true
            case _ => false
          }

          shadowedNames.prepend((vparamss.flatten map { _.name }).toSet + name)

          val defDef =
            if (emptyTypeTree && typeSignature != null && typeSignature != NoType)
              DefDef(
                transformModifiers(mods), name,
                transformTypeDefs(tparams), transformValDefss(vparamss),
                TypeTree(tree.symbol.typeSignature.finalResultType),
                EmptyTree).withAttrs(
                  tree.symbol, tree.tpe, tree.pos)
            else
              super.transform(tree)

          shadowedNames.remove(0)
          defDef

        case ValDef(mods, name, tpt, EmptyTree) =>
          val typeSignature = tree.symbol.typeSignature
          val emptyTypeTree = tpt match {
            case tree: TypeTree => tree.tpe == null && tree.original == null
            case EmptyTree => true
            case _ => false
          }

          shadowedNames.prepend(Set(name))

          val result =
            if (emptyTypeTree && typeSignature != null && typeSignature != NoType)
              ValDef(
                transformModifiers(mods), name,
                TypeTree(tree.symbol.typeSignature.finalResultType),
                EmptyTree).withAttrs(
                  tree.symbol, tree.tpe, tree.pos)
            else
              super.transform(tree)

          shadowedNames.remove(0)
          result

        case _: ValDef | _: DefDef | _: CaseDef | _: Function | _: Block =>
          val names = tree match {
            case ValDef(_, name, _, _) =>
              List(name)
            case DefDef(_, name, _, vparamss, _, _) =>
              name :: (vparamss.flatten map { _.name })
            case CaseDef(pat, _, _) =>
              pat collect { case Bind(name, _) => name }
            case Function(vparams, _) =>
              vparams map { _.name }
            case Block(stats, _) =>
              stats collect {
                case defTree: DefTree if defTree.isTerm => defTree.name
              }
            case _ =>
              List.empty
          }

          shadowedNames.prepend(names.toSet)

          val result = super.transform(tree)

          shadowedNames.remove(0)
          result

        case TypeApply(fun, targs) =>
          if (hasNonRepresentableType(targs))
            transform(fun)
          else
            super.transform(tree)

        case Apply(
              typeApply @ TypeApply(
                select @ Select(qual, TermName(name)),
                List(targ)),
              List())
            if name == "$" + "isInstanceOf" =>
          super.transform(
            TypeApply(
                Select(qual, TermName("isInstanceOf")).withAttrs(
                  NoSymbol, select.tpe, select.pos),
                List(targ)).withAttrs(
              NoSymbol, typeApply.tpe, typeApply.pos))

        case Apply(fun, args)
            if tree.symbol != null && tree.symbol.isMethod && {
              val paramLists = tree.symbol.asMethod.paramLists
              paramLists.size == 1 &&
                paramLists.head.nonEmpty &&
                paramLists.head.head.isImplicit
            } =>
          val hasNonRepresentableTypeArg = args exists {
            case Apply(TypeApply(_, targs), _) =>
              hasNonRepresentableType(targs)
            case TypeApply(_, targs) =>
              hasNonRepresentableType(targs)
            case _ =>
              false
          }

          if (hasNonRepresentableTypeArg)
            transform(fun)
          else
            super.transform(tree)

        case Select(This(_), termNames.CONSTRUCTOR) =>
          Ident(termNames.CONSTRUCTOR)

        case Select(qualifier @ This(qual), name) =>
          val fixedEnclosingSelfReference =
            if (!qualifier.symbol.isModuleClass &&
                !(shadowedNames exists { _ contains name }))
              classNestingList collectFirst {
                case outerName if outerName == qualifier.symbol.name =>
                  Ident(name)
              }
            else
              None

          fixedEnclosingSelfReference getOrElse {
            if (qual != typeNames.EMPTY &&
                qualifier.symbol.isModuleClass &&
                isAccessible(tree.symbol, classNestingSet) &&
                !isTypeUnderExpansion(qualifier.symbol.asType.toType) &&
                !(classNestingSet contains qualifier.symbol))
              Select(expandSymbol(qualifier.symbol, qualifier.pos), name)
            else
              super.transform(tree)
          }

        case Select(_, _) | Ident(_) | This(_)
            if tree.tpe != null && isTypeUnderExpansion(tree.tpe) =>
          internal.setSymbol(tree, NoSymbol)
          super.transform(tree)

        case _ =>
          super.transform(tree)
      }
    }

    typesAndSymbolsFixer transform tree
  }


  private def fixCaseClasses(tree: Tree): Tree = {
    case object CaseClassMarker

    val symbols = mutable.Set.empty[Symbol]

    val scala213 = c.classPath exists { url =>
      val file = url.getFile
      val index = file indexOf "/scala-library-"
      val start = index + 15
      val end = index + 19
      index != -1 && end <= file.length && file.substring(start, end) == "2.13"
    }

    val syntheticMethodNames =
      if (scala213)
        Set("apply", "unapply")
      else
        Set("apply", "canEqual", "copy", "equals",
            "hashCode", "productArity", "productElement", "productIterator",
            "productPrefix", "readResolve", "toString", "unapply")

    def isSyntheticMethodName(name: TermName) =
      (syntheticMethodNames contains name.toString) ||
      ((name.toString startsWith "copy$") && !(name.toString endsWith "$macro"))

    object caseClassFixer extends Transformer {
      def resetCaseImplBody(body: List[Tree]) =
        body filterNot {
          case DefDef(mods, name, _, _, _, _) =>
            (mods hasFlag SYNTHETIC) && isSyntheticMethodName(name)
          case _ => false
        }

      def resetCaseImplDef(implDef: ImplDef) = implDef match {
        case ModuleDef(mods, name, Template(parents, self, body)) =>
          val moduleDef = ModuleDef(mods, name,
            Template(parents, self, resetCaseImplBody(body)))

          internal.updateAttachment(moduleDef, CaseClassMarker)
          moduleDef.withAttrs(implDef.symbol, implDef.tpe, implDef.pos)

        case ClassDef(mods, tpname, tparams, Template(parents, self, body)) =>
          val classDef = ClassDef(mods, tpname, tparams,
            Template(parents, self, resetCaseImplBody(body)))

          internal.updateAttachment(classDef, CaseClassMarker)
          classDef.withAttrs(implDef.symbol, implDef.tpe, implDef.pos)

        case _ =>
          implDef
      }

      def fixCaseClasses(trees: List[Tree]) = {
        val names = (trees collect {
          case ClassDef(mods, tpname, _, _) if mods hasFlag CASE =>
            tpname.toTermName
        }).toSet

        val companions = (trees collect {
          case ModuleDef(_, name, _) if names contains name =>
            name.toTypeName
        }).toSet

        val havingCompanions = (trees collect {
          case classDef @ ClassDef(_, tpname, _, _) if companions contains tpname =>
            tpname -> classDef
        }).toMap

        val reorderedTrees =
          trees flatMap {
            case tree @ ClassDef(_, tpname, _, _) =>
              havingCompanions get tpname map { _ =>
                Seq.empty
              } getOrElse Seq(tree)
            case tree @ ModuleDef(_, name, _) =>
              havingCompanions get name.toTypeName map { classDef =>
                Seq(tree, classDef)
              } getOrElse Seq(tree)
            case tree =>
              Seq(tree)
          }

        symbols ++= (reorderedTrees collect {
          case tree @ ClassDef(mods, _, _, _)
              if tree.symbol != NoSymbol &&
                 (mods hasFlag CASE) =>
            Seq(tree.symbol)
          case tree @ ModuleDef(mods, name, _)
              if tree.symbol != NoSymbol &&
                 ((mods hasFlag CASE) || (names contains name)) =>
            Seq(tree.symbol, tree.symbol.asModule.moduleClass)
        }).flatten

        reorderedTrees map {
          case tree @ ModuleDef(mods, name, _)
              if (mods hasFlag CASE) || (names contains name) =>
            resetCaseImplDef(tree)
          case tree @ ClassDef(mods, _, _, _)
              if mods hasFlag CASE =>
            resetCaseImplDef(tree)
          case tree =>
            tree
        }
      }

      override def transform(tree: Tree) = tree match {
        case Template(parents, self, body) =>
          super.transform(Template(parents, self, fixCaseClasses(body)))
        case Block(stats, expr) =>
          val fixedExpr :: fixedStats = fixCaseClasses(expr :: stats): @unchecked
          super.transform(Block(fixedStats, fixedExpr))
        case _ =>
          super.transform(tree)
      }
    }

    object caseClassReferenceFixer extends Transformer {
      val owners = mutable.Set.empty[Symbol]

      def symbolsContains(symbol: Symbol): Boolean =
        symbol != null && symbol != NoSymbol &&
        ((symbols contains symbol) || symbolsContains(symbol.owner))

      override def transform(tree: Tree) = {
        val owner = tree match {
          case ClassDef(_, _, _, _) if tree.symbol.isClass =>
            Some(tree.symbol)
          case ModuleDef(_, _, _) if tree.symbol.isModule =>
            Some(tree.symbol.asModule.moduleClass)
          case _ =>
            None
        }

        owner foreach { owners += _ }

        val result = tree match {
          case _ if internal.attachments(tree).contains[CaseClassMarker.type] =>
            internal.removeAttachment[CaseClassMarker.type](tree)
            tree
          case tree: TypeTree if symbolsContains(tree.symbol) =>
            createTypeTree(tree.tpe, tree.pos, owners)
          case _ if symbolsContains(tree.symbol) =>
            super.transform(internal.setSymbol(tree, NoSymbol))
          case _ =>
            super.transform(tree)
        }

        owner foreach { owners -= _ }

        result
      }
    }

    caseClassReferenceFixer transform (caseClassFixer transform tree)
  }


  private def selfReferenceFixer = new Transformer {
    type ImplEnv = (Symbol, TypeName, Set[Name], Option[ValDef])
    type BlockEnv = Set[Name]

    val ownerStack: List[Either[ImplEnv, BlockEnv]] =
      (ownerChain(c.internal.enclosingOwner)
        filter { symbol =>
          (symbol.isClass || symbol.isModuleClass) &&
          !symbol.isPackage &&
          !symbol.isPackageClass
        }
        map { symbol =>
          val typeSymbol = symbol.asType
          val names = (typeSymbol.toType.members map { _.name }).toSet[Name]
          Left((symbol, typeSymbol.name, names, None))
        })

    val stack = mutable.ListBuffer(ownerStack: _*)

    override def transform(tree: Tree) = tree match {
      case tree: TypeTree =>
        if (tree.original != null)
          internal.setOriginal(tree, transform(tree.original))
        tree

      case implDef: ImplDef =>
        val symbol =
          if (implDef.symbol.isModule)
            implDef.symbol.asModule.moduleClass
          else
            implDef.symbol

        val declNames =
          (implDef.impl.parents flatMap { parent =>
            if (parent.symbol != null && parent.symbol.isType)
              parent.symbol.asType.toType.members map { _.name }
            else
              Iterable.empty
          }) ++
          (implDef.impl.body collect {
            case defTree: DefTree => defTree.name
          })

        val memberNames =
          if (symbol.isType)
            (symbol.asType.toType.members map { _.name }).toSet[Name]
          else
            Set.empty

        val names = declNames.toSet ++ memberNames

        val self =
          if (implDef.impl.self.name != termNames.EMPTY &&
              implDef.impl.self.name != termNames.WILDCARD)
            Some(implDef.impl.self)
          else
            None

        stack.prepend(Left((symbol, implDef.name.toTypeName, names, self)))

        val result = super.transform(implDef)

        stack.remove(0)
        result

      case _: ValDef | _: DefDef | _: CaseDef | _: Function | _: Block =>
        val names = tree match {
          case ValDef(_, name, _, _) =>
            List(name)
          case DefDef(_, name, _, vparamss, _, _) =>
            name :: (vparamss.flatten map { _.name })
          case CaseDef(pat, _, _) =>
            pat collect { case Bind(name, _) => name }
          case Function(vparams, _) =>
            vparams map { _.name }
          case Block(stats, _) =>
            stats collect { case defTree: DefTree => defTree.name }
          case _ =>
            List.empty
        }

        stack.prepend(Right(names.toSet))

        val result = super.transform(tree)

        stack.remove(0)
        result

      case Select(thisTree @ This(thisName), selectedName) =>
        trait Modification
        case class Self(self: ValDef) extends Modification
        case object Unqualified extends Modification
        case object Stable extends Modification
        case object Unmodified extends Modification

        val (modification, _, _) =
          stack.foldLeft[(Modification, Set[Name], Boolean)](
              (Stable, Set.empty, true)) {
            case ((modification, shadowed, innerImpl),
                  Left((symbol, name, names, self))) =>
              val updatedShadowed = shadowed ++ names

              val updatedModification =
                if (symbol != NoSymbol &&
                    symbol == thisTree.symbol &&
                    modification == Unmodified) {
                  if ((updatedShadowed contains selectedName) ||
                      tree.symbol == NoSymbol)
                    (self
                      filterNot { updatedShadowed contains _.name }
                      map Self
                      getOrElse Unmodified)
                  else
                    Unqualified
                }
                else if ((name == thisName ||
                         (innerImpl && thisName == typeNames.EMPTY)) &&
                          modification == Stable) {
                  if ((updatedShadowed contains selectedName) ||
                      tree.symbol == NoSymbol)
                    Unmodified
                  else
                    Unqualified
                }
                else
                  modification

              (self
                map { self =>
                  (updatedModification, updatedShadowed + self.name, false)
                }
                getOrElse {
                  (updatedModification, updatedShadowed, false)
                })

            case ((modification, shadowed, innerImpl), Right(names)) =>
              (modification, shadowed ++ names, innerImpl)
          }

        val result = modification match {
          case Self(self) =>
            val qualifier = Ident(self.name).withAttrs(
              self.symbol,
              thisTree.tpe,
              if (thisTree.pos != NoPosition) thisTree.pos else tree.pos)
            Some(Select(qualifier, selectedName))
          case Unqualified =>
            Some(Ident(selectedName))
          case Stable if thisTree.symbol != NoSymbol =>
            val qualifier = expandSymbol(thisTree.symbol, thisTree.pos)
            Some(Select(qualifier, selectedName))
          case _ =>
            None
        }

        result map {
          _.withAttrs(
            tree.symbol,
            tree.tpe,
            if (tree.pos != NoPosition) tree.pos else thisTree.pos)
        } getOrElse tree

      case _ =>
        super.transform(tree)
    }
  }

  private implicit class TreeOps(tree: Tree) {
    def withAttrs(symbol: Symbol, tpe: Type, pos: Position) =
      internal.setPos(
        internal.setType(
          internal.setSymbol(tree, symbol), tpe), pos)
    def equalsTypedStructure(other: Tree) =
      (tree equalsStructure other) &&
      ((tree collect { case tree: TypeTree => tree }).zipAll
       (other collect { case tree: TypeTree => tree }, null, null)
       forall { case (tree, other) =>
         tree != null && other != null &&
          (tree.tpe != null && tree.tpe =:= other.tpe || other.tpe == null)
       })
  }

  private implicit class TermOps(term: TermSymbol) {
    def getterOrNoSymbol =
      try term.getter
      catch { case _: reflect.internal.Symbols#CyclicReference => NoSymbol }
    def setterOrNoSymbol =
      try term.setter
      catch { case _: reflect.internal.Symbols#CyclicReference => NoSymbol }
  }

  private def fixTypecheck(tree: Tree): Tree = {
    val definedSymbols = (tree collect {
      case tree: DefTree if tree.symbol != null && tree.symbol != NoSymbol =>
        tree.symbol
    }).toSet

    val rhss = (tree collect {
      case valDef @ ValDef(_, _, _, _) if valDef.symbol.isTerm =>
        val term = valDef.symbol.asTerm
        val getter = term.getterOrNoSymbol
        val setter = term.setterOrNoSymbol

        val getterDef =
          if (getter != NoSymbol && getter != term)
            List(getter -> valDef)
          else
            List.empty

        val setterDef =
          if (setter != NoSymbol && setter != term)
            List(setter -> valDef)
          else
            List.empty

        getterDef ++ setterDef
    }).flatten.toMap

    def defaultArgDef(defDef: DefDef): Boolean = {
      val nameString = defDef.name.toString
      val symbol = defDef.symbol.owner.owner

      val isDefaultArg =
        (nameString contains "$default$") &&
        ((nameString endsWith "$macro") ||
         (defDef.mods hasFlag (SYNTHETIC | DEFAULTPARAM)))

      val isConstructorInsideExpression =
        (nameString startsWith termNames.CONSTRUCTOR.encodedName.toString) &&
        !symbol.isClass && !symbol.isModule && !symbol.isModuleClass

      symbol != NoSymbol && isDefaultArg && !isConstructorInsideExpression
    }

    val definedDefaultArgs = tree collect {
      case defDef @ DefDef(_, _, _, _, _, _) if defaultArgDef(defDef) =>
        defDef.symbol
    }

    val accessedDefaultArgs = (tree collect {
      case select @ Select(_, _) if definedDefaultArgs contains select.symbol =>
        select.symbol
    }).toSet

    def processDefaultArgs(stats: List[Tree]) = {
      val macroDefaultArgs = mutable.ListBuffer.empty[DefDef]
      val macroDefaultArgsPending = mutable.Map.empty[String, Option[DefDef]]

      val processedStats = stats map {
        case defDef @ DefDef(_, name, tparams, vparamss, tpt, rhs)
            if defaultArgDef(defDef) =>
          val nameString = name.toString
          val macroDefDef = DefDef(
            Modifiers(SYNTHETIC),
            TermName(s"$nameString$$macro"),
            tparams, vparamss, tpt, rhs)

          if (nameString endsWith "$macro") {
            if (accessedDefaultArgs contains defDef.symbol)
              macroDefaultArgsPending.getOrElseUpdate(
                nameString.substring(0, nameString.length - 6),
                None) foreach { macroDefaultArgs += _ }
            EmptyTree
          }
          else {
            if ((accessedDefaultArgs contains defDef.symbol) ||
                (macroDefaultArgsPending contains nameString))
              macroDefaultArgs += macroDefDef
            else
              macroDefaultArgsPending += nameString -> Some(macroDefDef)
            defDef
          }

        case stat =>
          stat
      }

      processedStats ++ macroDefaultArgs
    }

    def applyMetaProperties(from: Tree, to: Tree) = {
      if (from.symbol != null)
        internal.setSymbol(to, from.symbol)
      internal.setType(internal.setPos(to, from.pos), from.tpe)
    }

    object typecheckFixer extends Transformer {
      def fixModifiers(mods: Modifiers, symbol: Symbol,
          annotations: List[Annotation] = List.empty): Modifiers = {
        val flags = cleanModifiers(mods, removeFlags = INTERFACE).flags

        val allAnnotations =
          (mods.annotations ++
           (symbol.annotations map { _.tree }) ++
           (annotations map { _.tree }))
            .foldLeft(List.empty[Tree]) { (all, tree) =>
              val transformedTree = transform(tree)
              if (all exists { _ equalsTypedStructure transformedTree })
                all
              else
                transformedTree :: all
            }.reverse

        internal.setFlag(symbol, flags)

        Modifiers(flags, mods.privateWithin, allAnnotations)
      }

      override def transform(tree: Tree) = tree match {
        case tree: TypeTree =>
          if (tree.original != null)
            internal.setOriginal(tree, transform(tree.original))
          tree

        // workaround for default arguments
        case Template(parents, self, body) =>
          super.transform(
            applyMetaProperties(
              tree, Template(parents, self, processDefaultArgs(body))))

        case tree @ Block(stats, expr) =>
          val defaultArgStats = stats map {
            case tree @ ValDef(mods, name, tpt, rhs)
                if (mods hasFlag ARTIFACT) &&
                   !(name.toString startsWith "default$") =>
              val defaultName = TermName(s"default$$$name")
              val valDef = ValDef(mods, defaultName, tpt, rhs).withAttrs(
                tree.symbol, tree.tpe, tree.pos)
              Some(valDef -> (name -> tree.symbol -> defaultName))
            case _ =>
              None
          }

          val renamedDefaultArgs =
            if (!(defaultArgStats contains None)) {
              val names = (defaultArgStats collect {
                case Some((_, mapping)) => mapping
              }).toMap

              object transformer extends Transformer {
                override def transform(tree: Tree) = tree match {
                  case tree @ Ident(name: TermName) =>
                    names get (name -> tree.symbol) map { name =>
                      Ident(name).withAttrs(tree.symbol, tree.tpe, tree.pos)
                    } getOrElse tree
                  case tree =>
                    super.transform(tree)
                }
              }

              Block(
                defaultArgStats collect { case Some((stat, _)) =>
                  transformer transform stat
                },
                transformer transform expr)
            }
            else
              tree

          val block = processDefaultArgs(
            renamedDefaultArgs.expr :: renamedDefaultArgs.stats)

          super.transform(
            applyMetaProperties(tree, Block(block.tail, block.head)))

        case Select(qualifier, name)
            if accessedDefaultArgs contains tree.symbol =>
          val macroName =
            if (name.toString endsWith "$macro") name
            else TermName(s"${name.toString}$$macro")
          super.transform(Select(qualifier, macroName))

        case Ident(name) if tree.symbol.isTerm && name == tree.symbol.name =>
          expandSymbol(tree.symbol, tree.pos) match {
            case Ident(_) =>
              tree
            case expandedTree
                if expandedTree exists { definedSymbols contains _.symbol } =>
              tree
            case expandedTree =>
              expandedTree
          }

        // fix renamed imports
        case Select(qual, _) if tree.symbol != NoSymbol =>
          super.transform(
            applyMetaProperties(tree, Select(qual, tree.symbol.name)))

        // fix extractors
        case UnApply(
            Apply(fun, List(Ident(TermName("")))), args) =>
          fun collect {
            case Select(fun, TermName("unapply" | "unapplySeq")) => fun
          } match {
            case Seq(fun) =>
              transform(applyMetaProperties(fun, Apply(fun, args)))
            case _ =>
              super.transform(tree)
          }

        // fix vars, vals and lazy vals
        case ValDef(_, _, TypeTree(), rhs)
            if tree.symbol.isTerm && {
              val term = tree.symbol.asTerm
              (term.isLazy && term.isImplementationArtifact && rhs.isEmpty) ||
              (term.isPrivateThis &&
                (rhss contains term.asTerm.getterOrNoSymbol))
            } =>
          EmptyTree
        case DefDef(_, _, _, _, _, _)
            if tree.symbol.isTerm && tree.symbol.asTerm.isSetter =>
          EmptyTree

        // fix vars and vals
        case defDef @ DefDef(mods, name, _, _, _, _)
            if tree.symbol.isTerm && {
              val term = tree.symbol.asTerm
              !term.isLazy && term.isGetter
            } =>
          val term = tree.symbol.asTerm
          val valDef = rhss.getOrElse(tree.symbol, defDef)
          val valAnnotations = (rhss get tree.symbol).toList flatMap {
            _.symbol.annotations }
          val newMods = Modifiers(
            mods.flags
              | (if (valDef.mods hasFlag PRESUPER) PRESUPER else NoFlags)
              | (if (!valDef.symbol.asTerm.isStable) MUTABLE else NoFlags)
              | (if (term.isPrivate) PRIVATE else NoFlags)
              | (if (term.isProtected) PROTECTED else NoFlags)
              | (if (term.isPrivateThis ||
                     term.isProtectedThis) LOCAL else NoFlags),
            if (mods.privateWithin != typeNames.EMPTY)
              mods.privateWithin
            else if (defDef.symbol.privateWithin != NoSymbol)
              defDef.symbol.privateWithin.name
            else
              typeNames.EMPTY,
            mods.annotations)
          val newValDef = ValDef(
            fixModifiers(newMods, defDef.symbol, valAnnotations),
            name, transform(valDef.tpt), transform(valDef.rhs))
          newValDef.withAttrs(defDef.symbol, valDef.tpe, valDef.pos)

        // fix lazy vals
        case valOrDefDef: ValOrDefDef
            if tree.symbol.isTerm && {
              val term = tree.symbol.asTerm
              term.isLazy && term.isGetter
            } =>
          val mods = valOrDefDef.mods
          val (lazyValAnnotations, assignment) = valOrDefDef.rhs collect {
            case Assign(lhs, rhs) => lhs.symbol.annotations -> rhs
          } match {
            case (annotations, rhs) :: _ => annotations -> rhs
            case _ => List.empty -> valOrDefDef.rhs
          }
          val valDef = rhss get tree.symbol
          val typeTree = valDef map { _.tpt } getOrElse valOrDefDef.tpt
          val valAnnotations = lazyValAnnotations ++ 
            (valDef.toList flatMap { _.symbol.annotations })
          val newMods = Modifiers(
            mods.flags,
            if (mods.privateWithin != typeNames.EMPTY)
              mods.privateWithin
            else if (valOrDefDef.symbol.privateWithin != NoSymbol)
              valOrDefDef.symbol.privateWithin.name
            else
              typeNames.EMPTY,
            mods.annotations)
          val newValDef = ValDef(
            fixModifiers(newMods, valOrDefDef.symbol, valAnnotations),
            valOrDefDef.name, transform(typeTree), transform(assignment))
          newValDef.withAttrs(
            valOrDefDef.symbol,
            valDef map { _.tpe } getOrElse valOrDefDef.tpe,
            valDef map { _.pos } getOrElse valOrDefDef.pos
          )

        // fix vals
        case valDef @ ValDef(mods, name, tpt, rhs)
            if tree.symbol.isTerm =>
          val newValDef = ValDef(
            fixModifiers(mods, valDef.symbol), name,
            transform(tpt),
            transform(rhs))
          newValDef.withAttrs(valDef.symbol, valDef.tpe, valDef.pos)

        // fix defs
        case defDef @ DefDef(mods, name, tparams, vparamss, tpt, rhs)
            if tree.symbol.isTerm =>
          val newDefDef = DefDef(
            fixModifiers(mods, defDef.symbol), name,
            transformTypeDefs(tparams),
            transformValDefss(vparamss),
            transform(tpt),
            transform(rhs))
          internal.setType(newDefDef, defDef.tpe)
          internal.setPos(newDefDef, defDef.pos)
          if (tree.symbol.name != TermName("$init$"))
            internal.setSymbol(newDefDef, defDef.symbol)
          else
            newDefDef

        // fix classes
        case classDef @ ClassDef(mods, name, tparams, impl)
            if tree.symbol.isClass =>
          impl.body exists {
            case tree if tree.symbol != null && tree.symbol.isConstructor =>
              internal.setPos(tree, classDef.pos)
              true
            case _ =>
              false
          }

          val newClassDef = ClassDef(
            fixModifiers(mods, classDef.symbol), name,
            transformTypeDefs(tparams),
            transformTemplate(impl))
          newClassDef.withAttrs(classDef.symbol, classDef.tpe, classDef.pos)

        // fix objects
        case moduleDef @ ModuleDef(mods, name, impl)
            if tree.symbol.isModule =>
          val newModuleDef = ModuleDef(
            fixModifiers(mods, moduleDef.symbol), name,
            transformTemplate(impl))
          newModuleDef.withAttrs(moduleDef.symbol, moduleDef.tpe, moduleDef.pos)

        // fix type definitions
        case typeDef @ TypeDef(mods, name, tparams, rhs)
            if tree.symbol.isType =>
          val newTypeDef = TypeDef(
            fixModifiers(mods, typeDef.symbol), name,
            transformTypeDefs(tparams),
            transform(rhs))
          newTypeDef.withAttrs(typeDef.symbol, typeDef.tpe, typeDef.pos)

        case _ =>
          super.transform(tree)
      }
    }

    tree match {
      case _: ImplDef if tree.pos == NoPosition =>
        internal.setPos(tree, c.enclosingPosition)
      case _ =>
    }

    typecheckFixer transform tree
  }

  private class UndefinedOffsetPosition(
    override val source: util.SourceFile,
    override val point: Int)
      extends util.Position {
    override val start = point
    override val end = point
  }

  private class UndefinedPosition extends util.Position {
    override def source = util.NoSourceFile
    override def point = fail("start")
    override def start = fail("point")
    override def end = fail("end")
  }

  private def maskUnusedImports(tree: Tree): Tree = {
    def maskPosition(tree: Tree) = {
      val pos =
        if (tree.pos != NoPosition)
          new UndefinedOffsetPosition(tree.pos.source, tree.pos.point)
        else
          new UndefinedPosition

      pos match {
        case pos: Position => internal.setPos(tree, pos)
        case _ => tree
      }
    }

    def isPositionMasked(tree: Tree) = tree.pos match {
      case (_: UndefinedOffsetPosition) | (_: UndefinedPosition) => true
      case _ => false
    }

    object importMasker extends Traverser {
      type Imports = List[List[(Tree, Set[Symbol])]]

      var imports: Imports = List(List.empty)
      var hasImports = false

      def maskImports(symbol: Symbol) = {
        def maskImports(imports: Imports): Imports =
          imports match {
            case Nil =>
              Nil

            case Nil :: scopes =>
              Nil :: maskImports(scopes)

            case ((info @ (tree, symbols)) :: scope) :: scopes =>
              if (symbols contains symbol) {
                hasImports = hasImports || (scope :: scopes exists { _.nonEmpty })
                maskPosition(tree)
                scope :: scopes
              }
              else {
                hasImports = true
                val imports = maskImports(scope :: scopes)
                (info :: imports.head) :: imports.tail
              }
          }

        if (hasImports) {
          hasImports = false
          imports = maskImports(imports)
        }
      }

      override def traverse(tree: Tree) = tree match {
        case tree: TypeTree if tree.original != null =>
          traverse(tree.original)

        case Import(expr, selectors)
            if expr.symbol != NoSymbol && !isPositionMasked(tree) =>
          if (selectors forall { _.namePos != -1 }) {
            val (importedNames, hiddenNames, isWildcard) =
              selectors.foldLeft[(Set[String], Set[String], Boolean)](
                  (Set.empty, Set.empty, false)) {
                case ((importedNames, hiddenNames, isWildcard), selector) =>
                  if (selector.name != termNames.WILDCARD) {
                    if (selector.rename != termNames.WILDCARD)
                      (importedNames + selector.name.toString,
                       hiddenNames,
                       isWildcard)
                    else
                      (importedNames,
                       hiddenNames + selector.name.toString,
                       isWildcard)
                  }
                  else
                    (importedNames, hiddenNames, true)
              }

            val members =
              expr.symbol.info.members filter { symbol =>
                symbol.isPublic && !symbol.isConstructor
              }

            val imported =
              if (!isWildcard)
                members filter { importedNames contains _.name.toString }
              else
                members

            val unhidden =
              imported filterNot { hiddenNames contains _.name.toString }

            hasImports = true
            imports = (tree -> unhidden.toSet :: imports.head) :: imports.tail
          }
          else
            maskPosition(tree)

        case Select(_, _) if hasImports =>
          def traversePath(tree: Tree, found: Boolean): Unit = tree match {
            case Select(qualifier, name) =>
              if (!found &&
                  tree.pos != NoPosition &&
                  qualifier.pos != NoPosition &&
                  tree.pos.point == qualifier.pos.point) {
                if (tree.symbol != NoSymbol)
                  maskImports(tree.symbol)

                traversePath(
                  qualifier,
                  found = name.toString != "apply" && name.toString != "update")
              }
              else
                traversePath(qualifier, found)

            case _ =>
              traverse(tree)
          }

          traversePath(tree, found = false)

        case Block(_, _) | Template(_, _, _) =>
          imports ::= List.empty
          super.traverse(tree)
          imports = imports.tail

        case _ =>
          super.traverse(tree)
      }
    }

    importMasker traverse tree

    tree
  }

  private def fixUntypecheck(tree: Tree): Tree = {
    def replaceSuperCall(call: Tree, replacement: Tree): Option[Tree] =
      call match {
        case Apply(fun, args) =>
          replaceSuperCall(fun, replacement) map { Apply(_, args) }
        case TypeApply(fun, args) =>
          replaceSuperCall(fun, replacement) map { TypeApply(_, args) }
        case Select(Super(_, _), termNames.CONSTRUCTOR) =>
          Some(replacement)
        case _ =>
          None
      }

    object untypecheckFixer extends Transformer {
      override def transform(tree: Tree) = tree match {
        case tree: TypeTree =>
          if (tree.original != null)
            internal.setOriginal(tree, transform(tree.original))
          tree

        case Apply(fun, _) if fun.symbol != null && fun.symbol.isModule =>
          internal.setSymbol(fun, NoSymbol)
          super.transform(tree)

        case Typed(expr, tpt) =>
          tpt match {
            case Function(List(), EmptyTree) =>
              super.transform(tree)

            case Annotated(_, arg)
                if expr != null && arg != null &&
                   (expr equalsTypedStructure arg) =>
              super.transform(tpt)

            case tpt: TypeTree
                if (tpt.original match {
                  case Annotated(_, _) => true
                  case _ => false
                }) =>
              super.transform(tpt.original)

            case tpt if !tpt.isType =>
              super.transform(expr)

            case _ =>
              super.transform(tree)
          }

        case Ident(_) =>
          if (tree.symbol.isTerm && tree.symbol.asTerm.isLazy)
            internal.setSymbol(tree, NoSymbol)
          else
            tree

        case DefDef(mods, name, _, _, _, _)
            if (mods hasFlag (SYNTHETIC | DEFAULTPARAM)) &&
               (name.toString contains "$default$") &&
               !(name.toString endsWith "$macro") =>
          EmptyTree

        case ClassDef(mods, tpname, tparams, Template(parents, self, body))
            if (tpname.toString startsWith "$anon") && parents.nonEmpty =>
          val fixedPreSuperBody = body map {
            case tree @ ValDef(mods, name, tpt, rhs) if mods hasFlag PRESUPER =>
              tpt match {
                case _: TypeTree =>
                  tree
                case tpt =>
                  ValDef(mods, name, internal.setOriginal(TypeTree(), tpt), rhs)
              }
            case tree =>
              tree
          }

          if (body != fixedPreSuperBody) {
            val (fixedBody, fixedParentOptions) = (fixedPreSuperBody map {
              case tree @ DefDef(
                  mods, termNames.CONSTRUCTOR, tparams, vparamss, tpt, rhs) =>
                rhs match {
                  case Block(stats, expr) =>
                    val (fixedStats, fixedParentOptions) = (stats map { stat =>
                      val fixedParent = replaceSuperCall(stat, parents.head)
                      if (fixedParent.nonEmpty)
                        pendingSuperCall -> fixedParent
                      else
                        stat -> fixedParent
                    }).unzip

                    val fixedParent =
                      fixedParentOptions collect { case Some(parent) => parent }

                    if (fixedParent.size == 1)
                      DefDef(
                        mods, termNames.CONSTRUCTOR, tparams, vparamss, tpt,
                        Block(fixedStats, expr)) -> fixedParent.headOption
                    else
                      tree -> None

                  case tree =>
                    tree -> None
                }

              case tree =>
                tree -> None
            }).unzip

            val fixedParent =
              fixedParentOptions collect { case Some(parent) => parent }

            if (fixedParent.size == 1)
              super.transform(
                ClassDef(mods, tpname, tparams,
                  Template(fixedParent.head :: parents.tail, self, fixedBody)))
            else
              super.transform(tree)
          }
          else
            super.transform(tree)

        case ModuleDef(_, _, Template(_, _, _)) =>
          super.transform(tree) match {
            case ModuleDef(mods, _, Template(parents, `noSelfType`,
                  List() |
                  List(DefDef(_, termNames.CONSTRUCTOR,
                    List(), List(List()), _, _))))
                if (mods hasFlag SYNTHETIC) &&
                   (parents.isEmpty ||
                     (parents.size == 1 &&
                      parents.head.tpe != null &&
                      parents.head.tpe =:= definitions.AnyRefTpe)) =>
              EmptyTree

            case tree =>
              tree
          }

        case _ =>
          super.transform(tree)
      }
    }

    untypecheckFixer transform tree
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy