From 8e5b76d6dda8f518211f0ab07d6f997d44caf3eb Mon Sep 17 00:00:00 2001
From: Nicolas Stucki <nicolas.stucki@gmail.com>
Date: Thu, 22 Nov 2018 17:32:55 +0100
Subject: [PATCH 1/3] Add instace checks for TASTy relfect `Tree.Term`s

---
 .../tools/dotc/tastyreflect/TreeOpsImpl.scala | 169 +++++++++++++++++-
 library/src/scala/tasty/reflect/TreeOps.scala | 158 ++++++++++++++--
 2 files changed, 302 insertions(+), 25 deletions(-)

diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
index 5b786eb6543c..67b77dbaa9b5 100644
--- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
+++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
@@ -13,7 +13,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
     def symbol(implicit ctx: Context): Symbol = tree.symbol
   }
 
-  object IsPackageClause extends IsPackageClauseExtractor {
+  object IsPackageClause extends IsPackageClauseModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[PackageClause] = tree match {
       case x: tpd.PackageDef => Some(x)
       case _ => None
@@ -47,7 +47,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
 
   // ----- Definitions ----------------------------------------------
 
-  object IsDefinition extends IsDefinitionExtractor {
+  object IsDefinition extends IsDefinitionModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[Definition] = tree match {
       case tree: tpd.MemberDef => Some(tree)
       case tree: PackageDefinition => Some(tree)
@@ -61,7 +61,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
 
   // ClassDef
 
-  object IsClassDef extends IsClassDefExtractor {
+  object IsClassDef extends IsClassDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[ClassDef] = tree match {
       case x: tpd.TypeDef if x.isClassDef => Some(x)
       case _ => None
@@ -87,7 +87,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
 
   // DefDef
 
-  object IsDefDef extends IsDefDefExtractor {
+  object IsDefDef extends IsDefDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[DefDef] = tree match {
       case x: tpd.DefDef => Some(x)
       case _ => None
@@ -112,7 +112,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
 
   // ValDef
 
-  object IsValDef extends IsValDefExtractor {
+  object IsValDef extends IsValDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[ValDef] = tree match {
       case x: tpd.ValDef => Some(x)
       case _ => None
@@ -135,7 +135,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
 
   // TypeDef
 
-  object IsTypeDef extends IsTypeDefExtractor {
+  object IsTypeDef extends IsTypeDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[TypeDef] = tree match {
       case x: tpd.TypeDef if !x.symbol.isClass => Some(x)
       case _ => None
@@ -168,7 +168,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
     def symbol(implicit ctx: Context): PackageSymbol = pdef.symbol
   }
 
-  object IsPackageDef extends IsPackageDefExtractor {
+  object IsPackageDef extends IsPackageDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[PackageDef] = tree match {
       case x: PackageDefinition => Some(x)
       case _ => None
@@ -193,7 +193,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
     def underlying(implicit ctx: Context): Term = term.underlying
   }
 
-  object IsTerm extends IsTermExtractor {
+  object IsTerm extends IsTermModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[Term] =
       if (tree.isTerm) Some(tree) else None
     def unapply(termOrTypeTree: TermOrTypeTree)(implicit ctx: Context, dummy: DummyImplicit): Option[Term] =
@@ -202,6 +202,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
 
   object Term extends TermModule with TermCoreModuleImpl {
 
+    object IsIdent extends IsIdentModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Ident] = x match {
+        case x: tpd.Ident if x.isTerm => Some(x)
+        case _ => None
+      }
+    }
+
     object Ident extends IdentExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[String] = x match {
         case x: tpd.Ident if x.isTerm => Some(x.name.show)
@@ -209,6 +216,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsSelect extends IsSelectModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Select] = x match {
+        case x: tpd.Select if x.isTerm => Some(x)
+        case _ => None
+      }
+    }
+
     object Select extends SelectExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, String, Option[Signature])] = x match {
         case x: tpd.Select if x.isTerm =>
@@ -220,6 +234,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsLiteral extends IsLiteralModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Literal] = x match {
+        case x: tpd.Literal => Some(x)
+        case _ => None
+      }
+    }
+
     object Literal extends LiteralExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[Constant] = x match {
         case Trees.Literal(const) => Some(const)
@@ -227,6 +248,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsThis extends IsThisModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[This] = x match {
+        case x: tpd.This => Some(x)
+        case _ => None
+      }
+    }
+
     object This extends ThisExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[Option[Id]] = x match {
         case Trees.This(qual) => Some(optional(qual))
@@ -234,6 +262,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsNew extends IsNewModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[New] = x match {
+        case x: tpd.New => Some(x)
+        case _ => None
+      }
+    }
+
     object New extends NewExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[TypeTree] = x match {
         case x: tpd.New => Some(x.tpt)
@@ -241,6 +276,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsNamedArg extends IsNamedArgModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[NamedArg] = x match {
+        case x: tpd.NamedArg if x.name.isInstanceOf[Names.TermName] => Some(x) // TODO: Now, the name should alwas be a term name
+        case _ => None
+      }
+    }
+
     object NamedArg extends NamedArgExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(String, Term)] = x match {
         case x: tpd.NamedArg if x.name.isInstanceOf[Names.TermName] => Some((x.name.toString, x.arg))
@@ -248,6 +290,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsApply extends IsApplyModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Apply] = x match {
+        case x: tpd.Apply => Some(x)
+        case _ => None
+      }
+    }
+
     object Apply extends ApplyExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[Term])] = x match {
         case x: tpd.Apply => Some((x.fun, x.args))
@@ -255,6 +304,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsTypeApply extends IsTypeApplyModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[TypeApply] = x match {
+        case x: tpd.TypeApply => Some(x)
+        case _ => None
+      }
+    }
+
     object TypeApply extends TypeApplyExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[TypeTree])] = x match {
         case x: tpd.TypeApply => Some((x.fun, x.args))
@@ -262,6 +318,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsSuper extends IsSuperModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Super] = x match {
+        case x: tpd.Super => Some(x)
+        case _ => None
+      }
+    }
+
     object Super extends SuperExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[Id])] = x match {
         case x: tpd.Super => Some((x.qual, if (x.mix.isEmpty) None else Some(x.mix)))
@@ -269,6 +332,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsTyped extends IsTypedModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Typed] = x match {
+        case x: tpd.Typed => Some(x)
+        case _ => None
+      }
+    }
+
     object Typed extends TypedExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, TypeTree)] = x match {
         case x: tpd.Typed => Some((x.expr, x.tpt))
@@ -276,6 +346,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsAssign extends IsAssignModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Assign] = x match {
+        case x: tpd.Assign => Some(x)
+        case _ => None
+      }
+    }
+
     object Assign extends AssignExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match {
         case x: tpd.Assign => Some((x.lhs, x.rhs))
@@ -283,6 +360,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsBlock extends IsBlockModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Block] = Block.normalizedLoops(x) match {
+        case x: tpd.Block => Some(x)
+        case _ => None
+      }
+    }
+
     object Block extends BlockExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = normalizedLoops(x) match {
         case Trees.Block(stats, expr) => Some((stats, expr))
@@ -292,7 +376,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
        *  i) Put `while` and `doWhile` loops in their own blocks: `{ def while$() = ...; while$() }`
        *  ii) Put closures in their own blocks: `{ def anon$() = ...; closure(anon$, ...) }`
        */
-      private def normalizedLoops(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
+      private[Term] def normalizedLoops(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
         case block: tpd.Block if block.stats.size > 1 =>
           def normalizeInnerLoops(stats: List[tpd.Tree]): List[tpd.Tree] = stats match {
             case (x: tpd.DefDef) :: y :: xs if needsNormalization(y) =>
@@ -318,6 +402,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsInlined extends IsInlinedModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Inlined] = x match {
+        case x: tpd.Inlined => Some(x)
+        case _ => None
+      }
+    }
+
     object Inlined extends InlinedExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Option[TermOrTypeTree], List[Statement], Term)] = x match {
         case x: tpd.Inlined =>
@@ -326,6 +417,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsLambda extends IsLambdaModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Lambda] = x match {
+        case x: tpd.Closure => Some(x)
+        case _ => None
+      }
+    }
+
     object Lambda extends LambdaExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[TypeTree])] = x match {
         case x: tpd.Closure => Some((x.meth, optional(x.tpt)))
@@ -333,6 +431,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsIf extends IsIfModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[If] = x match {
+        case x: tpd.If => Some(x)
+        case _ => None
+      }
+    }
+
     object If extends IfExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term, Term)] = x match {
         case x: tpd.If => Some((x.cond, x.thenp, x.elsep))
@@ -340,6 +445,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsMatch extends IsMatchModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Match] = x match {
+        case x: tpd.Match => Some(x)
+        case _ => None
+      }
+    }
+
     object Match extends MatchExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef])] = x match {
         case x: tpd.Match => Some((x.selector, x.cases))
@@ -347,6 +459,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsTry extends IsTryModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Try] = x match {
+        case x: tpd.Try => Some(x)
+        case _ => None
+      }
+    }
+
     object Try extends TryExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef], Option[Term])] = x match {
         case x: tpd.Try => Some((x.expr, x.cases, optional(x.finalizer)))
@@ -354,6 +473,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsReturn extends IsReturnModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Return] = x match {
+        case x: tpd.Return => Some(x)
+        case _ => None
+      }
+    }
+
     object Return extends ReturnExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[Term] = x match {
         case x: tpd.Return => Some(x.expr)
@@ -361,6 +487,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsRepeated extends IsRepeatedModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[Repeated] = x match {
+        case x: tpd.SeqLiteral => Some(x)
+        case _ => None
+      }
+    }
+
     object Repeated extends RepeatedExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[List[Term]] = x match {
         case x: tpd.SeqLiteral => Some(x.elems)
@@ -368,6 +501,17 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsSelectOuter extends IsSelectOuterModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[SelectOuter] = x match {
+        case x: tpd.Select =>
+          x.name match {
+            case NameKinds.OuterSelectName(_, _) => Some(x)
+            case _ => None
+          }
+        case _ => None
+      }
+    }
+
     object SelectOuter extends SelectOuterExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Int, Type)] = x match {
         case x: tpd.Select =>
@@ -379,6 +523,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object IsWhile extends IsWhileModule {
+      def unapply(x: Term)(implicit ctx: Context): Option[While] = x match {
+        case x: tpd.WhileDo => Some(x)
+        case _ => None
+      }
+    }
+
     object While extends WhileExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match {
         case x: tpd.WhileDo => Some((x.cond, x.body))
diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala
index e53b9ae61d34..a398ed45c258 100644
--- a/library/src/scala/tasty/reflect/TreeOps.scala
+++ b/library/src/scala/tasty/reflect/TreeOps.scala
@@ -11,8 +11,8 @@ trait TreeOps extends Core {
   }
   implicit def TreeDeco(tree: Tree): TreeAPI
 
-  val IsPackageClause: IsPackageClauseExtractor
-  abstract class IsPackageClauseExtractor {
+  val IsPackageClause: IsPackageClauseModule
+  abstract class IsPackageClauseModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[PackageClause]
   }
 
@@ -41,8 +41,8 @@ trait TreeOps extends Core {
 
   // ----- Definitions ----------------------------------------------
 
-  val IsDefinition: IsDefinitionExtractor
-  abstract class IsDefinitionExtractor {
+  val IsDefinition: IsDefinitionModule
+  abstract class IsDefinitionModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[Definition]
   }
 
@@ -53,8 +53,8 @@ trait TreeOps extends Core {
 
   // ClassDef
 
-  val IsClassDef: IsClassDefExtractor
-  abstract class IsClassDefExtractor {
+  val IsClassDef: IsClassDefModule
+  abstract class IsClassDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[ClassDef]
   }
 
@@ -75,8 +75,8 @@ trait TreeOps extends Core {
 
   // DefDef
 
-  val IsDefDef: IsDefDefExtractor
-  abstract class IsDefDefExtractor {
+  val IsDefDef: IsDefDefModule
+  abstract class IsDefDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[DefDef]
   }
 
@@ -97,8 +97,8 @@ trait TreeOps extends Core {
 
   // ValDef
 
-  val IsValDef: IsValDefExtractor
-  abstract class IsValDefExtractor {
+  val IsValDef: IsValDefModule
+  abstract class IsValDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[ValDef]
   }
 
@@ -117,8 +117,8 @@ trait TreeOps extends Core {
 
   // TypeDef
 
-  val IsTypeDef: IsTypeDefExtractor
-  abstract class IsTypeDefExtractor {
+  val IsTypeDef: IsTypeDefModule
+  abstract class IsTypeDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[TypeDef]
   }
 
@@ -135,8 +135,8 @@ trait TreeOps extends Core {
 
   // PackageDef
 
-  val IsPackageDef: IsPackageDefExtractor
-  abstract class IsPackageDefExtractor {
+  val IsPackageDef: IsPackageDefModule
+  abstract class IsPackageDefModule {
     def unapply(tree: Tree)(implicit ctx: Context): Option[PackageDef]
   }
 
@@ -162,8 +162,8 @@ trait TreeOps extends Core {
   }
   implicit def TermDeco(term: Term): TermAPI
 
-  val IsTerm: IsTermExtractor
-  abstract class IsTermExtractor {
+  val IsTerm: IsTermModule
+  abstract class IsTermModule {
     /** Matches any term */
     def unapply(tree: Tree)(implicit ctx: Context): Option[Term]
     /** Matches any term */
@@ -174,6 +174,12 @@ trait TreeOps extends Core {
   val Term: TermModule
   abstract class TermModule extends TermCoreModule {
 
+    val IsIdent: IsIdentModule
+    abstract class IsIdentModule {
+      /** Matches any Ident and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Ident]
+    }
+
     /** Scala term identifier */
     val Ident: IdentExtractor
     abstract class IdentExtractor {
@@ -181,6 +187,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[String]
     }
 
+    val IsSelect: IsSelectModule
+    abstract class IsSelectModule {
+      /** Matches any Select and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Select]
+    }
+
     /** Scala term selection */
     val Select: SelectExtractor
     abstract class SelectExtractor {
@@ -188,12 +200,24 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, String, Option[Signature])]
     }
 
+    val IsLiteral: IsLiteralModule
+    abstract class IsLiteralModule {
+      /** Matches any Literal and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Literal]
+    }
+
     /** Scala literal constant */
     val Literal: LiteralExtractor
     abstract class LiteralExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Constant]
     }
 
+    val IsThis: IsThisModule
+    abstract class IsThisModule {
+      /** Matches any This and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[This]
+    }
+
     /** Scala `this` or `this[id]` */
     val This: ThisExtractor
     abstract class ThisExtractor {
@@ -201,6 +225,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Option[Id]]
     }
 
+    val IsNew: IsNewModule
+    abstract class IsNewModule {
+      /** Matches any New and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[New]
+    }
+
     /** Scala `new` */
     val New: NewExtractor
     abstract class NewExtractor {
@@ -208,6 +238,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[TypeTree]
     }
 
+    val IsNamedArg: IsNamedArgModule
+    abstract class IsNamedArgModule {
+      /** Matches any NamedArg and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[NamedArg]
+    }
+
     /** Scala named argument `x = y` in argument position */
     val NamedArg: NamedArgExtractor
     abstract class NamedArgExtractor {
@@ -215,6 +251,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(String, Term)]
     }
 
+    val IsApply: IsApplyModule
+    abstract class IsApplyModule {
+      /** Matches any Apply and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Apply]
+    }
+
     /** Scala parameter application */
     val Apply: ApplyExtractor
     abstract class ApplyExtractor {
@@ -222,6 +264,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, List[Term])]
     }
 
+    val IsTypeApply: IsTypeApplyModule
+    abstract class IsTypeApplyModule {
+      /** Matches any TypeApply and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[TypeApply]
+    }
+
     /** Scala type parameter application */
     val TypeApply: TypeApplyExtractor
     abstract class TypeApplyExtractor {
@@ -229,6 +277,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, List[TypeTree])]
     }
 
+    val IsSuper: IsSuperModule
+    abstract class IsSuperModule {
+      /** Matches any Super and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Super]
+    }
+
     /** Scala `x.super` or `x.super[id]` */
     val Super: SuperExtractor
     abstract class SuperExtractor {
@@ -236,6 +290,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, Option[Id])]
     }
 
+    val IsTyped: IsTypedModule
+    abstract class IsTypedModule {
+      /** Matches any Typed and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Typed]
+    }
+
     /** Scala ascription `x: T` */
     val Typed: TypedExtractor
     abstract class TypedExtractor {
@@ -243,6 +303,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, TypeTree)]
     }
 
+    val IsAssign: IsAssignModule
+    abstract class IsAssignModule {
+      /** Matches any Assign and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Assign]
+    }
+
     /** Scala assign `x = y` */
     val Assign: AssignExtractor
     abstract class AssignExtractor {
@@ -250,6 +316,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, Term)]
     }
 
+    val IsBlock: IsBlockModule
+    abstract class IsBlockModule {
+      /** Matches any Block and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Block]
+    }
+
     /** Scala code block `{ stat0; ...; statN; expr }` term */
     val Block: BlockExtractor
     abstract class BlockExtractor {
@@ -257,11 +329,23 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(List[Statement], Term)]
     }
 
+    val IsLambda: IsLambdaModule
+    abstract class IsLambdaModule {
+      /** Matches any Lambda and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Lambda]
+    }
+
     val Lambda: LambdaExtractor
     abstract class LambdaExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, Option[TypeTree])]
     }
 
+    val IsIf: IsIfModule
+    abstract class IsIfModule {
+      /** Matches any If and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[If]
+    }
+
     /** Scala `if`/`else` term */
     val If: IfExtractor
     abstract class IfExtractor {
@@ -269,6 +353,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, Term, Term)]
     }
 
+    val IsMatch: IsMatchModule
+    abstract class IsMatchModule {
+      /** Matches any Match and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Match]
+    }
+
     /** Scala `match` term */
     val Match: MatchExtractor
     abstract class MatchExtractor {
@@ -276,6 +366,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, List[CaseDef])]
     }
 
+    val IsTry: IsTryModule
+    abstract class IsTryModule {
+      /** Matches any Try and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Try]
+    }
+
     /** Scala `try`/`catch`/`finally` term */
     val Try: TryExtractor
     abstract class TryExtractor {
@@ -283,6 +379,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, List[CaseDef], Option[Term])]
     }
 
+    val IsReturn: IsReturnModule
+    abstract class IsReturnModule {
+      /** Matches any Return and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Return]
+    }
+
     /** Scala local `return` */
     val Return: ReturnExtractor
     abstract class ReturnExtractor {
@@ -290,21 +392,45 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Term]
     }
 
+    val IsRepeated: IsRepeatedModule
+    abstract class IsRepeatedModule {
+      /** Matches any Repeated and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Repeated]
+    }
+
     val Repeated: RepeatedExtractor
     abstract class RepeatedExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[List[Term]]
     }
 
+    val IsInlined: IsInlinedModule
+    abstract class IsInlinedModule {
+      /** Matches any Inlined and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[Inlined]
+    }
+
     val Inlined: InlinedExtractor
     abstract class InlinedExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Option[TermOrTypeTree], List[Definition], Term)]
     }
 
+    val IsSelectOuter: IsSelectOuterModule
+    abstract class IsSelectOuterModule {
+      /** Matches any SelectOuter and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[SelectOuter]
+    }
+
     val SelectOuter: SelectOuterExtractor
     abstract class SelectOuterExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, Int, Type)]
     }
 
+    val IsWhile: IsWhileModule
+    abstract class IsWhileModule {
+      /** Matches any While and returns it */
+      def unapply(tree: Tree)(implicit ctx: Context): Option[While]
+    }
+
     val While: WhileExtractor
     abstract class WhileExtractor {
       /** Extractor for while loops. Matches `while (<cond>) <body>` and returns (<cond>, <body>) */

From 4c9035019d92e27dd72063db3d3e3d5cd134413b Mon Sep 17 00:00:00 2001
From: Nicolas Stucki <nicolas.stucki@gmail.com>
Date: Fri, 23 Nov 2018 11:32:26 +0100
Subject: [PATCH 2/3] Avoid duplicated checks for blocks

---
 .../tools/dotc/tastyreflect/TreeOpsImpl.scala   | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
index 67b77dbaa9b5..0e987ebdd819 100644
--- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
+++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
@@ -361,22 +361,16 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
     }
 
     object IsBlock extends IsBlockModule {
-      def unapply(x: Term)(implicit ctx: Context): Option[Block] = Block.normalizedLoops(x) match {
+      def unapply(x: Term)(implicit ctx: Context): Option[Block] = normalizedLoops(x) match {
         case x: tpd.Block => Some(x)
         case _ => None
       }
-    }
 
-    object Block extends BlockExtractor {
-      def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = normalizedLoops(x) match {
-        case Trees.Block(stats, expr) => Some((stats, expr))
-        case _ => None
-      }
       /** Normalizes non Blocks.
        *  i) Put `while` and `doWhile` loops in their own blocks: `{ def while$() = ...; while$() }`
        *  ii) Put closures in their own blocks: `{ def anon$() = ...; closure(anon$, ...) }`
        */
-      private[Term] def normalizedLoops(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
+      private def normalizedLoops(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match {
         case block: tpd.Block if block.stats.size > 1 =>
           def normalizeInnerLoops(stats: List[tpd.Tree]): List[tpd.Tree] = stats match {
             case (x: tpd.DefDef) :: y :: xs if needsNormalization(y) =>
@@ -402,6 +396,13 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    object Block extends BlockExtractor {
+      def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = x match {
+        case IsBlock(x) => Some((x.stats, x.expr))
+        case _ => None
+      }
+    }
+
     object IsInlined extends IsInlinedModule {
       def unapply(x: Term)(implicit ctx: Context): Option[Inlined] = x match {
         case x: tpd.Inlined => Some(x)

From d6d223639f9b02277adcce269c90706a696f905f Mon Sep 17 00:00:00 2001
From: Nicolas Stucki <nicolas.stucki@gmail.com>
Date: Fri, 23 Nov 2018 13:40:48 +0100
Subject: [PATCH 3/3] Add missing Term extension methods

---
 .../tools/dotc/tastyreflect/TreeOpsImpl.scala | 117 +++++++++++++++-
 library/src/scala/tasty/reflect/TreeOps.scala | 129 +++++++++++++++++-
 2 files changed, 241 insertions(+), 5 deletions(-)

diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
index 0e987ebdd819..e4b701bdf20f 100644
--- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
+++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala
@@ -1,6 +1,6 @@
 package dotty.tools.dotc.tastyreflect
 
-import dotty.tools.dotc.ast.{Trees, tpd}
+import dotty.tools.dotc.ast.{Trees, tpd, untpd}
 import dotty.tools.dotc.core
 import dotty.tools.dotc.core.Decorators._
 import dotty.tools.dotc.core._
@@ -79,9 +79,9 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
   def ClassDefDeco(cdef: ClassDef): ClassDefAPI = new ClassDefAPI {
     private def rhs = cdef.rhs.asInstanceOf[tpd.Template]
     def constructor(implicit ctx: Context): DefDef = rhs.constr
-    def parents(implicit ctx: Context): List[tpd.Tree] = rhs.parents
+    def parents(implicit ctx: Context): List[TermOrTypeTree] = rhs.parents
     def self(implicit ctx: Context): Option[tpd.ValDef] = optional(rhs.self)
-    def body(implicit ctx: Context): List[tpd.Tree] = rhs.body
+    def body(implicit ctx: Context): List[Statement] = rhs.body
     def symbol(implicit ctx: Context): ClassSymbol = cdef.symbol.asClass
   }
 
@@ -209,6 +209,10 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def IdentDeco(x: Ident): IdentAPI = new IdentAPI {
+      def name(implicit ctx: Context): String = x.name.show
+    }
+
     object Ident extends IdentExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[String] = x match {
         case x: tpd.Ident if x.isTerm => Some(x.name.show)
@@ -223,6 +227,14 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def SelectDeco(x: Select): SelectAPI = new SelectAPI {
+      def qualifier(implicit ctx: Context): Term = x.qualifier
+      def name(implicit ctx: Context): String = x.name.toString
+      def signature(implicit ctx: Context): Option[Signature] =
+        if (x.symbol.signature == core.Signature.NotAMethod) None
+        else Some(x.symbol.signature)
+    }
+
     object Select extends SelectExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, String, Option[Signature])] = x match {
         case x: tpd.Select if x.isTerm =>
@@ -241,6 +253,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+
+    def LiteralDeco(x: Literal): LiteralAPI = new LiteralAPI {
+      def constant(implicit ctx: Context): Constant = x.const
+    }
+
     object Literal extends LiteralExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[Constant] = x match {
         case Trees.Literal(const) => Some(const)
@@ -255,6 +272,10 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def ThisDeco(x: This): ThisAPI = new ThisAPI {
+      def id(implicit ctx: Context): Option[Id] = optional(x.qual)
+    }
+
     object This extends ThisExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[Option[Id]] = x match {
         case Trees.This(qual) => Some(optional(qual))
@@ -269,6 +290,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+
+    def NewDeco(x: New): Term.NewAPI = new NewAPI {
+      def tpt(implicit ctx: Context): TypeTree = x.tpt
+    }
+
     object New extends NewExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[TypeTree] = x match {
         case x: tpd.New => Some(x.tpt)
@@ -283,6 +309,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def NamedArgDeco(x: NamedArg): NamedArgAPI = new NamedArgAPI {
+      def name(implicit ctx: Context): String = x.name.toString
+      def value(implicit ctx: Context): Term = x.arg
+    }
+
     object NamedArg extends NamedArgExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(String, Term)] = x match {
         case x: tpd.NamedArg if x.name.isInstanceOf[Names.TermName] => Some((x.name.toString, x.arg))
@@ -297,6 +328,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def ApplyDeco(x: Apply): ApplyAPI = new ApplyAPI {
+      def fun(implicit ctx: Context): Term = x.fun
+      def args(implicit ctx: Context): List[Term] = x.args
+    }
+
     object Apply extends ApplyExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[Term])] = x match {
         case x: tpd.Apply => Some((x.fun, x.args))
@@ -311,6 +347,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def TypeApplyDeco(x: TypeApply): TypeApplyAPI = new TypeApplyAPI {
+      def fun(implicit ctx: Context): Term = x.fun
+      def args(implicit ctx: Context): List[TypeTree] = x.args
+    }
+
     object TypeApply extends TypeApplyExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[TypeTree])] = x match {
         case x: tpd.TypeApply => Some((x.fun, x.args))
@@ -325,6 +366,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def SuperDeco(x: Super): SuperAPI = new SuperAPI {
+      def qualifier(implicit ctx: Context): Term = x.qual
+      def id(implicit ctx: Context): Option[untpd.Ident] = optional(x.mix)
+    }
+
     object Super extends SuperExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[Id])] = x match {
         case x: tpd.Super => Some((x.qual, if (x.mix.isEmpty) None else Some(x.mix)))
@@ -339,6 +385,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def TypedDeco(x: Typed): TypedAPI = new TypedAPI {
+      def expr(implicit ctx: Context): Term = x.expr
+      def tpt(implicit ctx: Context): TypeTree = x.tpt
+    }
+
     object Typed extends TypedExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, TypeTree)] = x match {
         case x: tpd.Typed => Some((x.expr, x.tpt))
@@ -353,6 +404,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def AssignDeco(x: Assign): AssignAPI = new AssignAPI {
+      def lhs(implicit ctx: Context): Term = x.lhs
+      def rhs(implicit ctx: Context): Term = x.rhs
+    }
+
     object Assign extends AssignExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match {
         case x: tpd.Assign => Some((x.lhs, x.rhs))
@@ -396,6 +452,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def BlockDeco(x: Block): BlockAPI = new BlockAPI {
+      def statements(implicit ctx: Context): List[Statement] = x.stats
+      def expr(implicit ctx: Context): Term = x.expr
+    }
+
     object Block extends BlockExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(List[Statement], Term)] = x match {
         case IsBlock(x) => Some((x.stats, x.expr))
@@ -410,6 +471,12 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def InlinedDeco(x: Inlined): InlinedAPI = new InlinedAPI {
+      def call(implicit ctx: Context): Option[Term] = optional(x.call)
+      def bindings(implicit ctx: Context): List[Definition] = x.bindings
+      def body(implicit ctx: Context): Term = x.expansion
+    }
+
     object Inlined extends InlinedExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Option[TermOrTypeTree], List[Statement], Term)] = x match {
         case x: tpd.Inlined =>
@@ -425,6 +492,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def LambdaDeco(x: Lambda): LambdaAPI = new LambdaAPI {
+      def meth(implicit ctx: Context): Term = x.meth
+      def tptOpt(implicit ctx: Context): Option[TypeTree] = optional(x.tpt)
+    }
+
     object Lambda extends LambdaExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Option[TypeTree])] = x match {
         case x: tpd.Closure => Some((x.meth, optional(x.tpt)))
@@ -439,6 +511,12 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def IfDeco(x: If): IfAPI = new IfAPI {
+      def cond(implicit ctx: Context): Term = x.cond
+      def thenp(implicit ctx: Context): Term = x.thenp
+      def elsep(implicit ctx: Context): Term = x.elsep
+    }
+
     object If extends IfExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term, Term)] = x match {
         case x: tpd.If => Some((x.cond, x.thenp, x.elsep))
@@ -453,6 +531,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def MatchDeco(x: Match): MatchAPI = new MatchAPI {
+      def scrutinee(implicit ctx: Context): Term = x.selector
+      def cases(implicit ctx: Context): List[tpd.CaseDef] = x.cases
+    }
+
     object Match extends MatchExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef])] = x match {
         case x: tpd.Match => Some((x.selector, x.cases))
@@ -467,6 +550,12 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def TryDeco(x: Try): TryAPI = new TryAPI {
+      def body(implicit ctx: Context): Term = x.expr
+      def cases(implicit ctx: Context): List[CaseDef] = x.cases
+      def finalizer(implicit ctx: Context): Option[Term] = optional(x.finalizer)
+    }
+
     object Try extends TryExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, List[CaseDef], Option[Term])] = x match {
         case x: tpd.Try => Some((x.expr, x.cases, optional(x.finalizer)))
@@ -481,6 +570,10 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def ReturnDeco(x: Return): ReturnAPI = new ReturnAPI {
+      def expr(implicit ctx: Context): Term = x.expr
+    }
+
     object Return extends ReturnExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[Term] = x match {
         case x: tpd.Return => Some(x.expr)
@@ -495,6 +588,10 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def RepeatedDeco(x: Repeated): RepeatedAPI = new RepeatedAPI {
+      def elems(implicit ctx: Context): List[Term] = x.elems
+    }
+
     object Repeated extends RepeatedExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[List[Term]] = x match {
         case x: tpd.SeqLiteral => Some(x.elems)
@@ -513,6 +610,15 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def SelectOuterDeco(x: SelectOuter): SelectOuterAPI = new SelectOuterAPI {
+      def qualifier(implicit ctx: Context): Term = x.qualifier
+      def level(implicit ctx: Context): Int = {
+        val NameKinds.OuterSelectName(_, levels) = x.name
+        levels
+      }
+      def tpe(implicit ctx: Context): Type = x.tpe.stripTypeVar
+    }
+
     object SelectOuter extends SelectOuterExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Int, Type)] = x match {
         case x: tpd.Select =>
@@ -531,6 +637,11 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers
       }
     }
 
+    def WhileDeco(x: While): WhileAPI = new WhileAPI {
+      def cond(implicit ctx: Context): Term = x.cond
+      def body(implicit ctx: Context): Term = x.body
+    }
+
     object While extends WhileExtractor {
       def unapply(x: Term)(implicit ctx: Context): Option[(Term, Term)] = x match {
         case x: tpd.WhileDo => Some((x.cond, x.body))
diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala
index a398ed45c258..a9d6ec7efed5 100644
--- a/library/src/scala/tasty/reflect/TreeOps.scala
+++ b/library/src/scala/tasty/reflect/TreeOps.scala
@@ -180,6 +180,11 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Ident]
     }
 
+    trait IdentAPI {
+      def name(implicit ctx: Context): String
+    }
+    implicit def IdentDeco(ident: Ident): IdentAPI
+
     /** Scala term identifier */
     val Ident: IdentExtractor
     abstract class IdentExtractor {
@@ -193,10 +198,17 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Select]
     }
 
+    trait SelectAPI {
+      def qualifier(implicit ctx: Context): Term
+      def name(implicit ctx: Context): String
+      def signature(implicit ctx: Context): Option[Signature]
+    }
+    implicit def SelectDeco(select: Select): SelectAPI
+
     /** Scala term selection */
     val Select: SelectExtractor
     abstract class SelectExtractor {
-      /** Matches `<qual: Term>.<name: String>: <sig: Signature>` */
+      /** Matches `<qual: Term>.<name: String>: <sig: Option[Signature]>` */
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, String, Option[Signature])]
     }
 
@@ -206,6 +218,11 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Literal]
     }
 
+    trait LiteralAPI {
+      def constant(implicit ctx: Context): Constant
+    }
+    implicit def LiteralDeco(x: Literal): LiteralAPI
+
     /** Scala literal constant */
     val Literal: LiteralExtractor
     abstract class LiteralExtractor {
@@ -218,6 +235,11 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[This]
     }
 
+    trait ThisAPI {
+      def id(implicit ctx: Context): Option[Id]
+    }
+    implicit def ThisDeco(x: This): ThisAPI
+
     /** Scala `this` or `this[id]` */
     val This: ThisExtractor
     abstract class ThisExtractor {
@@ -231,6 +253,11 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[New]
     }
 
+    trait NewAPI {
+      def tpt(implicit ctx: Context): TypeTree
+    }
+    implicit def NewDeco(x: New): NewAPI
+
     /** Scala `new` */
     val New: NewExtractor
     abstract class NewExtractor {
@@ -244,6 +271,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[NamedArg]
     }
 
+    trait NamedArgAPI {
+      def name(implicit ctx: Context): String
+      def value(implicit ctx: Context): Term
+    }
+    implicit def NamedArgDeco(x: NamedArg): NamedArgAPI
+
     /** Scala named argument `x = y` in argument position */
     val NamedArg: NamedArgExtractor
     abstract class NamedArgExtractor {
@@ -257,6 +290,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Apply]
     }
 
+    trait ApplyAPI {
+      def fun(implicit ctx: Context): Term
+      def args(implicit ctx: Context): List[Term]
+    }
+    implicit def ApplyDeco(x: Apply): ApplyAPI
+
     /** Scala parameter application */
     val Apply: ApplyExtractor
     abstract class ApplyExtractor {
@@ -270,6 +309,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[TypeApply]
     }
 
+    trait TypeApplyAPI {
+      def fun(implicit ctx: Context): Term
+      def args(implicit ctx: Context): List[TypeTree]
+    }
+    implicit def TypeApplyDeco(x: TypeApply): TypeApplyAPI
+
     /** Scala type parameter application */
     val TypeApply: TypeApplyExtractor
     abstract class TypeApplyExtractor {
@@ -283,6 +328,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Super]
     }
 
+    trait SuperAPI {
+      def qualifier(implicit ctx: Context): Term
+      def id(implicit ctx: Context): Option[Id]
+    }
+    implicit def SuperDeco(x: Super): SuperAPI
+
     /** Scala `x.super` or `x.super[id]` */
     val Super: SuperExtractor
     abstract class SuperExtractor {
@@ -296,10 +347,16 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Typed]
     }
 
+    trait TypedAPI {
+      def expr(implicit ctx: Context): Term
+      def tpt(implicit ctx: Context): Term
+    }
+    implicit def TypedDeco(x: Typed): TypedAPI
+
     /** Scala ascription `x: T` */
     val Typed: TypedExtractor
     abstract class TypedExtractor {
-      /** Matches `<x: Term>: <tpt: Term>` */
+      /** Matches `<expr: Term>: <tpt: Term>` */
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, TypeTree)]
     }
 
@@ -309,6 +366,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Assign]
     }
 
+    trait AssignAPI {
+      def lhs(implicit ctx: Context): Term
+      def rhs(implicit ctx: Context): Term
+    }
+    implicit def AssignDeco(x: Assign): AssignAPI
+
     /** Scala assign `x = y` */
     val Assign: AssignExtractor
     abstract class AssignExtractor {
@@ -322,6 +385,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Block]
     }
 
+    trait BlockAPI {
+      def statements(implicit ctx: Context): List[Statement]
+      def expr(implicit ctx: Context): Term
+    }
+    implicit def BlockDeco(x: Block): BlockAPI
+
     /** Scala code block `{ stat0; ...; statN; expr }` term */
     val Block: BlockExtractor
     abstract class BlockExtractor {
@@ -335,6 +404,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Lambda]
     }
 
+    trait LambdaAPI {
+      def meth(implicit ctx: Context): Term
+      def tptOpt(implicit ctx: Context): Option[TypeTree]
+    }
+    implicit def LambdaDeco(x: Lambda): LambdaAPI
+
     val Lambda: LambdaExtractor
     abstract class LambdaExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, Option[TypeTree])]
@@ -346,6 +421,13 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[If]
     }
 
+    trait IfAPI {
+      def cond(implicit ctx: Context): Term
+      def thenp(implicit ctx: Context): Term
+      def elsep(implicit ctx: Context): Term
+    }
+    implicit def IfDeco(x: If): IfAPI
+
     /** Scala `if`/`else` term */
     val If: IfExtractor
     abstract class IfExtractor {
@@ -359,6 +441,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Match]
     }
 
+    trait MatchAPI {
+      def scrutinee(implicit ctx: Context): Term
+      def cases(implicit ctx: Context): List[CaseDef]
+    }
+    implicit def MatchDeco(x: Match): MatchAPI
+
     /** Scala `match` term */
     val Match: MatchExtractor
     abstract class MatchExtractor {
@@ -372,6 +460,13 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Try]
     }
 
+    trait TryAPI {
+      def body(implicit ctx: Context): Term
+      def cases(implicit ctx: Context): List[CaseDef]
+      def finalizer(implicit ctx: Context): Option[Term]
+    }
+    implicit def TryDeco(x: Try): TryAPI
+
     /** Scala `try`/`catch`/`finally` term */
     val Try: TryExtractor
     abstract class TryExtractor {
@@ -385,6 +480,11 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Return]
     }
 
+    trait ReturnAPI {
+      def expr(implicit ctx: Context): Term
+    }
+    implicit def ReturnDeco(x: Return): ReturnAPI
+
     /** Scala local `return` */
     val Return: ReturnExtractor
     abstract class ReturnExtractor {
@@ -398,6 +498,11 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Repeated]
     }
 
+    trait RepeatedAPI {
+      def elems(implicit ctx: Context): List[Term]
+    }
+    implicit def RepeatedDeco(x: Repeated): RepeatedAPI
+
     val Repeated: RepeatedExtractor
     abstract class RepeatedExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[List[Term]]
@@ -409,6 +514,13 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[Inlined]
     }
 
+    trait InlinedAPI {
+      def call(implicit ctx: Context): Option[TermOrTypeTree]
+      def bindings(implicit ctx: Context): List[Definition]
+      def body(implicit ctx: Context): Term
+    }
+    implicit def InlinedDeco(x: Inlined): InlinedAPI
+
     val Inlined: InlinedExtractor
     abstract class InlinedExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Option[TermOrTypeTree], List[Definition], Term)]
@@ -420,6 +532,13 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[SelectOuter]
     }
 
+    trait SelectOuterAPI {
+      def qualifier(implicit ctx: Context): Term
+      def level(implicit ctx: Context): Int
+      def tpe(implicit ctx: Context): Type
+    }
+    implicit def SelectOuterDeco(x: SelectOuter): SelectOuterAPI
+
     val SelectOuter: SelectOuterExtractor
     abstract class SelectOuterExtractor {
       def unapply(tree: Tree)(implicit ctx: Context): Option[(Term, Int, Type)]
@@ -431,6 +550,12 @@ trait TreeOps extends Core {
       def unapply(tree: Tree)(implicit ctx: Context): Option[While]
     }
 
+    trait WhileAPI {
+      def cond(implicit ctx: Context): Term
+      def body(implicit ctx: Context): Term
+    }
+    implicit def WhileDeco(x: While): WhileAPI
+
     val While: WhileExtractor
     abstract class WhileExtractor {
       /** Extractor for while loops. Matches `while (<cond>) <body>` and returns (<cond>, <body>) */