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

scala.quasiquotes.ReificationSupport.scala Maven / Gradle / Ivy

package scala.quasiquotes

import scala.reflect.internal.Flags._
import scala.reflect.internal.util._
import scala.reflect.internal.SymbolTable

abstract class ReificationSupport extends SymbolTableCompat { self =>
  val global: SymbolTable

  import global._
  import symbolTable._
  import definitions._

  def selectType(owner: Symbol, name: String): TypeSymbol =
    select(owner, newTypeName(name)).asType

  def selectTerm(owner: Symbol, name: String): TermSymbol = {
    val result = select(owner, newTermName(name)).asTerm
    if (result.isOverloaded) result.suchThat(!_.isMethod).asTerm
    else result
  }

  protected def select(owner: Symbol, name: Name): Symbol = {
    val result = owner.info decl name
    if (result ne NoSymbol) result
    else {
      val mirror = mirrorThatLoaded(owner)
      val m_missingHook = mirror.getClass().getMethod("missingHook")
      val result = m_missingHook.invoke(mirror, owner, name).asInstanceOf[Symbol]
      result orElse {
        throw new ScalaReflectionException("%s %s in %s not found".format(if (name.isTermName) "term" else "type", name, owner.fullName))
      }
    }
  }

  def selectOverloadedMethod(owner: Symbol, name: String, index: Int): MethodSymbol = {
    val result = owner.info.decl(newTermName(name)).alternatives(index)
    if (result ne NoSymbol) result.asMethod
    else throw new ScalaReflectionException("overloaded method %s #%d in %s not found".format(name, index, owner.fullName))
  }

  def newFreeTerm(name: String, value: => Any, flags: Long = 0L, origin: String = null): FreeTermSymbol =
    newFreeTermSymbol(newTermName(name), value, flags, origin)

  def newFreeType(name: String, flags: Long = 0L, origin: String = null): FreeTypeSymbol =
    newFreeTypeSymbol(newTypeName(name), flags, origin)

  def newNestedSymbol(owner: Symbol, name: Name, pos: Position, flags: Long, isClass: Boolean): Symbol =
    owner.newNestedSymbol(name, pos, flags, isClass)

  def newScopeWith(elems: Symbol*): Scope =
    global.newScopeWith(elems: _*)

  def setAnnotations[S <: Symbol](sym: S, annots: List[AnnotationInfo]): S =
    sym.setAnnotations(annots)

  def setInfo[S <: Symbol](sym: S, tpe: Type): S =
    sym.setInfo(tpe)

  def mkThis(sym: Symbol): Tree = global.This(sym)

  def mkSelect(qualifier: Tree, sym: Symbol): Select = global.Select(qualifier, sym)

  def mkIdent(sym: Symbol): Ident = global.Ident(sym)

  def mkTypeTree(tp: Type): TypeTree = global.TypeTree(tp)

  def ThisType(sym: Symbol): Type = global.ThisType(sym)

  def SingleType(pre: Type, sym: Symbol): Type = global.SingleType(pre, sym)

  def SuperType(thistpe: Type, supertpe: Type): Type = global.SuperType(thistpe, supertpe)

  def ConstantType(value: Constant): ConstantType = global.ConstantType(value)

  def TypeRef(pre: Type, sym: Symbol, args: List[Type]): Type = global.TypeRef(pre, sym, args)

  def RefinedType(parents: List[Type], decls: Scope, typeSymbol: Symbol): RefinedType = global.RefinedType(parents, decls, typeSymbol)

  def ClassInfoType(parents: List[Type], decls: Scope, typeSymbol: Symbol): ClassInfoType = global.ClassInfoType(parents, decls, typeSymbol)

  def MethodType(params: List[Symbol], resultType: Type): MethodType = global.MethodType(params, resultType)

  def NullaryMethodType(resultType: Type): NullaryMethodType = global.NullaryMethodType(resultType)

  def PolyType(typeParams: List[Symbol], resultType: Type): PolyType = global.PolyType(typeParams, resultType)

  def ExistentialType(quantified: List[Symbol], underlying: Type): ExistentialType = global.ExistentialType(quantified, underlying)

  def AnnotatedType(annotations: List[Annotation], underlying: Type, selfSym: Symbol): AnnotatedType = global.AnnotatedType(annotations, underlying, selfSym)

  def TypeBounds(lo: Type, hi: Type): TypeBounds = global.TypeBounds(lo, hi)

  def BoundedWildcardType(bounds: TypeBounds): BoundedWildcardType = global.BoundedWildcardType(bounds)

  def thisPrefix(sym: Symbol): Type = sym.thisPrefix

  def setType[T <: Tree](tree: T, tpe: Type): T = { tree.setType(tpe); tree }

  def setSymbol[T <: Tree](tree: T, sym: Symbol): T = { tree.setSymbol(sym); tree }

  def toStats(tree: Tree): List[Tree] = tree match {
    case EmptyTree             => Nil
    case SyntacticBlock(stats) => stats
    case _                     => throw new IllegalArgumentException(s"can't flatten $tree")
  }

  def mkAnnotation(tree: Tree): Tree = tree match {
    case SyntacticNew(Nil, SyntacticApplied(SyntacticAppliedType(_, _), _) :: Nil, emptyValDef, Nil) =>
      tree
    case _ =>
      throw new IllegalArgumentException(s"Tree ${showRaw(tree)} isn't a correct representation of annotation." +
                                          """Consider reformatting it into a q"new $name[..$targs](...$argss)" shape""")
  }

  def mkAnnotation(trees: List[Tree]): List[Tree] = trees.map(mkAnnotation)

  def mkParam(argss: List[List[Tree]], extraFlags: FlagSet = NoFlags, excludeFlags: FlagSet = DEFERRED): List[List[ValDef]] =
    argss.map { args => args.map { mkParam(_, extraFlags, excludeFlags) } }

  def mkParam(tree: Tree, extraFlags: FlagSet, excludeFlags: FlagSet): ValDef = tree match {
    case Typed(Ident(name: TermName), tpt) =>
      mkParam(ValDef(NoMods, name, tpt, EmptyTree), extraFlags, excludeFlags)
    case vd: ValDef =>
      var newmods = vd.mods & (~excludeFlags)
      if (vd.rhs.nonEmpty) newmods |= DEFAULTPARAM
      copyValDef(vd)(mods = newmods | extraFlags)
    case _ =>
      throw new IllegalArgumentException(s"$tree is not valid represenation of a parameter, " +
                                          """consider reformatting it into q"val $name: $T = $default" shape""")
  }

  def mkImplicitParam(args: List[Tree]): List[ValDef] = args.map(mkImplicitParam)

  def mkImplicitParam(tree: Tree): ValDef = mkParam(tree, IMPLICIT | PARAM, NoFlags)

  def mkTparams(tparams: List[Tree]): List[TypeDef] =
    tparams.map {
      case td: TypeDef => copyTypeDef(td)(mods = (td.mods | PARAM) & (~DEFERRED))
      case other => throw new IllegalArgumentException(s"can't splice $other as type parameter")
    }

  def mkRefineStat(stat: Tree): Tree = {
    stat match {
      case dd: DefDef => require(dd.rhs.isEmpty, "can't use DefDef with non-empty body as refine stat")
      case vd: ValDef => require(vd.rhs.isEmpty, "can't use ValDef with non-empty rhs as refine stat")
      case td: TypeDef =>
      case _ => throw new IllegalArgumentException(s"not legal refine stat: $stat")
    }
    stat
  }

  def mkRefineStat(stats: List[Tree]): List[Tree] = stats.map(mkRefineStat)

  def mkPackageStat(stat: Tree): Tree = {
    stat match {
      case cd: ClassDef =>
      case md: ModuleDef =>
      case pd: PackageDef =>
      case _ => throw new IllegalArgumentException(s"not legal package stat: $stat")
    }
    stat
  }

  def mkPackageStat(stats: List[Tree]): List[Tree] = stats.map(mkPackageStat)

  object ScalaDot {
    def apply(name: Name): Tree = gen.scalaDot(name)
    def unapply(tree: Tree): Option[Name] = tree match {
      case Select(id @ Ident(nme.scala_), name) if id.symbol == ScalaPackage => Some(name)
      case _ => None
    }
  }

  def mkEarlyDef(defn: Tree): Tree = defn match {
    case vdef @ ValDef(mods, _, _, _) if !mods.isDeferred =>
      copyValDef(vdef)(mods = mods | PRESUPER)
    case tdef @ TypeDef(mods, _, _, _) =>
      copyTypeDef(tdef)(mods = mods | PRESUPER)
    case _ =>
      throw new IllegalArgumentException(s"not legal early def: $defn")
  }

  def mkEarlyDef(defns: List[Tree]): List[Tree] = defns.map(mkEarlyDef)

  def mkRefTree(qual: Tree, sym: Symbol): RefTree = global.RefTree(qual, sym.name) setSymbol sym

  def freshTermName(prefix: String = nme.FRESH_TERM_NAME_PREFIX): TermName = global.freshTermName(prefix)

  def freshTypeName(prefix: String): TypeName = global.freshTypeName(prefix)

  protected implicit def fresh: FreshNameCreator = global.currentFreshNameCreator

  object ImplicitParams {
    def apply(paramss: List[List[Tree]], implparams: List[Tree]): List[List[Tree]] =
      if (implparams.nonEmpty) paramss :+ mkImplicitParam(implparams) else paramss

    def unapply(vparamss: List[List[ValDef]]): Some[(List[List[ValDef]], List[ValDef])] = vparamss match {
      case init :+ (last @ (initlast :: _)) if initlast.mods.isImplicit => Some((init, last))
      case _ => Some((vparamss, Nil))
    }
  }

  object FlagsRepr {
    def apply(bits: Long): FlagSet = bits
    def unapply(flags: Long): Some[Long] = Some(flags)
  }

  /** Construct/deconstruct type application term trees.
   *  Treats other term trees as zero-argument type applications.
   */
  object SyntacticTypeApplied {
    def apply(tree: Tree, targs: List[Tree]): Tree =
      if (targs.isEmpty) tree
      else if (tree.isTerm) TypeApply(tree, targs)
      else throw new IllegalArgumentException(s"can't apply type arguments to $tree")

    def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match {
      case TypeApply(fun, targs) => Some((fun, targs))
      case _ if tree.isTerm      => Some((tree, Nil))
      case _                     => None
    }
  }

  /** Construct/deconstruct applied type trees.
   *  Treats other types as zero-arity applied types.
   */
  object SyntacticAppliedType {
    def apply(tree: Tree, targs: List[Tree]): Tree =
      if (targs.isEmpty) tree
      else if (tree.isType) AppliedTypeTree(tree, targs)
      else throw new IllegalArgumentException(s"can't create applied type from non-type $tree")

    def unapply(tree: Tree): Option[(Tree, List[Tree])] = tree match {
      case MaybeTypeTreeOriginal(AppliedTypeTree(tpe, targs)) => Some((tpe, targs))
      case _ if tree.isType => Some((tree, Nil))
      case _ => None
    }
  }

  object SyntacticApplied {
    def apply(tree: Tree, argss: List[List[Tree]]): Tree =
      argss.foldLeft(tree) { (f, args) => Apply(f, args.map(treeInfo.assignmentToMaybeNamedArg)) }

    def unapply(tree: Tree): Some[(Tree, List[List[Tree]])] = tree match {
      case UnApply(treeInfo.Unapplied(Select(fun, nme.unapply)), pats) =>
        Some((fun, pats :: Nil))
      case treeInfo.Applied(fun, targs, argss) =>
        fun match {
          case Select(_: New, nme.CONSTRUCTOR) =>
            Some((tree, Nil))
          case _ =>
            val callee =
              if (fun.isTerm) SyntacticTypeApplied(fun, targs)
              else SyntacticAppliedType(fun, targs)
            Some((callee, argss))
        }
    }
  }

  // recover constructor contents generated by gen.mkTemplate
  private object UnCtor {
    def unapply(tree: Tree): Option[(Modifiers, List[List[ValDef]], List[List[Tree]], List[Tree])] = tree match {
      // NOTE: should be SyntacticBlock(lvdefs :+ _), but have to work around because of emulation that we have to do for SyntacticBlock
      // read more about the workaround in comments for SyntacticBlock
      case DefDef(mods, nme.MIXIN_CONSTRUCTOR, _, _, _, SyntacticBlock(lvdefs)) =>
        Some((mods | Flag.TRAIT, Nil, Nil, lvdefs))
      case DefDef(mods, nme.CONSTRUCTOR, Nil, vparamss, _, SyntacticBlock(lvdefs :+ SyntacticApplied(_, argss) :+ _)) =>
        Some((mods, vparamss, argss, lvdefs))
      case _ => None
    }
  }

  // undo gen.mkTemplate
  private object UnMkTemplate {
    def unapply(templ: Template): Option[(List[Tree], ValDef, Modifiers, List[List[ValDef]], List[Tree], List[Tree])] = {
      val Template(parents0, selfdef, _) = templ
      val tbody = treeInfo.untypecheckedTemplBody(templ)

      def result(ctorMods: Modifiers, vparamss: List[List[ValDef]], edefs: List[Tree], parents: List[Tree], body: List[Tree]) =
        Some((parents, selfdef, ctorMods, vparamss, edefs, body))
      def indexOfCtor(trees: List[Tree]) =
        trees.indexWhere { case UnCtor(_, _, _, _) => true ; case _ => false }

      if (tbody forall treeInfo.isInterfaceMember)
        result(NoMods | Flag.TRAIT, Nil, Nil, parents0, tbody)
      else if (indexOfCtor(tbody) == -1)
        None
      else {
        val (rawEdefs, rest) = tbody.span(treeInfo.isEarlyDef)
        val (gvdefs, etdefs) = rawEdefs.partition(treeInfo.isEarlyValDef)
        val (fieldDefs, UnCtor(ctorMods, ctorVparamss, argss, lvdefs) :: body) = rest.splitAt(indexOfCtor(rest))
        val parents = parents0 match {
          case head :: tail =>
            if (argss == List(Nil)) parents0
            else SyntacticApplied(head, argss) :: tail
          case Nil =>
            Nil
        }
        val evdefs = gvdefs.zip(lvdefs).map {
          case (gvdef @ ValDef(_, _, tpt: TypeTree, _), ValDef(_, _, _, rhs)) =>
            copyValDef(gvdef)(tpt = tpt.original, rhs = rhs)
          case (gvdef @ ValDef(_, _, tpt, _), ValDef(_, _, _, rhs)) =>
            copyValDef(gvdef)(tpt = tpt, rhs = rhs)
        }
        val edefs = evdefs ::: etdefs
        if (ctorMods.isTrait)
          result(ctorMods, Nil, edefs, parents, body)
        else {
          // undo conversion from (implicit ... ) to ()(implicit ... ) when its the only parameter section
          val vparamssRestoredImplicits = ctorVparamss match {
            case Nil :: (tail @ ((head :: _) :: _)) if head.mods.hasFlag(IMPLICIT) => tail
            case other => other
          }
          // undo flag modifications by mergeing flag info from constructor args and fieldDefs
          val modsMap = fieldDefs.map { case ValDef(mods, name, _, _) => name -> mods }.toMap
          def ctorArgsCorrespondToFields = vparamssRestoredImplicits.flatten.forall { vd => modsMap.contains(vd.name) }
          if (!ctorArgsCorrespondToFields) None
          else {
            val vparamss = mmap(vparamssRestoredImplicits) { vd =>
              val originalMods = modsMap(vd.name) | (vd.mods.flags & DEFAULTPARAM)
              atPos(vd.pos)(ValDef(originalMods, vd.name, vd.tpt, vd.rhs))
            }
            result(ctorMods, vparamss, edefs, parents, body)
          }
        }
      }
    }
  }

  protected def mkSelfType(tree: Tree) = tree match {
    case vd: ValDef =>
      require(vd.rhs.isEmpty, "self types must have empty right hand side")
      copyValDef(vd)(mods = (vd.mods | PRIVATE) & (~DEFERRED))
    case _ =>
      throw new IllegalArgumentException(s"$tree is not a valid representation of self type, " +
                                         """consider reformatting into q"val $self: $T" shape""")
  }

  object SyntacticClassDef {
    def apply(mods: Modifiers, name: TypeName, tparams: List[Tree],
              constrMods: Modifiers, vparamss: List[List[Tree]],
              earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = {
      val extraFlags = PARAMACCESSOR | (if (mods.isCase) CASEACCESSOR else 0L)
      val vparamss0 = mkParam(vparamss, extraFlags, excludeFlags = DEFERRED | PARAM)
      val tparams0 = mkTparams(tparams)
      val parents0 = gen.mkParents(mods,
        if (mods.isCase) parents.filter {
          case ScalaDot(tpnme.Product | tpnme.Serializable | tpnme.AnyRef) => false
          case _ => true
        } else parents
      )
      val body0 = earlyDefs ::: body
      val selfType0 = mkSelfType(selfType)
      val templ = gen.mkTemplate(parents0, selfType0, constrMods, vparamss0, body0)
      gen.mkClassDef(mods, name, tparams0, templ)
    }

    def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef], Modifiers, List[List[ValDef]],
                                     List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
      case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfType, ctorMods, vparamss, earlyDefs, body))
        if !ctorMods.isTrait && !ctorMods.hasFlag(JAVA) =>
        Some((mods, name, tparams, ctorMods, vparamss, earlyDefs, parents, selfType, body))
      case _ =>
        None
    }
  }

  object SyntacticTraitDef {
    def apply(mods: Modifiers, name: TypeName, tparams: List[Tree], earlyDefs: List[Tree],
              parents: List[Tree], selfType: Tree, body: List[Tree]): ClassDef = {
      val mods0 = mods | TRAIT | ABSTRACT
      val templ = gen.mkTemplate(parents, mkSelfType(selfType), Modifiers(TRAIT), Nil, earlyDefs ::: body)
      gen.mkClassDef(mods0, name, mkTparams(tparams), templ)
    }

    def unapply(tree: Tree): Option[(Modifiers, TypeName, List[TypeDef],
                                     List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
      case ClassDef(mods, name, tparams, UnMkTemplate(parents, selfType, ctorMods, vparamss, earlyDefs, body))
        if mods.isTrait =>
        Some((mods, name, tparams, earlyDefs, parents, selfType, body))
      case _ => None
    }
  }

  object SyntacticObjectDef {
    def apply(mods: Modifiers, name: TermName, earlyDefs: List[Tree],
              parents: List[Tree], selfType: Tree, body: List[Tree]): ModuleDef =
      ModuleDef(mods, name, gen.mkTemplate(parents, mkSelfType(selfType), NoMods, Nil, earlyDefs ::: body))

    def unapply(tree: Tree): Option[(Modifiers, TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
      case ModuleDef(mods, name, UnMkTemplate(parents, selfType, _, _, earlyDefs, body)) =>
        Some((mods, name, earlyDefs, parents, selfType, body))
      case _ =>
        None
    }
  }

  object SyntacticPackageObjectDef {
    def apply(name: TermName, earlyDefs: List[Tree],
              parents: List[Tree], selfType: Tree, body: List[Tree]): PackageDef =
      gen.mkPackageObject(SyntacticObjectDef(NoMods, name, earlyDefs, parents, selfType, body))

    def unapply(tree: Tree): Option[(TermName, List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
      case PackageDef(Ident(name: TermName), List(SyntacticObjectDef(NoMods, nme.PACKAGEkw, earlyDefs, parents, selfType, body))) =>
        Some((name, earlyDefs, parents, selfType, body))
      case _ =>
        None
    }
  }

  // match references to `scala.$name`
  protected class ScalaMemberRef(symbols: Seq[Symbol]) {
    def result(name: Name): Option[Symbol] =
      symbols.collect { case sym if sym.name == name => sym }.headOption
    def unapply(tree: Tree): Option[Symbol] = tree match {
      case id @ Ident(name) if symbols.contains(id.symbol) && name == id.symbol.name =>
        Some(id.symbol)
      case Select(scalapkg @ Ident(nme.scala_), name) if scalapkg.symbol == ScalaPackage =>
        result(name)
      case Select(Select(Ident(nme.ROOTPKG), nme.scala_), name) =>
        result(name)
      case _ => None
    }
  }
  protected object TupleClassRef extends ScalaMemberRef(TupleClass.seq)
  protected object TupleCompanionRef extends ScalaMemberRef(TupleClass.seq.map { _.companionModule })
  protected object UnitClassRef extends ScalaMemberRef(Seq(UnitClass))
  protected object FunctionClassRef extends ScalaMemberRef(FunctionClass.seq)

  object SyntacticTuple {
    def apply(args: List[Tree]): Tree = {
      require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported")
      gen.mkTuple(args)
    }

    def unapply(tree: Tree): Option[List[Tree]] = tree match {
      case Literal(Constant(())) =>
        Some(Nil)
      case Apply(MaybeTypeTreeOriginal(SyntacticTypeApplied(MaybeSelectApply(TupleCompanionRef(sym)), targs)), args)
        if sym == TupleClass(args.length).companionModule
        && (targs.isEmpty || targs.length == args.length) =>
        Some(args)
      case _ if tree.isTerm =>
        Some(tree :: Nil)
      case _ =>
        None
    }
  }

  object SyntacticTupleType {
    def apply(args: List[Tree]): Tree = {
      require(args.isEmpty || TupleClass(args.length).exists, s"Tuples with ${args.length} arity aren't supported")
      gen.mkTupleType(args)
    }

    def unapply(tree: Tree): Option[List[Tree]] = tree match {
      case MaybeTypeTreeOriginal(UnitClassRef(_)) =>
        Some(Nil)
      case MaybeTypeTreeOriginal(AppliedTypeTree(TupleClassRef(sym), args))
        if sym == TupleClass(args.length) =>
        Some(args)
      case _ if tree.isType =>
        Some(tree :: Nil)
      case _ =>
        None
    }
  }

  object SyntacticFunctionType {
    def apply(argtpes: List[Tree], restpe: Tree): Tree = {
      require(FunctionClass(argtpes.length).exists, s"Function types with ${argtpes.length} arity aren't supported")
      gen.mkFunctionTypeTree(argtpes, restpe)
    }

    def unapply(tree: Tree): Option[(List[Tree], Tree)] = tree match {
      case MaybeTypeTreeOriginal(AppliedTypeTree(FunctionClassRef(sym), args @ (argtpes :+ restpe)))
        if sym == FunctionClass(args.length - 1) =>
        Some((argtpes, restpe))
      case _ => None
    }
  }

  object SyntheticUnit {
    def unapply(tree: Tree): Boolean = tree match {
      case Literal(Constant(())) if tree.hasAttachment[SyntheticUnitAttachment.type] => true
      case _ => false
    }
  }

  /** Syntactic combinator that abstracts over Block tree.
   *
   *  Apart from providing a more straightforward api that exposes
   *  block as a list of elements rather than (stats, expr) pair
   *  it also:
   *
   *  1. Strips trailing synthetic units which are inserted by the
   *     compiler if the block ends with a definition rather
   *     than an expression or is empty.
   *
   *  2. Matches non-block term trees and recognizes them as
   *     single-element blocks for sake of consistency with
   *     compiler's default to treat single-element blocks with
   *     expressions as just expressions. The only exception is q""
   *     which is not considered to be a block.
   */
  object SyntacticBlock {
    def apply(stats: List[Tree]): Tree = gen.mkBlock(stats)

    def unapply(tree: Tree): Option[List[Tree]] = tree match {
      // NOTE: these clauses are here just because vanilla 2.10.x parser doesn't support SyntheticUnit
      // therefore we have to emulate by dropping all units that conclude a block coming right after a definition
      // ===== start of emulation block ====
      // if you remove this clause, change UnCtor.unapply for mixin constructors
      case bl @ global.Block(stats @ (_ :+ (_: DefTree)), Literal(Constant(()))) => Some(treeInfo.untypecheckedBlockBody(bl))
      // ===== end of emulation block ====
      case bl @ global.Block(stats, SyntheticUnit()) => Some(treeInfo.untypecheckedBlockBody(bl))
      case bl @ global.Block(stats, expr)            => Some(treeInfo.untypecheckedBlockBody(bl) :+ expr)
      case SyntheticUnit()                           => Some(Nil)
      case _ if tree.isTerm && tree.nonEmpty         => Some(tree :: Nil)
      case _                                         => None
    }
  }

  object SyntacticFunction {
    def apply(params: List[Tree], body: Tree): Function = {
      val params0 :: Nil = mkParam(params :: Nil, PARAM)
      require(params0.forall { _.rhs.isEmpty }, "anonymous functions don't support parameters with default values")
      Function(params0, body)
    }

    def unapply(tree: Function): Option[(List[ValDef], Tree)] = Function.unapply(tree)
  }

  object SyntacticNew {
    def apply(earlyDefs: List[Tree], parents: List[Tree], selfType: Tree, body: List[Tree]): Tree =
      gen.mkNew(parents, mkSelfType(selfType), earlyDefs ::: body, NoPosition, NoPosition)

    def unapply(tree: Tree): Option[(List[Tree], List[Tree], ValDef, List[Tree])] = tree match {
      case treeInfo.Applied(Select(New(SyntacticAppliedType(ident, targs)), nme.CONSTRUCTOR), Nil, List(Nil)) =>
        Some((Nil, SyntacticAppliedType(ident, targs) :: Nil, emptyValDef, Nil))
      case treeInfo.Applied(Select(New(SyntacticAppliedType(ident, targs)), nme.CONSTRUCTOR), Nil, argss) =>
        Some((Nil, SyntacticApplied(SyntacticAppliedType(ident, targs), argss) :: Nil, emptyValDef, Nil))
      case SyntacticBlock(SyntacticClassDef(_, tpnme.ANON_CLASS_NAME, Nil, _, ListOfNil, earlyDefs, parents, selfType, body) ::
                          Apply(Select(New(Ident(tpnme.ANON_CLASS_NAME)), nme.CONSTRUCTOR), Nil) :: Nil) =>
        Some((earlyDefs, parents, selfType, body))
      case _ =>
        None
    }
  }

  object SyntacticDefDef {
    def apply(mods: Modifiers, name: TermName, tparams: List[Tree],
              vparamss: List[List[Tree]], tpt: Tree, rhs: Tree): DefDef = {
      val tparams0 = mkTparams(tparams)
      val vparamss0 = mkParam(vparamss, PARAM)
      val rhs0 = {
        if (name != nme.CONSTRUCTOR) rhs
        else rhs match {
          case Block(_, _) => rhs
          case _ => Block(List(rhs), gen.mkSyntheticUnit)
        }
      }
      DefDef(mods, name, tparams0, vparamss0, tpt, rhs0)
    }

    def unapply(tree: Tree): Option[(Modifiers, TermName, List[TypeDef], List[List[ValDef]], Tree, Tree)] = tree match {
      case DefDef(mods, nme.CONSTRUCTOR, tparams, vparamss, tpt, Block(List(expr), Literal(Constant(())))) =>
        Some((mods, nme.CONSTRUCTOR, tparams, vparamss, tpt, expr))
      case DefDef(mods, name, tparams, vparamss, tpt, rhs) =>
        Some((mods, name, tparams, vparamss, tpt, rhs))
      case _ => None
    }
  }

  protected class SyntacticValDefBase(isMutable: Boolean) {
    def modifiers(mods: Modifiers): Modifiers = if (isMutable) mods | MUTABLE else mods

    def apply(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = ValDef(modifiers(mods), name, tpt, rhs)

    def unapply(tree: Tree): Option[(Modifiers, TermName, Tree, Tree)] = tree match {
      case ValDef(mods, name, tpt, rhs) if mods.hasFlag(MUTABLE) == isMutable =>
        Some((mods, name, tpt, rhs))
      case _ =>
        None
    }
  }
  object SyntacticValDef extends SyntacticValDefBase(isMutable = false)
  object SyntacticVarDef extends SyntacticValDefBase(isMutable = true)

  object SyntacticAssign {
    def apply(lhs: Tree, rhs: Tree): Tree = gen.mkAssign(lhs, rhs)
    def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
      case Assign(lhs, rhs) => Some((lhs, rhs))
      case AssignOrNamedArg(lhs, rhs) => Some((lhs, rhs))
      case Apply(Select(fn, nme.update), args :+ rhs) => Some((atPos(fn.pos)(Apply(fn, args)), rhs))
      case _ => None
    }
  }

  // def UnliftListElementwise[T](unliftable: Unliftable[T]) = new UnliftListElementwise[T] {
  //   def unapply(lst: List[Tree]): Option[List[T]] = {
  //     val unlifted = lst.flatMap { unliftable.unapply(_) }
  //     if (unlifted.length == lst.length) Some(unlifted) else None
  //   }
  // }

  // def UnliftListOfListsElementwise[T](unliftable: Unliftable[T]) = new UnliftListOfListsElementwise[T] {
  //   def unapply(lst: List[List[Tree]]): Option[List[List[T]]] = {
  //     val unlifted = lst.map { l => l.flatMap { unliftable.unapply(_) } }
  //     if (unlifted.flatten.length == lst.flatten.length) Some(unlifted) else None
  //   }
  // }

  object SyntacticValFrom {
    def apply(pat: Tree, rhs: Tree): Tree = gen.ValFrom(pat, gen.mkCheckIfRefutable(pat, rhs))
    def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
      case gen.ValFrom(pat, UnCheckIfRefutable(pat1, rhs1)) if pat.equalsStructure(pat1) =>
        Some((pat, rhs1))
      case gen.ValFrom(pat, rhs) =>
        Some((pat, rhs))
      case _ => None
    }
  }

  object SyntacticValEq {
    def apply(pat: Tree, rhs: Tree): Tree         = gen.ValEq(pat, rhs)
    def unapply(tree: Tree): Option[(Tree, Tree)] = gen.ValEq.unapply(tree)
  }

  object SyntacticFilter {
    def apply(tree: Tree): Tree           = gen.Filter(tree)
    def unapply(tree: Tree): Option[Tree] = gen.Filter.unapply(tree)
  }

  // If a tree in type position isn't provided by the user (e.g. `tpt` fields of
  // `ValDef` and `DefDef`, function params etc), then it's going to be parsed as
  // TypeTree with empty original and empty tpe. This extractor matches such trees
  // so that one can write q"val x = 2" to match typecheck(q"val x = 2"). Note that
  // TypeTree() is the only possible representation for empty trees in type positions.
  // We used to sometimes receive EmptyTree in such cases, but not anymore.
  object SyntacticEmptyTypeTree {
    def apply(): TypeTree = global.TypeTree()
    def unapply(tt: TypeTree): Boolean = tt.original == null || tt.original.isEmpty
  }

  // match a sequence of desugared `val $pat = $value`
  protected object UnPatSeq {
    def unapply(trees: List[Tree]): Option[List[(Tree, Tree)]] = {
      val imploded = implodePatDefs(trees)
      val patvalues = imploded.flatMap {
        case SyntacticPatDef(_, pat, EmptyTree, rhs) => Some((pat, rhs))
        case ValDef(_, name, SyntacticEmptyTypeTree(), rhs) => Some((Bind(name, global.Ident(nme.WILDCARD)), rhs))
        case ValDef(_, name, tpt, rhs) => Some((Bind(name, Typed(global.Ident(nme.WILDCARD), tpt)), rhs))
        case _ => None
      }
      if (patvalues.length == imploded.length) Some(patvalues) else None
    }
  }

  // implode multiple-statement desugaring of pattern definitions
  // into single-statement valdefs with nme.QUASIQUOTE_PAT_DEF name
  object implodePatDefs extends Transformer {
    override def transform(tree: Tree) = tree match {
      case templ: Template => deriveTemplate(templ)(transformStats)
      case block: Block =>
        val Block(init, last) = block
        Block(transformStats(init), transform(last)).copyAttrs(block)
      case ValDef(mods, name1, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, Ident(name2)) :: Nil))
        if name1 == name2 =>
        ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value))
      case _ =>
        super.transform(tree)
    }
    def transformStats(trees: List[Tree]): List[Tree] = trees match {
      case Nil => Nil
      case ValDef(mods, _, SyntacticEmptyTypeTree(), Match(MaybeTyped(MaybeUnchecked(value), tpt), CaseDef(pat, EmptyTree, SyntacticTuple(ids)) :: Nil)) :: tail
        if mods.hasFlag(SYNTHETIC) && mods.hasFlag(ARTIFACT) =>
        ids match {
          case Nil =>
            ValDef(NoMods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail)
          case _   =>
            val mods = tail.take(1).head.asInstanceOf[ValDef].mods
            ValDef(mods, nme.QUASIQUOTE_PAT_DEF, Typed(pat, tpt), transform(value)) :: transformStats(tail.drop(ids.length))
        }
      case other :: tail =>
        transform(other) :: transformStats(tail)
    }
    def apply(tree: Tree) = transform(tree)
    def apply(trees: List[Tree]) = transformStats(trees)
  }

  object SyntacticPatDef {
    def apply(mods: Modifiers, pat: Tree, tpt: Tree, rhs: Tree): List[ValDef] = tpt match {
      case SyntacticEmptyTypeTree() => gen.mkPatDef(mods, pat, rhs)
      case _                        => gen.mkPatDef(mods, Typed(pat, tpt), rhs)
    }
    def unapply(tree: Tree): Option[(Modifiers, Tree, Tree, Tree)] = tree match {
      case ValDef(mods, compatnme.QUASIQUOTE_PAT_DEF, Typed(pat,  tpt), rhs) => Some((mods, pat, tpt, rhs))
      case _ => None
    }
  }

  // match a sequence of desugared `val $pat = $value` with a tuple in the end
  protected object UnPatSeqWithRes {
    def unapply(tree: Tree): Option[(List[(Tree, Tree)], List[Tree])] = tree match {
      case SyntacticBlock(UnPatSeq(trees) :+ SyntacticTuple(elems)) => Some((trees, elems))
      case _ => None
    }
  }

  // undo gen.mkSyntheticParam
  protected object UnSyntheticParam {
    def unapply(tree: Tree): Option[TermName] = tree match {
      case ValDef(mods, name, _, EmptyTree)
        if mods.hasFlag(SYNTHETIC) && mods.hasFlag(PARAM) =>
        Some(name)
      case _ => None
    }
  }

  // undo gen.mkVisitor
  protected object UnVisitor {
    def unapply(tree: Tree): Option[(TermName, List[CaseDef])] = tree match {
      case Function(UnSyntheticParam(x1) :: Nil, Match(MaybeUnchecked(Ident(x2)), cases))
        if x1 == x2 =>
        Some((x1, cases))
      case _ => None
    }
  }

  // undo gen.mkFor:makeClosure
  protected object UnClosure {
    def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
      case Function(ValDef(Modifiers(PARAM, _, _), name, tpt, EmptyTree) :: Nil, body) =>
        tpt match {
          case SyntacticEmptyTypeTree() => Some((Bind(name, global.Ident(nme.WILDCARD)), body))
          case _                        => Some((Bind(name, Typed(global.Ident(nme.WILDCARD), tpt)), body))
        }
      case UnVisitor(_, CaseDef(pat, EmptyTree, body) :: Nil) =>
        Some((pat, body))
      case _ => None
    }
  }

  // match call to either withFilter or filter
  protected object FilterCall {
    def unapply(tree: Tree): Option[(Tree,Tree)] = tree match {
      case Apply(Select(obj, nme.withFilter | nme.filter), arg :: Nil) =>
        Some(obj, arg)
      case _ => None
    }
  }

  // transform a chain of withFilter calls into a sequence of for filters
  protected object UnFilter {
    def unapply(tree: Tree): Some[(Tree, List[Tree])] = tree match {
      case UnCheckIfRefutable(_, _) =>
        Some((tree, Nil))
      case FilterCall(UnFilter(rhs, rest), UnClosure(_, test)) =>
        Some((rhs, rest :+ SyntacticFilter(test)))
      case _ =>
        Some((tree, Nil))
    }
  }

  // undo gen.mkCheckIfRefutable
  protected object UnCheckIfRefutable {
    def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
      case FilterCall(rhs, UnVisitor(name,
          CaseDef(pat, EmptyTree, Literal(Constant(true))) ::
          CaseDef(Ident(nme.WILDCARD), EmptyTree, Literal(Constant(false))) :: Nil))
        if name.toString.contains(nme.CHECK_IF_REFUTABLE_STRING) =>
        Some((pat, rhs))
      case _ => None
    }
  }

  // undo gen.mkFor:makeCombination accounting for possible extra implicit argument
  protected class UnForCombination(name: TermName) {
    def unapply(tree: Tree) = tree match {
      case SyntacticApplied(SyntacticTypeApplied(sel @ Select(lhs, meth), _), (f :: Nil) :: Nil)
        if name == meth && sel.hasAttachment[ForAttachment.type] =>
        Some(lhs, f)
      case SyntacticApplied(SyntacticTypeApplied(sel @ Select(lhs, meth), _), (f :: Nil) :: _ :: Nil)
        if name == meth && sel.hasAttachment[ForAttachment.type] =>
        Some(lhs, f)
      case _ => None
    }
  }
  protected object UnMap     extends UnForCombination(nme.map)
  protected object UnForeach extends UnForCombination(nme.foreach)
  protected object UnFlatMap extends UnForCombination(nme.flatMap)

  // undo desugaring done in gen.mkFor
  protected object UnFor {
    def unapply(tree: Tree): Option[(List[Tree], Tree)] = {
      val interm = tree match {
        case UnFlatMap(UnFilter(rhs, filters), UnClosure(pat, UnFor(rest, body))) =>
          Some(((pat, rhs), filters ::: rest, body))
        case UnForeach(UnFilter(rhs, filters), UnClosure(pat, UnFor(rest, body))) =>
          Some(((pat, rhs), filters ::: rest, body))
        case UnMap(UnFilter(rhs, filters), UnClosure(pat, cbody)) =>
          Some(((pat, rhs), filters, gen.Yield(cbody)))
        case UnForeach(UnFilter(rhs, filters), UnClosure(pat, cbody)) =>
          Some(((pat, rhs), filters, cbody))
        case _ => None
      }
      interm.flatMap {
        case ((Bind(_, SyntacticTuple(_)) | SyntacticTuple(_),
               UnFor(SyntacticValFrom(pat, rhs) :: innerRest, gen.Yield(UnPatSeqWithRes(pats, elems2)))),
              outerRest, fbody) =>
          val valeqs = pats.map { case (pat, rhs) => SyntacticValEq(pat, rhs) }
          Some((SyntacticValFrom(pat, rhs) :: innerRest ::: valeqs ::: outerRest, fbody))
        case ((pat, rhs), filters, body) =>
          Some((SyntacticValFrom(pat, rhs) :: filters, body))
      }
    }
  }

  // check that enumerators are valid
  protected def mkEnumerators(enums: List[Tree]): List[Tree] = {
    require(enums.nonEmpty, "enumerators can't be empty")
    enums.head match {
      case SyntacticValFrom(_, _) =>
      case t => throw new IllegalArgumentException(s"$t is not a valid fist enumerator of for loop")
    }
    enums.tail.foreach {
      case SyntacticValEq(_, _) | SyntacticValFrom(_, _) | SyntacticFilter(_) =>
      case t => throw new IllegalArgumentException(s"$t is not a valid representation of a for loop enumerator")
    }
    enums
  }

  object SyntacticFor {
    def apply(enums: List[Tree], body: Tree): Tree = gen.mkFor(mkEnumerators(enums), body)
    def unapply(tree: Tree) = tree match {
      case UnFor(enums, gen.Yield(body)) => None
      case UnFor(enums, body) => Some((enums, body))
      case _ => None
    }
  }

  object SyntacticForYield {
    def apply(enums: List[Tree], body: Tree): Tree = gen.mkFor(mkEnumerators(enums), gen.Yield(body))
    def unapply(tree: Tree) = tree match {
      case UnFor(enums, gen.Yield(body)) => Some((enums, body))
      case _ => None
    }
  }

  // use typetree's original instead of typetree itself
  protected object MaybeTypeTreeOriginal {
    def unapply(tree: Tree): Some[Tree] = tree match {
      case tt: TypeTree => Some(tt.original)
      case _            => Some(tree)
    }
  }

  // drop potential extra call to .apply
  protected object MaybeSelectApply {
    def unapply(tree: Tree): Some[Tree] = tree match {
      case Select(f, nme.apply) => Some(f)
      case other                => Some(other)
    }
  }

  // drop potential @scala.unchecked annotation
  protected object MaybeUnchecked {
    def unapply(tree: Tree): Some[Tree] = tree match {
      case Annotated(SyntacticNew(Nil, ScalaDot(compattpnme.unchecked) :: Nil, emptyValDef, Nil), annottee) =>
        Some(annottee)
      case Typed(annottee, MaybeTypeTreeOriginal(
        Annotated(SyntacticNew(Nil, ScalaDot(compattpnme.unchecked) :: Nil, emptyValDef, Nil), _))) =>
        Some(annottee)
      case annottee => Some(annottee)
    }
  }

  protected object MaybeTyped {
    def unapply(tree: Tree): Some[(Tree, Tree)] = tree match {
      case Typed(v, tpt) => Some((v, tpt))
      case v             => Some((v, SyntacticEmptyTypeTree()))
    }
  }

  protected def mkCases(cases: List[Tree]): List[CaseDef] = cases.map {
    case c: CaseDef => c
    case tree => throw new IllegalArgumentException("$tree is not valid representation of pattern match case")
  }

  object SyntacticPartialFunction {
    def apply(cases: List[Tree]): Match = Match(EmptyTree, mkCases(cases))
    def unapply(tree: Tree): Option[List[CaseDef]] = tree match {
      case Match(EmptyTree, cases) => Some(cases)
      case Typed(
             Block(
               List(ClassDef(clsMods, tpnme.ANON_FUN_NAME, Nil, Template(
                 List(abspf: TypeTree, ser: TypeTree), noSelfType, List(
                   DefDef(_, nme.CONSTRUCTOR, _, _, _, _),
                   DefDef(_, nme.applyOrElse, _, _, _,
                     Match(_, cases :+
                       CaseDef(Bind(nme.DEFAULT_CASE, Ident(nme.WILDCARD)), _, _))),
                   DefDef(_, nme.isDefinedAt, _, _, _, _))))),
               Apply(Select(New(Ident(tpnme.ANON_FUN_NAME)), nme.CONSTRUCTOR), List())),
             pf: TypeTree)
        if pf.tpe != null && pf.tpe.typeSymbol.eq(PartialFunctionClass) &&
           abspf.tpe != null && abspf.tpe.typeSymbol.eq(AbstractPartialFunctionClass) &&
           ser.tpe != null && ser.tpe.typeSymbol.eq(SerializableClass) &&
           clsMods.hasFlag(FINAL) && clsMods.hasFlag(SYNTHETIC) =>
        Some(cases)
      case _ => None
    }
  }

  object SyntacticMatch {
    def apply(scrutinee: Tree, cases: List[Tree]) = {
      require(scrutinee.nonEmpty, "match's scrutinee may not be empty")
      Match(scrutinee, mkCases(cases))
    }

    def unapply(tree: Match): Option[(Tree, List[CaseDef])] = tree match {
      case Match(scrutinee, cases) if scrutinee.nonEmpty => Some((scrutinee, cases))
      case _                                             => None
    }
  }

  object SyntacticTry {
    def apply(block: Tree, catches: List[Tree], finalizer: Tree) = Try(block, mkCases(catches), finalizer)
    def unapply(tree: Try): Option[(Tree, List[CaseDef], Tree)] = Try.unapply(tree)
  }


  object SyntacticTermIdent {
    def apply(name: TermName, isBackquoted: Boolean): Ident = {
      val id = Ident(name)
      if (isBackquoted) id updateAttachment BackquotedIdentifierAttachment
      id
    }
    def unapply(id: Ident): Option[(TermName, Boolean)] = id.name match {
      case name: TermName => Some((name, id.hasAttachment[BackquotedIdentifierAttachment.type]))
      case _              => None
    }
  }

  object SyntacticTypeIdent {
    def apply(name: TypeName): Ident = Ident(name)
    def unapply(tree: Tree): Option[TypeName] = tree match {
      case MaybeTypeTreeOriginal(Ident(name: TypeName)) => Some(name)
      case _ => None
    }
  }

  /** Facade over Imports and ImportSelectors that lets to structurally
   *  deconstruct/reconstruct them.
   *
   *  Selectors are represented in the following way:
   *  1. q"import foo._"            <==> q"import foo.${pq"_"}"
   *  2. q"import foo.bar"          <==> q"import foo.${pq"bar"}"
   *  3. q"import foo.{bar => baz}" <==> q"import foo.${pq"bar -> baz"}"
   *  4. q"import foo.{bar => _}"   <==> q"import foo.${pq"bar -> _"}"
   *
   *  All names in selectors are TermNames despite the fact ImportSelector
   *  can theoretically contain TypeNames too (but they never do in practice.)
   */
  object SyntacticImport {
    // construct/deconstruct {_} import selector
    private object WildcardSelector {
      def apply(offset: Int): ImportSelector = ImportSelector(nme.WILDCARD, offset, null, -1)
      def unapply(sel: ImportSelector): Option[Int] = sel match {
        case ImportSelector(nme.WILDCARD, offset, null, -1) => Some(offset)
        case _                                              => None
      }
    }

    // construct/deconstruct {foo} import selector
    private object NameSelector {
      def apply(name: TermName, offset: Int): ImportSelector = ImportSelector(name, offset, name, offset)
      def unapply(sel: ImportSelector): Option[(TermName, Int)] = sel match {
        case ImportSelector(name1, offset1, name2, offset2) if name1 == name2 && offset1 == offset2 =>
          Some((name1.toTermName, offset1))
        case _ =>
          None
      }
    }

    // construct/deconstruct {foo => bar} import selector
    private object RenameSelector {
      def apply(name1: TermName, offset1: Int, name2: TermName, offset2: Int): ImportSelector =
        ImportSelector(name1, offset1, name2, offset2)
      def unapply(sel: ImportSelector): Option[(TermName, Int, TermName, Int)] = sel match {
        case ImportSelector(_, _, null | nme.WILDCARD, _) =>
          None
        case ImportSelector(name1, offset1, name2, offset2) if name1 != name2 =>
          Some((name1.toTermName, offset1, name2.toTermName, offset2))
        case _ =>
          None
      }
    }

    // construct/deconstruct {foo => _} import selector
    private object UnimportSelector {
      def apply(name: TermName, offset: Int): ImportSelector =
        ImportSelector(name, offset, nme.WILDCARD, -1)
      def unapply(sel: ImportSelector): Option[(TermName, Int)] = sel match {
        case ImportSelector(name, offset, nme.WILDCARD, _) => Some((name.toTermName, offset))
        case _                                             => None
      }
    }

    // represent {_} import selector as pq"_"
    private object WildcardSelectorRepr {
      def apply(pos: Position): Tree = atPos(pos)(global.Ident(nme.WILDCARD))
      def unapply(tree: Tree): Option[Position] = tree match {
        case global.Ident(nme.WILDCARD) => Some(tree.pos)
        case _                        => None
      }
    }

    // represent {foo} import selector as pq"foo"
    private object NameSelectorRepr {
      def apply(name: TermName, pos: Position): Tree = atPos(pos)(Bind(name, WildcardSelectorRepr(pos)))
      def unapply(tree: Tree): Option[(TermName, Position)] = tree match {
        case Bind(name, WildcardSelectorRepr(_)) => Some((name.toTermName, tree.pos))
        case _                                   => None
      }
    }

    // pq"left -> right"
    private object Arrow {
      def apply(left: Tree, right: Tree): Apply =
        Apply(global.Ident(nme.MINGT), left :: right :: Nil)
      def unapply(tree: Apply): Option[(Tree, Tree)] = tree match {
        case Apply(global.Ident(compatnme.MINGT), left :: right :: Nil) => Some((left, right))
        case _ => None
      }
    }

    // represent {foo => bar} import selector as pq"foo -> bar"
    private object RenameSelectorRepr {
      def apply(name1: TermName, pos1: Position, name2: TermName, pos2: Position): Tree = {
        val left = NameSelectorRepr(name1, pos1)
        val right = NameSelectorRepr(name2, pos2)
        atPos(wrappingPos(left :: right :: Nil))(Arrow(left, right))
      }
      def unapply(tree: Tree): Option[(TermName, Position, TermName, Position)] = tree match {
        case Arrow(NameSelectorRepr(name1, pos1), NameSelectorRepr(name2, pos2)) =>
          Some((name1.toTermName, pos1, name2.toTermName, pos2))
        case _ =>
          None
      }
    }

    // represent {foo => _} import selector as pq"foo -> _"
    private object UnimportSelectorRepr {
      def apply(name: TermName, pos: Position): Tree =
        atPos(pos)(Arrow(NameSelectorRepr(name, pos), WildcardSelectorRepr(pos)))
      def unapply(tree: Tree): Option[(TermName, Position)] = tree match {
        case Arrow(NameSelectorRepr(name, pos), WildcardSelectorRepr(_)) =>
          Some((name, pos))
        case _ =>
          None
      }
    }

    private def derivedPos(t: Tree, offset: Int): Position =
      if (t.pos == NoPosition) NoPosition else t.pos.withPoint(offset)

    private def derivedOffset(pos: Position): Int =
      if (pos == NoPosition) -1 else pos.point

    def apply(expr: Tree, selectors: List[Tree]): Import = {
      val importSelectors = selectors.map {
        case WildcardSelectorRepr(pos)                    => WildcardSelector(derivedOffset(pos))
        case NameSelectorRepr(name, pos)                  => NameSelector(name, derivedOffset(pos))
        case RenameSelectorRepr(name1, pos1, name2, pos2) => RenameSelector(name1, derivedOffset(pos1), name2, derivedOffset(pos2))
        case UnimportSelectorRepr(name, pos)              => UnimportSelector(name, derivedOffset(pos))
        case tree                                         => throw new IllegalArgumentException(s"${showRaw(tree)} doesn't correspond to import selector")
      }
      Import(expr, importSelectors)
    }

    def unapply(imp: Import): Some[(Tree, List[Tree])] = {
      val selectors = imp.selectors.map {
        case WildcardSelector(offset)                       => WildcardSelectorRepr(derivedPos(imp, offset))
        case NameSelector(name, offset)                     => NameSelectorRepr(name, derivedPos(imp, offset))
        case RenameSelector(name1, offset1, name2, offset2) => RenameSelectorRepr(name1, derivedPos(imp, offset1), name2, derivedPos(imp, offset2))
        case UnimportSelector(name, offset)                 => UnimportSelectorRepr(name, derivedPos(imp, offset))
      }
      Some((imp.expr, selectors))
    }
  }

  object SyntacticSelectType {
    def apply(qual: Tree, name: TypeName): Select = Select(qual, name)
    def unapply(tree: Tree): Option[(Tree, TypeName)] = tree match {
      case MaybeTypeTreeOriginal(Select(qual, name: TypeName)) => Some((qual, name))
      case _ => None
    }
  }

  object SyntacticSelectTerm {
    def apply(qual: Tree, name: TermName): Select = Select(qual, name)
    def unapply(tree: Tree): Option[(Tree, TermName)] = tree match {
      case Select(qual, name: TermName) => Some((qual, name))
      case _                            => None
    }
  }

  object SyntacticCompoundType {
    def apply(parents: List[Tree], defns: List[Tree]) =
      CompoundTypeTree(Template(gen.mkParents(NoMods, parents), emptyValDef, defns))
    def unapply(tree: Tree): Option[(List[Tree], List[Tree])] = tree match {
      case MaybeTypeTreeOriginal(CompoundTypeTree(Template(parents, _, defns))) =>
        Some((parents, defns))
      case _ =>
        None
    }
  }

  object SyntacticSingletonType {
    def apply(ref: Tree): SingletonTypeTree = SingletonTypeTree(ref)
    def unapply(tree: Tree): Option[Tree] = tree match {
      case MaybeTypeTreeOriginal(SingletonTypeTree(ref)) =>
        Some(ref)
      case _ =>
        None
    }
  }

  object SyntacticTypeProjection {
    def apply(qual: Tree, name: TypeName): SelectFromTypeTree =
      SelectFromTypeTree(qual, name)
    def unapply(tree: Tree): Option[(Tree, TypeName)] = tree match {
      case MaybeTypeTreeOriginal(SelectFromTypeTree(qual, name)) =>
        Some((qual, name))
      case _ =>
        None
    }
  }

  object SyntacticAnnotatedType {
    def apply(tpt: Tree, annot: Tree): Annotated =
      Annotated(annot, tpt)
    def unapply(tree: Tree): Option[(Tree, Tree)] = tree match {
      case MaybeTypeTreeOriginal(Annotated(annot, tpt)) =>
        Some((tpt, annot))
      case _ =>
        None
    }
  }

  object SyntacticExistentialType {
    def apply(tpt: Tree, where: List[Tree]): ExistentialTypeTree =
      ExistentialTypeTree(tpt, where.map {
        case md: MemberDef => md
        case tree => throw new IllegalArgumentException("$tree is not legal forSome definition")
      })
    def unapply(tree: Tree): Option[(Tree, List[MemberDef])] = tree match {
      case MaybeTypeTreeOriginal(ExistentialTypeTree(tpt, where)) =>
        Some((tpt, where.asInstanceOf[List[MemberDef]])) // NOTE: in 2.11 where is typed as List[MemberDef], but in 2.10.x it's still List[Tree]
      case _ =>
        None
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy