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

scala.tasty.Reflection.scala Maven / Gradle / Ivy

The newest version!
package scala.tasty

import scala.internal.tasty.CompilerInterface

import scala.quoted.QuoteContext
import scala.quoted.show.SyntaxHighlight
import scala.tasty.reflect._

/** TASTy Reflect Interface.
 *
 *
 */

/** TASTy Reflect Interface.
 *
 *  Provides all functionality related with AST based metaprogramming.
 *
 *  Type hierarchy
 *  ```none
 *
 *  +- Tree -+- PackageClause
 *           +- Import
 *           +- Statement -+- Definition --+- ClassDef
 *           |             |               +- TypeDef
 *           |             |               +- DefDef
 *           |             |               +- ValDef
 *           |             |
 *           |             +- Term --------+- Ref -+- Ident
 *           |                             |       +- Select
 *           |                             |
 *           |                             +- Literal
 *           |                             +- This
 *           |                             +- New
 *           |                             +- NamedArg
 *           |                             +- Apply
 *           |                             +- TypeApply
 *           |                             +- Super
 *           |                             +- Typed
 *           |                             +- Assign
 *           |                             +- Block
 *           |                             +- Closure
 *           |                             +- If
 *           |                             +- Match
 *           |                             +- GivenMatch
 *           |                             +- Try
 *           |                             +- Return
 *           |                             +- Repeated
 *           |                             +- Inlined
 *           |                             +- SelectOuter
 *           |                             +- While
 *           |
 *           |
 *           +- TypeTree ----+- Inferred
 *           |               +- TypeIdent
 *           |               +- TypeSelect
 *           |               +- Projection
 *           |               +- Singleton
 *           |               +- Refined
 *           |               +- Applied
 *           |               +- Annotated
 *           |               +- MatchTypeTree
 *           |               +- ByName
 *           |               +- LambdaTypeTree
 *           |               +- TypeBind
 *           |               +- TypeBlock
 *           |
 *           +- TypeBoundsTree
 *           +- WildcardTypeTree
 *           |
 *           +- CaseDef
 *           |
 *           +- TypeCaseDef
 *           +- Bind
 *           +- Unapply
 *           +- Alternatives
 *
 *
 *  +- Type -+- ConstantType
 *           +- TermRef
 *           +- TypeRef
 *           +- SuperType
 *           +- Refinement
 *           +- AppliedType
 *           +- AnnotatedType
 *           +- AndType
 *           +- OrType
 *           +- MatchType
 *           +- ByNameType
 *           +- ParamRef
 *           +- ThisType
 *           +- RecursiveThis
 *           +- RecursiveType
 *           +- LambdaType -+- MethodType
 *           |              +- PolyType
 *           |              +- TypeLambda
 *           +- TypeBounds
 *           +- NoPrefix
 *
 *  +- ImportSelector -+- SimpleSelector
 *                     +- RenameSelector
 *                     +- OmitSelector
 *
 *  +- Signature
 *
 *  +- Position
 *
 *  +- Documentation
 *
 *  +- Constant
 *
 *  +- Symbol
 *
 *  +- Flags
 *
 *  ```
 */
trait Reflection { reflection =>

  //////////////
  // CONTEXTS //
  //////////////

  /** Compilation context */
  type Context <: AnyRef

  /** Context of the macro expansion */
  def rootContext: Context // TODO: Should this be moved to QuoteContext?
  given Context = rootContext // TODO: Should be an implicit converion from QuoteContext to Context

  ///////////////
  //   TREES   //
  ///////////////


  /** Tree representing code written in the source */
  type Tree <: AnyRef

  val Tree: TreeModule

  trait TreeModule { this: Tree.type => }

  given TreeMethods as TreeMethods = TreeMethodsImpl
  protected val TreeMethodsImpl: TreeMethods

  trait TreeMethods {
    extension (self: Tree):
      /** Position in the source code */
      def pos: Position

      /** Symbol of defined or referred by this tree */
      def symbol: Symbol

      /** Shows the tree as extractors */
      def showExtractors: String

      /** Shows the tree as fully typed source code */
      def show: String

      /** Shows the tree as fully typed source code */
      def showWith(syntaxHighlight: SyntaxHighlight): String

      /** Does this tree represent a valid expression? */
      def isExpr: Boolean
    end extension

    /** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */
    extension [T](self: Tree)
      def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T]
  }

  /** Tree representing a pacakage clause in the source code */
  type PackageClause <: Tree

  given TypeTest[Tree, PackageClause] = PackageClauseTypeTest
  protected val PackageClauseTypeTest: TypeTest[Tree, PackageClause]

  val PackageClause: PackageClauseModule

  trait PackageClauseModule { this: PackageClause.type =>
    def apply(pid: Ref, stats: List[Tree]): PackageClause
    def copy(original: Tree)(pid: Ref, stats: List[Tree]): PackageClause
    def unapply(tree: PackageClause): Some[(Ref, List[Tree])]
  }

  given PackageClauseMethods as PackageClauseMethods = PackageClauseMethodsImpl
  protected val PackageClauseMethodsImpl: PackageClauseMethods

  trait PackageClauseMethods:
    extension (self: PackageClause):
      def pid: Ref
      def stats: List[Tree]
    end extension
  end PackageClauseMethods

  /** Tree representing an import in the source code */
  type Import <: Statement

  given TypeTest[Tree, Import] = ImportTypeTest
  protected val ImportTypeTest: TypeTest[Tree, Import]

  val Import: ImportModule

  trait ImportModule { this: Import.type =>
    def apply(expr: Term, selectors: List[ImportSelector]): Import
    def copy(original: Tree)(expr: Term, selectors: List[ImportSelector]): Import
    def unapply(tree: Import): Option[(Term, List[ImportSelector])]
  }

  given ImportMethods as ImportMethods = ImportMethodsImpl
  protected val ImportMethodsImpl: ImportMethods

  trait ImportMethods:
    extension (self: Import):
      def expr: Term
      def selectors: List[ImportSelector]
    end extension
  end ImportMethods

  /** Tree representing a statement in the source code */
  type Statement <: Tree

  given TypeTest[Tree, Statement] = StatementTypeTest
  protected val StatementTypeTest: TypeTest[Tree, Statement]

  // ----- Definitions ----------------------------------------------

  /** Tree representing a definition in the source code. It can be `ClassDef`, `TypeDef`, `DefDef` or `ValDef` */
  type Definition <: Statement

  given TypeTest[Tree, Definition] = DefinitionTypeTest
  protected val DefinitionTypeTest: TypeTest[Tree, Definition]

  val Definition: DefinitionModule

  trait DefinitionModule { this: Definition.type => }

  given DefinitionMethods as DefinitionMethods = DefinitionMethodsImpl
  protected val DefinitionMethodsImpl: DefinitionMethods

  trait DefinitionMethods:
    extension (self: Definition):
      def name: String
    end extension
  end DefinitionMethods

  // ClassDef

  /** Tree representing a class definition. This includes anonymous class definitions and the class of a module object */
  type ClassDef <: Definition

  given TypeTest[Tree, ClassDef] = ClassDefTypeTest
  protected val ClassDefTypeTest: TypeTest[Tree, ClassDef]

  val ClassDef: ClassDefModule

  trait ClassDefModule { this: ClassDef.type =>
    // TODO def apply(name: String, constr: DefDef, parents: List[TermOrTypeTree], selfOpt: Option[ValDef], body: List[Statement]): ClassDef
    def copy(original: Tree)(name: String, constr: DefDef, parents: List[Tree /* Term | TypeTree */], derived: List[TypeTree], selfOpt: Option[ValDef], body: List[Statement]): ClassDef
    def unapply(cdef: ClassDef): Option[(String, DefDef, List[Tree /* Term | TypeTree */], List[TypeTree], Option[ValDef], List[Statement])]
  }

  given ClassDefMethods as ClassDefMethods = ClassDefMethodsImpl
  protected val ClassDefMethodsImpl: ClassDefMethods

  trait ClassDefMethods:
    extension (self: ClassDef):
      def constructor: DefDef
      def parents: List[Tree /* Term | TypeTree */]
      def derived: List[TypeTree]
      def self: Option[ValDef]
      def body: List[Statement]
    end extension
  end ClassDefMethods

  // DefDef

  /** Tree representing a method definition in the source code */
  type DefDef <: Definition

  given TypeTest[Tree, DefDef] = DefDefTypeTest
  protected val DefDefTypeTest: TypeTest[Tree, DefDef]

  val DefDef: DefDefModule

  trait DefDefModule { this: DefDef.type =>
    def apply(symbol: Symbol, rhsFn: List[Type] => List[List[Term]] => Option[Term]): DefDef
    def copy(original: Tree)(name: String, typeParams: List[TypeDef], paramss: List[List[ValDef]], tpt: TypeTree, rhs: Option[Term]): DefDef
    def unapply(ddef: DefDef): Option[(String, List[TypeDef], List[List[ValDef]], TypeTree, Option[Term])]
  }

  given DefDefMethods as DefDefMethods = DefDefMethodsImpl
  protected val DefDefMethodsImpl: DefDefMethods

  trait DefDefMethods:
    extension (self: DefDef):
      def typeParams: List[TypeDef]
      def paramss: List[List[ValDef]]
      def returnTpt: TypeTree
      def rhs: Option[Term]
    end extension
  end DefDefMethods

  // ValDef

  /** Tree representing a value definition in the source code This inclues `val`, `lazy val`, `var`, `object` and parameter defintions. */
  type ValDef <: Definition

  given TypeTest[Tree, ValDef] = ValDefTypeTest
  protected val ValDefTypeTest: TypeTest[Tree, ValDef]

  val ValDef: ValDefModule

  trait ValDefModule { this: ValDef.type =>
    def apply(symbol: Symbol, rhs: Option[Term]): ValDef
    def copy(original: Tree)(name: String, tpt: TypeTree, rhs: Option[Term]): ValDef
    def unapply(vdef: ValDef): Option[(String, TypeTree, Option[Term])]
  }

  given ValDefMethods as ValDefMethods = ValDefMethodsImpl
  protected val ValDefMethodsImpl: ValDefMethods

  trait ValDefMethods:
    extension (self: ValDef):
      def tpt: TypeTree
      def rhs: Option[Term]
    end extension
  end ValDefMethods

  // TypeDef

  /** Tree representing a type (parameter or member) definition in the source code */
  type TypeDef <: Definition

  given TypeTest[Tree, TypeDef] = TypeDefTypeTest
  protected val TypeDefTypeTest: TypeTest[Tree, TypeDef]

  val TypeDef: TypeDefModule

  trait TypeDefModule { this: TypeDef.type =>
    def apply(symbol: Symbol): TypeDef
    def copy(original: Tree)(name: String, rhs: Tree /*TypeTree | TypeBoundsTree*/): TypeDef
    def unapply(tdef: TypeDef): Option[(String, Tree /*TypeTree | TypeBoundsTree*/ /* TypeTree | TypeBoundsTree */)]
  }

  given TypeDefMethods as TypeDefMethods = TypeDefMethodsImpl
  protected val TypeDefMethodsImpl: TypeDefMethods

  trait TypeDefMethods:
    extension (self: TypeDef):
      def rhs: Tree /*TypeTree | TypeBoundsTree*/
    end extension
  end TypeDefMethods


  // ----- Terms ----------------------------------------------------

  /** Tree representing an expression in the source code */
  type Term <: Statement

  given TypeTest[Tree, Term] = TermTypeTest
  protected val TermTypeTest: TypeTest[Tree, Term]

  val Term: TermModule

  trait TermModule { this: Term.type =>

    /** Returns a term that is functionally equivalent to `t`,
     *  however if `t` is of the form `((y1, ..., yn) => e2)(e1, ..., en)`
     *  then it optimizes this the top most call by returning the `Some`
     *  with the result of beta-reducing the application.
     *  Otherwise returns `None`.
     *
     *   To retain semantics the argument `ei` is bound as `val yi = ei` and by-name arguments to `def yi = ei`.
     *   Some bindings may be elided as an early optimization.
     */
    def betaReduce(term: Term): Option[Term]

  }

  given TermMethods as TermMethods = TermMethodsImpl
  protected val TermMethodsImpl: TermMethods

  trait TermMethods {
    extension (self: Term):

      /** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression or throws */
      def seal: scala.quoted.Expr[Any]

      /** Convert `Term` to an `quoted.Expr[Any]` if the term is a valid expression */
      def sealOpt: Option[scala.quoted.Expr[Any]]

      /** Type of this term */
      def tpe: Type

      /** Replace Inlined nodes and InlineProxy references to underlying arguments */
      def underlyingArgument: Term

      /** Replace Ident nodes references to the underlying tree that defined them */
      def underlying: Term

      /** Converts a partally applied term into a lambda expression */
      def etaExpand: Term

      /** A unary apply node with given argument: `tree(arg)` */
      def appliedTo(arg: Term): Term

      /** An apply node with given arguments: `tree(arg, args0, ..., argsN)` */
      def appliedTo(arg: Term, args: Term*): Term

      /** An apply node with given argument list `tree(args(0), ..., args(args.length - 1))` */
      def appliedToArgs(args: List[Term]): Apply

      /** The current tree applied to given argument lists:
      *  `tree (argss(0)) ... (argss(argss.length -1))`
      */
      def appliedToArgss(argss: List[List[Term]]): Term

      /** The current tree applied to (): `tree()` */
      def appliedToNone: Apply

      /** The current tree applied to given type argument: `tree[targ]` */
      def appliedToType(targ: Type): Term

      /** The current tree applied to given type arguments: `tree[targ0, ..., targN]` */
      def appliedToTypes(targs: List[Type]): Term

      /** The current tree applied to given type argument list: `tree[targs(0), ..., targs(targs.length - 1)]` */
      def appliedToTypeTrees(targs: List[TypeTree]): Term

      /** A select node that selects the given symbol. */
      def select(sym: Symbol): Select

    end extension

  }

  /** Tree representing a reference to definition */
  type Ref <: Term

  given TypeTest[Tree, Ref] = RefTypeTest
  protected val RefTypeTest: TypeTest[Tree, Ref]

  val Ref: RefModule

  trait RefModule { this: Ref.type =>

    /** A tree representing the same reference as the given type */
    def term(tp: TermRef): Ref

    /** Create a reference tree from a symbol
     *
     *  If `sym` refers to a class member `foo` in class `C`,
     *  returns a tree representing `C.this.foo`.
     *
     *  If `sym` refers to a local definition `foo`, returns
     *  a tree representing `foo`.
     *
     *  @note In both cases, the constructed tree should only
     *  be spliced into the places where such accesses make sense.
     *  For example, it is incorrect to have `C.this.foo` outside
     *  the class body of `C`, or have `foo` outside the lexical
     *  scope for the definition of `foo`.
     */
    def apply(sym: Symbol): Ref
  }

  /** Tree representing a reference to definition with a given name */
  type Ident <: Ref

  given TypeTest[Tree, Ident] = IdentTypeTest
  protected val IdentTypeTest: TypeTest[Tree, Ident]

  /** Scala term identifier */
  val Ident: IdentModule

  trait IdentModule { this: Ident.type =>
    def apply(tmref: TermRef): Term

    def copy(original: Tree)(name: String): Ident

    /** Matches a term identifier and returns its name */
    def unapply(tree: Ident): Option[String]
  }

  given IdentMethods as IdentMethods = IdentMethodsImpl
  protected val IdentMethodsImpl: IdentMethods

  trait IdentMethods:
    extension (self: Ident):
      def name: String
    end extension
  end IdentMethods

  /** Tree representing a selection of definition with a given name on a given prefix */
  type Select <: Ref

  given TypeTest[Tree, Select] = SelectTypeTest
  protected val SelectTypeTest: TypeTest[Tree, Select]

  /** Scala term selection */
  val Select: SelectModule

  trait SelectModule { this: Select.type =>
    /** Select a term member by symbol */
    def apply(qualifier: Term, symbol: Symbol): Select

    /** Select a field or a non-overloaded method by name
     *
     *  @note The method will produce an assertion error if the selected
     *        method is overloaded. The method `overloaded` should be used
     *        in that case.
     */
    def unique(qualifier: Term, name: String): Select

    // TODO rename, this returns an Apply and not a Select
    /** Call an overloaded method with the given type and term parameters */
    def overloaded(qualifier: Term, name: String, targs: List[Type], args: List[Term]): Apply

    def copy(original: Tree)(qualifier: Term, name: String): Select

    /** Matches `.` */
    def unapply(x: Select): Option[(Term, String)]
  }

  given SelectMethods as SelectMethods = SelectMethodsImpl
  protected val SelectMethodsImpl: SelectMethods

  trait SelectMethods:
    extension (self: Select):
      def qualifier: Term
      def name: String
      def signature: Option[Signature]
    end extension
  end SelectMethods

  given TypeTest[Tree, Literal] = LiteralTypeTest
  protected val LiteralTypeTest: TypeTest[Tree, Literal]

  /** Tree representing a literal value in the source code */
  type Literal <: Term

  /** Scala literal constant */
  val Literal: LiteralModule

  trait LiteralModule { this: Literal.type =>

    /** Create a literal constant */
    def apply(constant: Constant): Literal

    def copy(original: Tree)(constant: Constant): Literal

    /** Matches a literal constant */
    def unapply(x: Literal): Option[Constant]
  }

  given LiteralMethods as LiteralMethods = LiteralMethodsImpl
  protected val LiteralMethodsImpl: LiteralMethods

  trait LiteralMethods:
    extension (self: Literal):
      def constant: Constant
    end extension
  end LiteralMethods

  /** Tree representing `this` in the source code */
  type This <: Term

  given TypeTest[Tree, This] = ThisTypeTest
  protected val ThisTypeTest: TypeTest[Tree, This]

  /** Scala `this` or `this[id]` */
  val This: ThisModule

  trait ThisModule { this: This.type =>

    /** Create a `this[` */
    def apply(cls: Symbol): This

    def copy(original: Tree)(qual: Option[String]): This

    /** Matches `this[` */
    def unapply(x: This): Option[Option[String]]
  }

  given ThisMethods as ThisMethods = ThisMethodsImpl
  protected val ThisMethodsImpl: ThisMethods

  trait ThisMethods:
    extension (self: This):
      def id: Option[String]
    end extension
  end ThisMethods

  /** Tree representing `new` in the source code */
  type New <: Term

  given TypeTest[Tree, New] = NewTypeTest
  protected val NewTypeTest: TypeTest[Tree, New]

  /** Scala `new` */
  val New: NewModule

  trait NewModule { this: New.type =>

    /** Create a `new ` */
    def apply(tpt: TypeTree): New

    def copy(original: Tree)(tpt: TypeTree): New

    /** Matches a `new ` */
    def unapply(x: New): Option[TypeTree]
  }

  given NewMethods as NewMethods = NewMethodsImpl
  protected val NewMethodsImpl: NewMethods

  trait NewMethods:
    extension (self: New):
      def tpt: TypeTree
    end extension
  end NewMethods

  /** Tree representing an argument passed with an explicit name. Such as `arg1 = x` in `foo(arg1 = x)` */
  type NamedArg <: Term

  given TypeTest[Tree, NamedArg] = NamedArgTypeTest
  protected val NamedArgTypeTest: TypeTest[Tree, NamedArg]

  /** Scala named argument `x = y` in argument position */
  val NamedArg: NamedArgModule

  trait NamedArgModule { this: NamedArg.type =>

    /** Create a named argument ` = ` */
    def apply(name: String, arg: Term): NamedArg

    def copy(original: Tree)(name: String, arg: Term): NamedArg

    /** Matches a named argument ` = ` */
    def unapply(x: NamedArg): Option[(String, Term)]
  }

  given NamedArgMethods as NamedArgMethods = NamedArgMethodsImpl
  protected val NamedArgMethodsImpl: NamedArgMethods

  trait NamedArgMethods:
    extension (self: NamedArg):
      def name: String
      def value: Term
    end extension
  end NamedArgMethods

  /** Tree an application of arguments. It represents a single list of arguments, multiple argument lists will have nested `Apply`s  */
  type Apply <: Term

  given TypeTest[Tree, Apply] = ApplyTypeTest
  protected val ApplyTypeTest: TypeTest[Tree, Apply]

  /** Scala parameter application */
  val Apply: ApplyModule

  trait ApplyModule { this: Apply.type =>

    /** Create a function application `()` */
    def apply(fun: Term, args: List[Term]): Apply

    def copy(original: Tree)(fun: Term, args: List[Term]): Apply

    /** Matches a function application `()` */
    def unapply(x: Apply): Option[(Term, List[Term])]
  }

  given ApplyMethods as ApplyMethods = ApplyMethodsImpl
  protected val ApplyMethodsImpl: ApplyMethods

  trait ApplyMethods:
    extension (self: Apply):
      def fun: Term
      def args: List[Term]
    end extension
  end ApplyMethods

  /** Tree an application of type arguments */
  type TypeApply <: Term

  given TypeTest[Tree, TypeApply] = TypeApplyTypeTest
  protected val TypeApplyTypeTest: TypeTest[Tree, TypeApply]

  /** Scala type parameter application */
  val TypeApply: TypeApplyModule

  trait TypeApplyModule { this: TypeApply.type =>

    /** Create a function type application `[]` */
    def apply(fun: Term, args: List[TypeTree]): TypeApply

    def copy(original: Tree)(fun: Term, args: List[TypeTree]): TypeApply

    /** Matches a function type application `[]` */
    def unapply(x: TypeApply): Option[(Term, List[TypeTree])]
  }

  given TypeApplyMethods as TypeApplyMethods = TypeApplyMethodsImpl
  protected val TypeApplyMethodsImpl: TypeApplyMethods

  trait TypeApplyMethods:
    extension (self: TypeApply):
      def fun: Term
      def args: List[TypeTree]
    end extension
  end TypeApplyMethods

  given TypeTest[Tree, Super] = SuperTypeTest
  protected val SuperTypeTest: TypeTest[Tree, Super]

  /** Tree representing `super` in the source code */
  type Super <: Term

  /** Scala `x.super` or `x.super[id]` */
  val Super: SuperModule

  trait SuperModule { this: Super.type =>

    /** Creates a `.super[` */
    def apply(qual: Term, mix: Option[String]): Super

    def copy(original: Tree)(qual: Term, mix: Option[String]): Super

    /** Matches a `.super[` */
    def unapply(x: Super): Option[(Term, Option[String])]
  }

  given SuperMethods as SuperMethods = SuperMethodsImpl
  protected val SuperMethodsImpl: SuperMethods

  trait SuperMethods:
    extension (self: Super):
      def qualifier: Term
      def id: Option[String]
      def idPos: Position
    end extension
  end SuperMethods

  given TypeTest[Tree, Typed] = TypedTypeTest
  protected val TypedTypeTest: TypeTest[Tree, Typed]

  /** Tree representing a type ascription `x: T` in the source code */
  type Typed <: Term

  /** Scala ascription `x: T` */
  val Typed: TypedModule

  trait TypedModule { this: Typed.type =>

    /** Create a type ascription `: ` */
    def apply(expr: Term, tpt: TypeTree): Typed

    def copy(original: Tree)(expr: Term, tpt: TypeTree): Typed

    /** Matches `: ` */
    def unapply(x: Typed): Option[(Term, TypeTree)]
  }

  given TypedMethods as TypedMethods = TypedMethodsImpl
  protected val TypedMethodsImpl: TypedMethods

  trait TypedMethods:
    extension (self: Typed):
      def expr: Term
      def tpt: TypeTree
    end extension
  end TypedMethods

  /** Tree representing an assignment `x = y` in the source code */
  type Assign <: Term

  given TypeTest[Tree, Assign] = AssignTypeTest
  protected val AssignTypeTest: TypeTest[Tree, Assign]

  /** Scala assign `x = y` */
  val Assign: AssignModule

  trait AssignModule { this: Assign.type =>

    /** Create an assignment ` = ` */
    def apply(lhs: Term, rhs: Term): Assign

    def copy(original: Tree)(lhs: Term, rhs: Term): Assign

    /** Matches an assignment ` = ` */
    def unapply(x: Assign): Option[(Term, Term)]
  }

  given AssignMethods as AssignMethods = AssignMethodsImpl
  protected val AssignMethodsImpl: AssignMethods

  trait AssignMethods:
    extension (self: Assign):
      def lhs: Term
      def rhs: Term
    end extension
  end AssignMethods

  /** Tree representing a block `{ ... }` in the source code */
  type Block <: Term

  given TypeTest[Tree, Block] = BlockTypeTest
  protected val BlockTypeTest: TypeTest[Tree, Block]

  /** Scala code block `{ stat0; ...; statN; expr }` term */
  val Block: BlockModule

  trait BlockModule { this: Block.type =>

    /** Creates a block `{ ;  }` */
    def apply(stats: List[Statement], expr: Term): Block

    def copy(original: Tree)(stats: List[Statement], expr: Term): Block

    /** Matches a block `{ ;  }` */
    def unapply(x: Block): Option[(List[Statement], Term)]
  }

  given BlockMethods as BlockMethods = BlockMethodsImpl
  protected val BlockMethodsImpl: BlockMethods

  trait BlockMethods:
    extension (self: Block):
      def statements: List[Statement]
      def expr: Term
    end extension
  end BlockMethods

  given TypeTest[Tree, Closure] = ClosureTypeTest
  protected val ClosureTypeTest: TypeTest[Tree, Closure]

  /** A lambda `(...) => ...` in the source code is represented as
   *  a local method and a closure:
   *
   *  {
   *    def m(...) = ...
   *    closure(m)
   *  }
   *
   */
  type Closure <: Term

  val Closure: ClosureModule

  trait ClosureModule { this: Closure.type =>

    def apply(meth: Term, tpe: Option[Type]): Closure

    def copy(original: Tree)(meth: Tree, tpe: Option[Type]): Closure

    def unapply(x: Closure): Option[(Term, Option[Type])]
  }

  given ClosureMethods as ClosureMethods = ClosureMethodsImpl
  protected val ClosureMethodsImpl: ClosureMethods

  trait ClosureMethods:
    extension (self: Closure):
      def meth: Term
      def tpeOpt: Option[Type]
    end extension
  end ClosureMethods

  /** A lambda `(...) => ...` in the source code is represented as
   *  a local method and a closure:
   *
   *  {
   *    def m(...) = ...
   *    closure(m)
   *  }
   *
   *  @note Due to the encoding, in pattern matches the case for `Lambda`
   *        should come before the case for `Block` to avoid mishandling
   *        of `Lambda`.
   */
  val Lambda: LambdaModule

  trait LambdaModule { this: Lambda.type =>
    def unapply(tree: Block): Option[(List[ValDef], Term)]
    def apply(tpe: MethodType, rhsFn: List[Tree] => Tree): Block
  }

  given TypeTest[Tree, If] = IfTypeTest
  protected val IfTypeTest: TypeTest[Tree, If]

  /** Tree representing an if/then/else `if (...) ... else ...` in the source code */
  type If <: Term

  /** Scala `if`/`else` term */
  val If: IfModule

  trait IfModule { this: If.type =>

    /** Create an if/then/else `if ()  else ` */
    def apply(cond: Term, thenp: Term, elsep: Term): If

    def copy(original: Tree)(cond: Term, thenp: Term, elsep: Term): If

    /** Matches an if/then/else `if ()  else ` */
    def unapply(tree: If): Option[(Term, Term, Term)]
  }

  given IfMethods as IfMethods = IfMethodsImpl
  protected val IfMethodsImpl: IfMethods

  trait IfMethods:
    extension (self: If):
      def cond: Term
      def thenp: Term
      def elsep: Term
    end extension
  end IfMethods

  /** Tree representing a pattern match `x match  { ... }` in the source code */
  type Match <: Term

  given TypeTest[Tree, Match] = MatchTypeTest
  protected val MatchTypeTest: TypeTest[Tree, Match]

  /** Scala `match` term */
  val Match: MatchModule

  trait MatchModule { this: Match.type =>

    /** Creates a pattern match ` match {  }` */
    def apply(selector: Term, cases: List[CaseDef]): Match

    def copy(original: Tree)(selector: Term, cases: List[CaseDef]): Match

    /** Matches a pattern match ` match {  }` */
    def unapply(x: Match): Option[(Term, List[CaseDef])]
  }

  given MatchMethods as MatchMethods = MatchMethodsImpl
  protected val MatchMethodsImpl: MatchMethods

  trait MatchMethods:
    extension (self: Match):
      def scrutinee: Term
      def cases: List[CaseDef]
    end extension
  end MatchMethods

  /** Tree representing a pattern match `given match { ... }` in the source code */  // TODO: drop
  type GivenMatch <: Term

  given TypeTest[Tree, GivenMatch] = GivenMatchTypeTest
  protected val GivenMatchTypeTest: TypeTest[Tree, GivenMatch]

  /** Scala implicit `match` term */
  val GivenMatch: GivenMatchModule

  trait GivenMatchModule { this: GivenMatch.type =>

    /** Creates a pattern match `given match {  }` */
    def apply(cases: List[CaseDef]): GivenMatch

    def copy(original: Tree)(cases: List[CaseDef]): GivenMatch

    /** Matches a pattern match `given match {  }` */
    def unapply(x: GivenMatch): Option[List[CaseDef]]
  }

  given GivenMatchMethods as GivenMatchMethods = GivenMatchMethodsImpl
  protected val GivenMatchMethodsImpl: GivenMatchMethods

  trait GivenMatchMethods:
    extension (self: GivenMatch):
      def cases: List[CaseDef]
    end extension
  end GivenMatchMethods

  /** Tree representing a try catch `try x catch { ... } finally { ... }` in the source code */
  type Try <: Term

  given TypeTest[Tree, Try] = TryTypeTest
  protected val TryTypeTest: TypeTest[Tree, Try]

  /** Scala `try`/`catch`/`finally` term */
  val Try: TryModule

  trait TryModule { this: Try.type =>

    /** Create a try/catch `try  catch {  } finally ` */
    def apply(expr: Term, cases: List[CaseDef], finalizer: Option[Term]): Try

    def copy(original: Tree)(expr: Term, cases: List[CaseDef], finalizer: Option[Term]): Try

    /** Matches a try/catch `try  catch {  } finally ` */
    def unapply(x: Try): Option[(Term, List[CaseDef], Option[Term])]
  }

  given TryMethods as TryMethods = TryMethodsImpl
  protected val TryMethodsImpl: TryMethods

  trait TryMethods:
    extension (self: Try):
      def body: Term
      def cases: List[CaseDef]
      def finalizer: Option[Term]
    end extension
  end TryMethods

  given TypeTest[Tree, Return] = ReturnTypeTest
  protected val ReturnTypeTest: TypeTest[Tree, Return]

  /** Tree representing a `return` in the source code */
  type Return <: Term

  /** Scala local `return` */
  val Return: ReturnModule

  trait ReturnModule { this: Return.type =>

    /** Creates `return ` */
    def apply(expr: Term): Return

    def copy(original: Tree)(expr: Term): Return

    /** Matches `return ` */
    def unapply(x: Return): Option[Term]
  }

  given ReturnMethods as ReturnMethods = ReturnMethodsImpl
  protected val ReturnMethodsImpl: ReturnMethods

  trait ReturnMethods:
    extension (self: Return):
      def expr: Term
    end extension
  end ReturnMethods

  /** Tree representing a variable argument list in the source code */
  type Repeated <: Term

  given TypeTest[Tree, Repeated] = RepeatedTypeTest
  protected val RepeatedTypeTest: TypeTest[Tree, Repeated]

  val Repeated: RepeatedModule

  trait RepeatedModule { this: Repeated.type =>
    def apply(elems: List[Term], tpt: TypeTree): Repeated
    def copy(original: Tree)(elems: List[Term], tpt: TypeTree): Repeated
    def unapply(x: Repeated): Option[(List[Term], TypeTree)]
  }

  given RepeatedMethods as RepeatedMethods = RepeatedMethodsImpl
  protected val RepeatedMethodsImpl: RepeatedMethods

  trait RepeatedMethods:
    extension (self: Repeated):
      def elems: List[Term]
      def elemtpt: TypeTree
    end extension
  end RepeatedMethods

  /** Tree representing the scope of an inlined tree */
  type Inlined <: Term

  given TypeTest[Tree, Inlined] = InlinedTypeTest
  protected val InlinedTypeTest: TypeTest[Tree, Inlined]

  val Inlined: InlinedModule

  trait InlinedModule { this: Inlined.type =>
    def apply(call: Option[Tree /* Term | TypeTree */], bindings: List[Definition], expansion: Term): Inlined
    def copy(original: Tree)(call: Option[Tree /* Term | TypeTree */], bindings: List[Definition], expansion: Term): Inlined
    def unapply(x: Inlined): Option[(Option[Tree /* Term | TypeTree */], List[Definition], Term)]
  }

  given InlinedMethods as InlinedMethods = InlinedMethodsImpl
  protected val InlinedMethodsImpl: InlinedMethods

  trait InlinedMethods:
    extension (self: Inlined):
      def call: Option[Tree /* Term | TypeTree */]
      def bindings: List[Definition]
      def body: Term
    end extension
  end InlinedMethods

  /** Tree representing a selection of definition with a given name on a given prefix and number of nested scopes of inlined trees */
  type SelectOuter <: Term

  given TypeTest[Tree, SelectOuter] = SelectOuterTypeTest
  protected val SelectOuterTypeTest: TypeTest[Tree, SelectOuter]

  val SelectOuter: SelectOuterModule

  trait SelectOuterModule { this: SelectOuter.type =>
    def apply(qualifier: Term, name: String, levels: Int): SelectOuter
    def copy(original: Tree)(qualifier: Term, name: String, levels: Int): SelectOuter
    def unapply(x: SelectOuter): Option[(Term, String, Int)]
  }

  given SelectOuterMethods as SelectOuterMethods = SelectOuterMethodsImpl
  protected val SelectOuterMethodsImpl: SelectOuterMethods

  trait SelectOuterMethods:
    extension (self: SelectOuter):
      def qualifier: Term
      def name: String
      def level: Int
    end extension
  end SelectOuterMethods

  /** Tree representing a while loop */
  type While <: Term

  given TypeTest[Tree, While] = WhileTypeTest
  protected val WhileTypeTest: TypeTest[Tree, While]

  val While: WhileModule

  trait WhileModule { this: While.type =>

    /** Creates a while loop `while () ` and returns (, ) */
    def apply(cond: Term, body: Term): While

    def copy(original: Tree)(cond: Term, body: Term): While

    /** Extractor for while loops. Matches `while () ` and returns (, ) */
    def unapply(x: While): Option[(Term, Term)]
  }

  given WhileMethods as WhileMethods = WhileMethodsImpl
  protected val WhileMethodsImpl: WhileMethods

  trait WhileMethods:
    extension (self: While):
      def cond: Term
      def body: Term
    end extension
  end WhileMethods

  // ----- TypeTrees ------------------------------------------------

  /** Type tree representing a type written in the source */
  type TypeTree <: Tree

  given TypeTest[Tree, TypeTree] = TypeTreeTypeTest
  protected val TypeTreeTypeTest: TypeTest[Tree, TypeTree]

  val TypeTree: TypeTreeModule

  trait TypeTreeModule { this: TypeTree.type => }

  given TypeTreeMethods as TypeTreeMethods = TypeTreeMethodsImpl
  protected val TypeTreeMethodsImpl: TypeTreeMethods

  trait TypeTreeMethods:
    extension (self: TypeTree):
      /** Type of this type tree */
      def tpe: Type
    end extension
  end TypeTreeMethods

  /** Type tree representing an inferred type */
  type Inferred <: TypeTree

  given TypeTest[Tree, Inferred] = InferredTypeTest
  protected val InferredTypeTest: TypeTest[Tree, Inferred]

  /** TypeTree containing an inferred type */
  val Inferred: InferredModule

  trait InferredModule { this: Inferred.type =>
    def apply(tpe: Type): Inferred
    /** Matches a TypeTree containing an inferred type */
    def unapply(x: Inferred): Boolean
  }

  /** Type tree representing a reference to definition with a given name */
  type TypeIdent <: TypeTree

  given TypeTest[Tree, TypeIdent] = TypeIdentTypeTest
  protected val TypeIdentTypeTest: TypeTest[Tree, TypeIdent]

  val TypeIdent: TypeIdentModule

  trait TypeIdentModule { this: TypeIdent.type =>
    def apply(sym: Symbol): TypeTree
    def copy(original: Tree)(name: String): TypeIdent
    def unapply(x: TypeIdent): Option[String]
  }

  given TypeIdentMethods as TypeIdentMethods = TypeIdentMethodsImpl
  protected val TypeIdentMethodsImpl: TypeIdentMethods

  trait TypeIdentMethods:
    extension (self: TypeIdent):
      def name: String
    end extension
  end TypeIdentMethods

  /** Type tree representing a selection of definition with a given name on a given term prefix */
  type TypeSelect <: TypeTree

  given TypeTest[Tree, TypeSelect] = TypeSelectTypeTest
  protected val TypeSelectTypeTest: TypeTest[Tree, TypeSelect]

  val TypeSelect: TypeSelectModule

  trait TypeSelectModule { this: TypeSelect.type =>
    def apply(qualifier: Term, name: String): TypeSelect
    def copy(original: Tree)(qualifier: Term, name: String): TypeSelect
    def unapply(x: TypeSelect): Option[(Term, String)]
  }

  given TypeSelectMethods as TypeSelectMethods = TypeSelectMethodsImpl
  protected val TypeSelectMethodsImpl: TypeSelectMethods

  trait TypeSelectMethods:
    extension (self: TypeSelect):
      def qualifier: Term
      def name: String
    end extension
  end TypeSelectMethods

  /** Type tree representing a selection of definition with a given name on a given type prefix */
  type Projection <: TypeTree

  given TypeTest[Tree, Projection] = ProjectionTypeTest
  protected val ProjectionTypeTest: TypeTest[Tree, Projection]

  val Projection: ProjectionModule

  trait ProjectionModule { this: Projection.type =>
    // TODO def apply(qualifier: TypeTree, name: String): Project
    def copy(original: Tree)(qualifier: TypeTree, name: String): Projection
    def unapply(x: Projection): Option[(TypeTree, String)]
  }

  given ProjectionMethods as ProjectionMethods = ProjectionMethodsImpl
  protected val ProjectionMethodsImpl: ProjectionMethods

  trait ProjectionMethods:
    extension (self: Projection):
      def qualifier: TypeTree
      def name: String
    end extension
  end ProjectionMethods

  /** Type tree representing a singleton type */
  type Singleton <: TypeTree

  given TypeTest[Tree, Singleton] = SingletonTypeTest
  protected val SingletonTypeTest: TypeTest[Tree, Singleton]

  val Singleton: SingletonModule

  trait SingletonModule { this: Singleton.type =>
    def apply(ref: Term): Singleton
    def copy(original: Tree)(ref: Term): Singleton
    def unapply(x: Singleton): Option[Term]
  }

  given SingletonMethods as SingletonMethods = SingletonMethodsImpl
  protected val SingletonMethodsImpl: SingletonMethods

  trait SingletonMethods:
    extension (self: Singleton):
      def ref: Term
    end extension
  end SingletonMethods

  /** Type tree representing a type refinement */
  type Refined <: TypeTree

  given TypeTest[Tree, Refined] = RefinedTypeTest
  protected val RefinedTypeTest: TypeTest[Tree, Refined]

  val Refined: RefinedModule

  trait RefinedModule { this: Refined.type =>
    // TODO def apply(tpt: TypeTree, refinements: List[Definition]): Refined
    def copy(original: Tree)(tpt: TypeTree, refinements: List[Definition]): Refined
    def unapply(x: Refined): Option[(TypeTree, List[Definition])]
  }

  given RefinedMethods as RefinedMethods = RefinedMethodsImpl
  protected val RefinedMethodsImpl: RefinedMethods

  trait RefinedMethods:
    extension (self: Refined):
      def tpt: TypeTree
      def refinements: List[Definition]
    end extension
  end RefinedMethods

  /** Type tree representing a type application */
  type Applied <: TypeTree

  given TypeTest[Tree, Applied] = AppliedTypeTest
  protected val AppliedTypeTest: TypeTest[Tree, Applied]

  val Applied: AppliedModule

  trait AppliedModule { this: Applied.type =>
    def apply(tpt: TypeTree, args: List[Tree /*TypeTree | TypeBoundsTree*/]): Applied
    def copy(original: Tree)(tpt: TypeTree, args: List[Tree /*TypeTree | TypeBoundsTree*/]): Applied
    def unapply(x: Applied): Option[(TypeTree, List[Tree /*TypeTree | TypeBoundsTree*/])]
  }

  given AppliedMethods as AppliedMethods = AppliedMethodsImpl
  protected val AppliedMethodsImpl: AppliedMethods

  trait AppliedMethods:
    extension (self: Applied):
      def tpt: TypeTree
      def args: List[Tree /*TypeTree | TypeBoundsTree*/]
    end extension
  end AppliedMethods

  /** Type tree representing an annotated type */
  type Annotated <: TypeTree

  given TypeTest[Tree, Annotated] = AnnotatedTypeTest
  protected val AnnotatedTypeTest: TypeTest[Tree, Annotated]

  val Annotated: AnnotatedModule

  trait AnnotatedModule { this: Annotated.type =>
    def apply(arg: TypeTree, annotation: Term): Annotated
    def copy(original: Tree)(arg: TypeTree, annotation: Term): Annotated
    def unapply(x: Annotated): Option[(TypeTree, Term)]
  }

  given AnnotatedMethods as AnnotatedMethods = AnnotatedMethodsImpl
  protected val AnnotatedMethodsImpl: AnnotatedMethods

  trait AnnotatedMethods:
    extension (self: Annotated):
      def arg: TypeTree
      def annotation: Term
    end extension
  end AnnotatedMethods

  /** Type tree representing a type match */
  type MatchTypeTree <: TypeTree

  given TypeTest[Tree, MatchTypeTree] = MatchTypeTreeTypeTest
  protected val MatchTypeTreeTypeTest: TypeTest[Tree, MatchTypeTree]

  val MatchTypeTree: MatchTypeTreeModule

  trait MatchTypeTreeModule { this: MatchTypeTree.type =>
    def apply(bound: Option[TypeTree], selector: TypeTree, cases: List[TypeCaseDef]): MatchTypeTree
    def copy(original: Tree)(bound: Option[TypeTree], selector: TypeTree, cases: List[TypeCaseDef]): MatchTypeTree
    def unapply(x: MatchTypeTree): Option[(Option[TypeTree], TypeTree, List[TypeCaseDef])]
  }

  given MatchTypeTreeMethods as MatchTypeTreeMethods = MatchTypeTreeMethodsImpl
  protected val MatchTypeTreeMethodsImpl: MatchTypeTreeMethods

  trait MatchTypeTreeMethods:
    extension (self: MatchTypeTree):
      def bound: Option[TypeTree]
      def selector: TypeTree
      def cases: List[TypeCaseDef]
    end extension
  end MatchTypeTreeMethods

  /** Type tree representing a by name parameter */
  type ByName <: TypeTree

  given TypeTest[Tree, ByName] = ByNameTypeTest
  protected val ByNameTypeTest: TypeTest[Tree, ByName]

  val ByName: ByNameModule

  trait ByNameModule { this: ByName.type =>
    def apply(result: TypeTree): ByName
    def copy(original: Tree)(result: TypeTree): ByName
    def unapply(x: ByName): Option[TypeTree]
  }

  given ByNameMethods as ByNameMethods = ByNameMethodsImpl
  protected val ByNameMethodsImpl: ByNameMethods

  trait ByNameMethods:
    extension (self: ByName):
      def result: TypeTree
    end extension
  end ByNameMethods

  /** Type tree representing a lambda abstraction type */
  type LambdaTypeTree <: TypeTree

  given TypeTest[Tree, LambdaTypeTree] = LambdaTypeTreeTypeTest
  protected val LambdaTypeTreeTypeTest: TypeTest[Tree, LambdaTypeTree]

  val LambdaTypeTree: LambdaTypeTreeModule

  trait LambdaTypeTreeModule { this: LambdaTypeTree.type =>
    def apply(tparams: List[TypeDef], body: Tree /*TypeTree | TypeBoundsTree*/): LambdaTypeTree
    def copy(original: Tree)(tparams: List[TypeDef], body: Tree /*TypeTree | TypeBoundsTree*/): LambdaTypeTree
    def unapply(tree: LambdaTypeTree): Option[(List[TypeDef], Tree /*TypeTree | TypeBoundsTree*/)]
  }

  given LambdaTypeTreeMethods as LambdaTypeTreeMethods = LambdaTypeTreeMethodsImpl
  protected val LambdaTypeTreeMethodsImpl: LambdaTypeTreeMethods

  trait LambdaTypeTreeMethods:
    extension (self: LambdaTypeTree):
      def tparams: List[TypeDef]
      def body: Tree /*TypeTree | TypeBoundsTree*/
    end extension
  end LambdaTypeTreeMethods

  /** Type tree representing a type binding */
  type TypeBind <: TypeTree

  given TypeTest[Tree, TypeBind] = TypeBindTypeTest
  protected val TypeBindTypeTest: TypeTest[Tree, TypeBind]

  val TypeBind: TypeBindModule

  trait TypeBindModule { this: TypeBind.type =>
    // TODO def apply(name: String, tree: Tree): TypeBind
    def copy(original: Tree)(name: String, tpt: Tree /*TypeTree | TypeBoundsTree*/): TypeBind
    def unapply(x: TypeBind): Option[(String, Tree /*TypeTree | TypeBoundsTree*/)]
  }

  given TypeBindMethods as TypeBindMethods = TypeBindMethodsImpl
  protected val TypeBindMethodsImpl: TypeBindMethods

  trait TypeBindMethods:
    extension (self: TypeBind):
      def name: String
      def body: Tree /*TypeTree | TypeBoundsTree*/
    end extension
  end TypeBindMethods

  /** Type tree within a block with aliases `{ type U1 = ... ; T[U1, U2] }` */
  type TypeBlock <: TypeTree

  given TypeTest[Tree, TypeBlock] = TypeBlockTypeTest
  protected val TypeBlockTypeTest: TypeTest[Tree, TypeBlock]

  val TypeBlock: TypeBlockModule

  trait TypeBlockModule { this: TypeBlock.type =>
    def apply(aliases: List[TypeDef], tpt: TypeTree): TypeBlock
    def copy(original: Tree)(aliases: List[TypeDef], tpt: TypeTree): TypeBlock
    def unapply(x: TypeBlock): Option[(List[TypeDef], TypeTree)]
  }

  given TypeBlockMethods as TypeBlockMethods = TypeBlockMethodsImpl
  protected val TypeBlockMethodsImpl: TypeBlockMethods

  trait TypeBlockMethods:
    extension (self: TypeBlock):
      def aliases: List[TypeDef]
      def tpt: TypeTree
    end extension
  end TypeBlockMethods

  // ----- TypeBoundsTrees ------------------------------------------------

  /** Type tree representing a type bound written in the source */
  type TypeBoundsTree <: Tree /*TypeTree | TypeBoundsTree*/

  given TypeTest[Tree, TypeBoundsTree] = TypeBoundsTreeTypeTest
  protected val TypeBoundsTreeTypeTest: TypeTest[Tree, TypeBoundsTree]

  val TypeBoundsTree: TypeBoundsTreeModule

  trait TypeBoundsTreeModule { this: TypeBoundsTree.type =>
    def unapply(x: TypeBoundsTree): Option[(TypeTree, TypeTree)]
  }

  given TypeBoundsTreeMethods as TypeBoundsTreeMethods = TypeBoundsTreeMethodsImpl
  protected val TypeBoundsTreeMethodsImpl: TypeBoundsTreeMethods

  trait TypeBoundsTreeMethods:
    extension (self: TypeBoundsTree):
      def tpe: TypeBounds
      def low: TypeTree
      def hi: TypeTree
    end extension
  end TypeBoundsTreeMethods

  /** Type tree representing wildcard type bounds written in the source.
   *  The wildcard type `_` (for example in in `List[_]`) will be a type tree that
   *  represents a type but has `TypeBound`a inside.
   */
  type WildcardTypeTree  <: Tree

  given TypeTest[Tree, WildcardTypeTree] = WildcardTypeTreeTypeTest
  protected val WildcardTypeTreeTypeTest: TypeTest[Tree, WildcardTypeTree]

  val WildcardTypeTree: WildcardTypeTreeModule

  trait WildcardTypeTreeModule { this: WildcardTypeTree.type =>
    /** Matches a TypeBoundsTree containing wildcard type bounds */
    def unapply(x: WildcardTypeTree): Boolean
  }

  given WildcardTypeTreeMethods as WildcardTypeTreeMethods = WildcardTypeTreeMethodsImpl
  protected val WildcardTypeTreeMethodsImpl: WildcardTypeTreeMethods

  trait WildcardTypeTreeMethods:
    extension (self: WildcardTypeTree):
      def tpe: Type
    end extension
  end WildcardTypeTreeMethods

  // ----- CaseDefs ------------------------------------------------

  /** Branch of a pattern match or catch clause */
  type CaseDef <: Tree

  given TypeTest[Tree, CaseDef] = CaseDefTypeTest
  protected val CaseDefTypeTest: TypeTest[Tree, CaseDef]

  val CaseDef: CaseDefModule

  trait CaseDefModule { this: CaseDef.type =>
    def apply(pattern: Tree, guard: Option[Term], rhs: Term): CaseDef
    def copy(original: Tree)(pattern: Tree, guard: Option[Term], rhs: Term): CaseDef
    def unapply(x: CaseDef): Option[(Tree, Option[Term], Term)]
  }

  given CaseDefMethods as CaseDefMethods = CaseDefMethodsImpl
  protected val CaseDefMethodsImpl: CaseDefMethods

  trait CaseDefMethods:
    extension (self: CaseDef):
      def pattern: Tree
      def guard: Option[Term]
      def rhs: Term
    end extension
  end CaseDefMethods

  /** Branch of a type pattern match */
  type TypeCaseDef <: Tree

  given TypeTest[Tree, TypeCaseDef] = TypeCaseDefTypeTest
  protected val TypeCaseDefTypeTest: TypeTest[Tree, TypeCaseDef]

  val TypeCaseDef: TypeCaseDefModule

  trait TypeCaseDefModule { this: TypeCaseDef.type =>
    def apply(pattern: TypeTree, rhs: TypeTree): TypeCaseDef
    def copy(original: Tree)(pattern: TypeTree, rhs: TypeTree): TypeCaseDef
    def unapply(tree: TypeCaseDef): Option[(TypeTree, TypeTree)]
  }

  given TypeCaseDefMethods as TypeCaseDefMethods = TypeCaseDefMethodsImpl
  protected val TypeCaseDefMethodsImpl: TypeCaseDefMethods

  trait TypeCaseDefMethods:
    extension (self: TypeCaseDef):
      def pattern: TypeTree
      def rhs: TypeTree
    end extension
  end TypeCaseDefMethods

  // ----- Patterns ------------------------------------------------

  /** Pattern representing a `_ @ _` binding. */
  type Bind <: Tree

  given TypeTest[Tree, Bind] = BindTypeTest
  protected val BindTypeTest: TypeTest[Tree, Bind]

  val Bind: BindModule

  trait BindModule { this: Bind.type =>
    def apply(sym: Symbol, pattern: Tree): Bind
    def copy(original: Tree)(name: String, pattern: Tree): Bind
    def unapply(pattern: Bind): Option[(String, Tree)]
  }

  given BindMethods as BindMethods = BindMethodsImpl
  protected val BindMethodsImpl: BindMethods

  trait BindMethods:
    extension (self: Bind):
      def name: String
      def pattern: Tree
    end extension
  end BindMethods

  /** Pattern representing a `Xyz(...)` unapply. */
  type Unapply <: Tree

  given TypeTest[Tree, Unapply] = UnapplyTypeTest
  protected val UnapplyTypeTest: TypeTest[Tree, Unapply]

  val Unapply: UnapplyModule

  trait UnapplyModule { this: Unapply.type =>
    // TODO def apply(fun: Term, implicits: List[Term], patterns: List[Tree]): Unapply
    def copy(original: Tree)(fun: Term, implicits: List[Term], patterns: List[Tree]): Unapply
    def unapply(x: Unapply): Option[(Term, List[Term], List[Tree])]
  }

  given UnapplyMethods as UnapplyMethods = UnapplyMethodsImpl
  protected val UnapplyMethodsImpl: UnapplyMethods

  trait UnapplyMethods:
    extension (self: Unapply):
      def fun: Term
      def implicits: List[Term]
      def patterns: List[Tree]
    end extension
  end UnapplyMethods

  /** Pattern representing `X | Y | ...` alternatives. */
  type Alternatives <: Tree

  given TypeTest[Tree, Alternatives] = AlternativesTypeTest
  protected val AlternativesTypeTest: TypeTest[Tree, Alternatives]

  val Alternatives: AlternativesModule

  trait AlternativesModule { this: Alternatives.type =>
    def apply(patterns: List[Tree]): Alternatives
    def copy(original: Tree)(patterns: List[Tree]): Alternatives
    def unapply(x: Alternatives): Option[List[Tree]]
  }

  given AlternativesMethods as AlternativesMethods = AlternativesMethodsImpl
  protected val AlternativesMethodsImpl: AlternativesMethods

  trait AlternativesMethods:
    extension (self: Alternatives):
      def patterns: List[Tree]
    end extension
  end AlternativesMethods

  //////////////////////
  // IMPORT SELECTORS //
  /////////////////////

  /** Import selectors:
   *   * SimpleSelector: `.bar` in `import foo.bar`
   *   * RenameSelector: `.{bar => baz}` in `import foo.{bar => baz}`
   *   * OmitSelector: `.{bar => _}` in `import foo.{bar => _}`
   */
  type ImportSelector <: AnyRef

  val ImportSelector: ImportSelectorModule

  trait ImportSelectorModule { this: ImportSelector.type => }

  given TypeTest[ImportSelector, SimpleSelector] = SimpleSelectorTypeTest
  protected val SimpleSelectorTypeTest: TypeTest[ImportSelector, SimpleSelector]

  val SimpleSelector: SimpleSelectorModule

  trait SimpleSelectorModule { this: SimpleSelector.type =>
    def unapply(x: SimpleSelector): Option[String]
  }

  /** Simple import selector: `.bar` in `import foo.bar` */
  type SimpleSelector <: ImportSelector

  given SimpleSelectorMethods as SimpleSelectorMethods = SimpleSelectorMethodsImpl
  protected val SimpleSelectorMethodsImpl: SimpleSelectorMethods

  trait SimpleSelectorMethods:
    extension (self: SimpleSelector):
      def name: String
      def namePos: Position
    end extension
  end SimpleSelectorMethods

  /** Rename import selector: `.{bar => baz}` in `import foo.{bar => baz}` */
  type RenameSelector <: ImportSelector

  given TypeTest[ImportSelector, RenameSelector] = RenameSelectorTypeTest
  protected val RenameSelectorTypeTest: TypeTest[ImportSelector, RenameSelector]

  val RenameSelector: RenameSelectorModule

  trait RenameSelectorModule { this: RenameSelector.type =>
    def unapply(x: RenameSelector): Option[(String, String)]
  }

  /** Omit import selector: `.{bar => _}` in `import foo.{bar => _}` */
  type OmitSelector <: ImportSelector

  given RenameSelectorMethods as RenameSelectorMethods = RenameSelectorMethodsImpl
  protected val RenameSelectorMethodsImpl: RenameSelectorMethods

  trait RenameSelectorMethods:
    extension (self: RenameSelector):
      def fromName: String
      def fromPos: Position
      def toName: String
      def toPos: Position
    end extension
  end RenameSelectorMethods

  given TypeTest[ImportSelector, OmitSelector] = OmitSelectorTypeTest
  protected val OmitSelectorTypeTest: TypeTest[ImportSelector, OmitSelector]

  val OmitSelector: OmitSelectorModule

  trait OmitSelectorModule { this: OmitSelector.type =>
    def unapply(x: OmitSelector): Option[String]
  }

  given OmitSelectorMethods as OmitSelectorMethods = OmitSelectorMethodsImpl
  protected val OmitSelectorMethodsImpl: OmitSelectorMethods

  trait OmitSelectorMethods:
    extension (self: OmitSelector):
      def name: String
      def namePos: Position
  end OmitSelectorMethods

  ///////////////
  //   TYPES   //
  ///////////////

  // ----- Types ----------------------------------------------------

  /** A type, type constructors, type bounds or NoPrefix */
  type Type

  val Type: TypeModule

  trait TypeModule { this: Type.type =>
    /** Returns the type or kind (Type) of T */
    def of[T <: AnyKind](using qtype: scala.quoted.Type[T]): Type

    /** Returns the type constructor of the runtime (erased) class */
    def typeConstructorOf(clazz: Class[?]): Type
  }

  given TypeMethods as TypeMethods = TypeMethodsImpl
  protected val TypeMethodsImpl: TypeMethods

  trait TypeMethods {
    extension (self: Type):

      /** Shows the tree as extractors */
      def showExtractors: String

      /** Shows the tree as fully typed source code */
      def show: String

      /** Shows the tree as fully typed source code */
      def showWith(syntaxHighlight: SyntaxHighlight): String

      /** Convert `Type` to an `quoted.Type[_]` */
      def seal: scala.quoted.Type[_]

      /** Is `self` type the same as `that` type?
       *  This is the case iff `self <:< that` and `that <:< self`.
       */
      def =:=(that: Type): Boolean

      /** Is this type a subtype of that type? */
      def <:<(that: Type): Boolean

      /** Widen from singleton type to its underlying non-singleton
       *  base type by applying one or more `underlying` dereferences,
       *  Also go from => T to T.
       *  Identity for all other types. Example:
       *
       *  class Outer { class C ; val x: C }
       *  def o: Outer
       *  .widen = o.C
       */
      def widen: Type

      /** Widen from TermRef to its underlying non-termref
        *  base type, while also skipping `=>T` types.
        */
      def widenTermRefExpr: Type

      /** Follow aliases and dereferences LazyRefs, annotated types and instantiated
        *  TypeVars until type is no longer alias type, annotated type, LazyRef,
        *  or instantiated type variable.
        */
      def dealias: Type

      /** A simplified version of this type which is equivalent wrt =:= to this type.
       *  Reduces typerefs, applied match types, and and or types.
       */
      def simplified: Type

      def classSymbol: Option[Symbol]
      def typeSymbol: Symbol
      def termSymbol: Symbol
      def isSingleton: Boolean
      def memberType(member: Symbol): Type

      /** The base classes of this type with the class itself as first element. */
      def baseClasses: List[Symbol]

      /** The least type instance of given class which is a super-type
       *  of this type.  Example:
       *  {{{
       *    class D[T]
       *    class C extends p.D[Int]
       *    ThisType(C).baseType(D) = p.D[Int]
       * }}}
       */
      def baseType(cls: Symbol): Type

      /** Is this type an instance of a non-bottom subclass of the given class `cls`? */
      def derivesFrom(cls: Symbol): Boolean

      /** Is this type a function type?
       *
       *  @return true if the dealised type of `self` without refinement is `FunctionN[T1, T2, ..., Tn]`
       *
       *  @note The function
       *
       *     - returns true for `given Int => Int` and `erased Int => Int`
       *     - returns false for `List[Int]`, despite that `List[Int] <:< Int => Int`.
       */
      def isFunctionType: Boolean

      /** Is this type an context function type?
       *
       *  @see `isFunctionType`
       */
      def isContextFunctionType: Boolean

      /** Is this type an erased function type?
       *
       *  @see `isFunctionType`
       */
      def isErasedFunctionType: Boolean

      /** Is this type a dependent function type?
       *
       *  @see `isFunctionType`
       */
      def isDependentFunctionType: Boolean

      /** The type , reduced if possible */
      def select(sym: Symbol): Type

      /** The current type applied to given type arguments: `this[targ]` */
      def appliedTo(targ: Type): Type

      /** The current type applied to given type arguments: `this[targ0, ..., targN]` */
      def appliedTo(targs: List[Type]): Type

    end extension
  }

  /** A singleton type representing a known constant value */
  type ConstantType <: Type

  given TypeTest[Type, ConstantType] = ConstantTypeTypeTest
  protected val ConstantTypeTypeTest: TypeTest[Type, ConstantType]

  val ConstantType: ConstantTypeModule

  trait ConstantTypeModule { this: ConstantType.type =>
    def apply(x : Constant): ConstantType
    def unapply(x: ConstantType): Option[Constant]
  }

  given ConstantTypeMethods as ConstantTypeMethods = ConstantTypeMethodsImpl
  protected val ConstantTypeMethodsImpl: ConstantTypeMethods

  trait ConstantTypeMethods:
    extension (self: ConstantType):
      def constant: Constant
    end extension
  end ConstantTypeMethods

  /** Type of a reference to a term symbol */
  type TermRef <: Type

  given TypeTest[Type, TermRef] = TermRefTypeTest
  protected val TermRefTypeTest: TypeTest[Type, TermRef]

  val TermRef: TermRefModule

  trait TermRefModule { this: TermRef.type =>
    def apply(qual: Type, name: String): TermRef
    def unapply(x: TermRef): Option[(Type, String)]
  }

  given TermRefMethods as TermRefMethods = TermRefMethodsImpl
  protected val TermRefMethodsImpl: TermRefMethods

  trait TermRefMethods:
    extension (self: TermRef):
      def qualifier: Type
      def name: String
    end extension
  end TermRefMethods

  /** Type of a reference to a type symbol */
  type TypeRef <: Type

  given TypeTest[Type, TypeRef] = TypeRefTypeTest
  protected val TypeRefTypeTest: TypeTest[Type, TypeRef]

  val TypeRef: TypeRefModule

  trait TypeRefModule { this: TypeRef.type =>
    def unapply(x: TypeRef): Option[(Type, String)]
  }

  given TypeRefMethods as TypeRefMethods = TypeRefMethodsImpl
  protected val TypeRefMethodsImpl: TypeRefMethods

  trait TypeRefMethods:
    extension (self: TypeRef):
      def qualifier: Type
      def name: String
      def isOpaqueAlias: Boolean
      def translucentSuperType: Type
    end extension
  end TypeRefMethods

  /** Type of a `super` reference */
  type SuperType <: Type

  given TypeTest[Type, SuperType] = SuperTypeTypeTest
  protected val SuperTypeTypeTest: TypeTest[Type, SuperType]

  val SuperType: SuperTypeModule

  trait SuperTypeModule { this: SuperType.type =>
    def apply(thistpe: Type, supertpe: Type): SuperType
    def unapply(x: SuperType): Option[(Type, Type)]
  }

  given SuperTypeMethods as SuperTypeMethods = SuperTypeMethodsImpl
  protected val SuperTypeMethodsImpl: SuperTypeMethods

  trait SuperTypeMethods { this: SuperTypeMethods =>
    extension (self: SuperType):
      def thistpe: Type
      def supertpe: Type
    end extension
  }

  /** A type with a type refinement `T { type U }` */
  type Refinement <: Type

  given TypeTest[Type, Refinement] = RefinementTypeTest
  protected val RefinementTypeTest: TypeTest[Type, Refinement]

  val Refinement: RefinementModule

  trait RefinementModule { this: Refinement.type =>
    def apply(parent: Type, name: String, info: Type): Refinement
    def unapply(x: Refinement): Option[(Type, String, Type)]
  }

  given RefinementMethods as RefinementMethods = RefinementMethodsImpl
  protected val RefinementMethodsImpl: RefinementMethods

  trait RefinementMethods:
    extension (self: Refinement):
      def parent: Type
      def name: String
      def info: Type
    end extension
  end RefinementMethods

  /** A higher kinded type applied to some types `T[U]` */
  type AppliedType <: Type

  given TypeTest[Type, AppliedType] = AppliedTypeTypeTest
  protected val AppliedTypeTypeTest: TypeTest[Type, AppliedType]

  val AppliedType: AppliedTypeModule

  trait AppliedTypeModule { this: AppliedType.type =>
    def unapply(x: AppliedType): Option[(Type, List[Type])]
  }

  given AppliedTypeMethods as AppliedTypeMethods = AppliedTypeMethodsImpl
  protected val AppliedTypeMethodsImpl: AppliedTypeMethods

  trait AppliedTypeMethods:
    extension (self: AppliedType):
      def tycon: Type
      def args: List[Type]
    end extension
  end AppliedTypeMethods

  /** A type with an anottation `T @foo` */
  type AnnotatedType <: Type

  given TypeTest[Type, AnnotatedType] = AnnotatedTypeTypeTest
  protected val AnnotatedTypeTypeTest: TypeTest[Type, AnnotatedType]

  val AnnotatedType: AnnotatedTypeModule

  trait AnnotatedTypeModule { this: AnnotatedType.type =>
    def apply(underlying: Type, annot: Term): AnnotatedType
    def unapply(x: AnnotatedType): Option[(Type, Term)]
  }

  given AnnotatedTypeMethods as AnnotatedTypeMethods = AnnotatedTypeMethodsImpl
  protected val AnnotatedTypeMethodsImpl: AnnotatedTypeMethods

  trait AnnotatedTypeMethods:
    extension (self: AnnotatedType):
      def underlying: Type
      def annot: Term
    end extension
  end AnnotatedTypeMethods

  /** Intersection type `T & U` */
  type AndType <: Type

  given TypeTest[Type, AndType] = AndTypeTypeTest
  protected val AndTypeTypeTest: TypeTest[Type, AndType]

  val AndType: AndTypeModule

  trait AndTypeModule { this: AndType.type =>
    def apply(lhs: Type, rhs: Type): AndType
    def unapply(x: AndType): Option[(Type, Type)]
  }

  given AndTypeMethods as AndTypeMethods = AndTypeMethodsImpl
  protected val AndTypeMethodsImpl: AndTypeMethods

  trait AndTypeMethods:
    extension (self: AndType):
      def left: Type
      def right: Type
    end extension
  end AndTypeMethods

  /** Union type `T | U` */
  type OrType <: Type

  given TypeTest[Type, OrType] = OrTypeTypeTest
  protected val OrTypeTypeTest: TypeTest[Type, OrType]

  val OrType: OrTypeModule

  trait OrTypeModule { this: OrType.type =>
    def apply(lhs: Type, rhs: Type): OrType
    def unapply(x: OrType): Option[(Type, Type)]
  }

  given OrTypeMethods as OrTypeMethods = OrTypeMethodsImpl
  protected val OrTypeMethodsImpl: OrTypeMethods

  trait OrTypeMethods:
    extension (self: OrType):
      def left: Type
      def right: Type
    end extension
  end OrTypeMethods

  /** Type match `T match { case U => ... }` */
  type MatchType <: Type

  given TypeTest[Type, MatchType] = MatchTypeTypeTest
  protected val MatchTypeTypeTest: TypeTest[Type, MatchType]

  val MatchType: MatchTypeModule

  trait MatchTypeModule { this: MatchType.type =>
    def apply(bound: Type, scrutinee: Type, cases: List[Type]): MatchType
    def unapply(x: MatchType): Option[(Type, Type, List[Type])]
  }

  given MatchTypeMethods as MatchTypeMethods = MatchTypeMethodsImpl
  protected val MatchTypeMethodsImpl: MatchTypeMethods

  trait MatchTypeMethods:
    extension (self: MatchType):
      def bound: Type
      def scrutinee: Type
      def cases: List[Type]
    end extension
  end MatchTypeMethods

  /** Type of a by by name parameter */
  type ByNameType <: Type

  given TypeTest[Type, ByNameType] = ByNameTypeTypeTest
  protected val ByNameTypeTypeTest: TypeTest[Type, ByNameType]

  val ByNameType: ByNameTypeModule

  trait ByNameTypeModule { this: ByNameType.type =>
    def apply(underlying: Type): Type
    def unapply(x: ByNameType): Option[Type]
  }

  given ByNameTypeMethods as ByNameTypeMethods = ByNameTypeMethodsImpl
  protected val ByNameTypeMethodsImpl: ByNameTypeMethods

  trait ByNameTypeMethods:
    extension (self: ByNameType):
      def underlying: Type
    end extension
  end ByNameTypeMethods

  /** Type of a parameter reference */
  type ParamRef <: Type

  given TypeTest[Type, ParamRef] = ParamRefTypeTest
  protected val ParamRefTypeTest: TypeTest[Type, ParamRef]

  val ParamRef: ParamRefModule

  trait ParamRefModule { this: ParamRef.type =>
    def unapply(x: ParamRef): Option[(LambdaType, Int)]
  }

  given ParamRefMethods as ParamRefMethods = ParamRefMethodsImpl
  protected val ParamRefMethodsImpl: ParamRefMethods

  trait ParamRefMethods:
    extension (self: ParamRef):
      def binder: LambdaType
      def paramNum: Int
    end extension
  end ParamRefMethods

  /** Type of `this` */
  type ThisType <: Type

  given TypeTest[Type, ThisType] = ThisTypeTypeTest
  protected val ThisTypeTypeTest: TypeTest[Type, ThisType]

  val ThisType: ThisTypeModule

  trait ThisTypeModule { this: ThisType.type =>
    def unapply(x: ThisType): Option[Type]
  }

  given ThisTypeMethods as ThisTypeMethods = ThisTypeMethodsImpl
  protected val ThisTypeMethodsImpl: ThisTypeMethods

  trait ThisTypeMethods:
    extension (self: ThisType):
      def tref: Type
    end extension
  end ThisTypeMethods

  /** A type that is recursively defined `this` */
  type RecursiveThis <: Type

  given TypeTest[Type, RecursiveThis] = RecursiveThisTypeTest
  protected val RecursiveThisTypeTest: TypeTest[Type, RecursiveThis]

  val RecursiveThis: RecursiveThisModule

  trait RecursiveThisModule { this: RecursiveThis.type =>
    def unapply(x: RecursiveThis): Option[RecursiveType]
  }

  given RecursiveThisMethods as RecursiveThisMethods = RecursiveThisMethodsImpl
  protected val RecursiveThisMethodsImpl: RecursiveThisMethods

  trait RecursiveThisMethods:
    extension (self: RecursiveThis):
      def binder: RecursiveType
    end extension
  end RecursiveThisMethods

  /** A type that is recursively defined */
  type RecursiveType <: Type

  given TypeTest[Type, RecursiveType] = RecursiveTypeTypeTest
  protected val RecursiveTypeTypeTest: TypeTest[Type, RecursiveType]

  val RecursiveType: RecursiveTypeModule

  trait RecursiveTypeModule { this: RecursiveType.type =>

    /** Create a RecType, normalizing its contents. This means:
     *
     *   1. Nested Rec types on the type's spine are merged with the outer one.
     *   2. Any refinement of the form `type T = z.T` on the spine of the type
     *      where `z` refers to the created rec-type is replaced by
     *      `type T`. This avoids infinite recursions later when we
     *      try to follow these references.
     */
    def apply(parentExp: RecursiveType => Type): RecursiveType

    def unapply(x: RecursiveType): Option[Type]
  }

  given RecursiveTypeMethods as RecursiveTypeMethods = RecursiveTypeMethodsImpl
  protected val RecursiveTypeMethodsImpl: RecursiveTypeMethods

  trait RecursiveTypeMethods:
    extension (self: RecursiveType):
      def underlying: Type
      def recThis: RecursiveThis
    end extension
  end RecursiveTypeMethods

  // TODO: remove LambdaType and use union types (MethodType | PolyType | TypeLambda)
  /** Common abstraction for lambda types (MethodType, PolyType and TypeLambda). */
  type LambdaType <: Type

  /** Type of the definition of a method taking a single list of parameters. It's return type may be a MethodType. */
  type MethodType <: LambdaType

  given TypeTest[Type, MethodType] = MethodTypeTypeTest
  protected val MethodTypeTypeTest: TypeTest[Type, MethodType]

  val MethodType: MethodTypeModule

  trait MethodTypeModule { this: MethodType.type =>
    def apply(paramNames: List[String])(paramInfosExp: MethodType => List[Type], resultTypeExp: MethodType => Type): MethodType
    def unapply(x: MethodType): Option[(List[String], List[Type], Type)]
  }

  given MethodTypeMethods as MethodTypeMethods = MethodTypeMethodsImpl
  protected val MethodTypeMethodsImpl: MethodTypeMethods

  trait MethodTypeMethods:
    extension (self: MethodType):
      def isImplicit: Boolean
      def isErased: Boolean
      def param(idx: Int): Type
      def paramNames: List[String]
      def paramTypes: List[Type]
      def resType: Type
    end extension
  end MethodTypeMethods

  /** Type of the definition of a method taking a list of type parameters. It's return type may be a MethodType. */
  type PolyType <: LambdaType

  given TypeTest[Type, PolyType] = PolyTypeTypeTest
  protected val PolyTypeTypeTest: TypeTest[Type, PolyType]

  val PolyType: PolyTypeModule

  trait PolyTypeModule { this: PolyType.type =>
    def apply(paramNames: List[String])(paramBoundsExp: PolyType => List[TypeBounds], resultTypeExp: PolyType => Type): PolyType
    def unapply(x: PolyType): Option[(List[String], List[TypeBounds], Type)]
  }

  given PolyTypeMethods as PolyTypeMethods = PolyTypeMethodsImpl
  protected val PolyTypeMethodsImpl: PolyTypeMethods

  trait PolyTypeMethods:
    extension (self: PolyType):
      def param(idx: Int): Type
      def paramNames: List[String]
      def paramBounds: List[TypeBounds]
      def resType: Type
    end extension
  end PolyTypeMethods

  /** Type of the definition of a type lambda taking a list of type parameters. It's return type may be a TypeLambda. */
  type TypeLambda <: LambdaType

  given TypeTest[Type, TypeLambda] = TypeLambdaTypeTest
  protected val TypeLambdaTypeTest: TypeTest[Type, TypeLambda]

  val TypeLambda: TypeLambdaModule

  trait TypeLambdaModule { this: TypeLambda.type =>
    def apply(paramNames: List[String], boundsFn: TypeLambda => List[TypeBounds], bodyFn: TypeLambda => Type): TypeLambda
    def unapply(x: TypeLambda): Option[(List[String], List[TypeBounds], Type)]
  }

  given TypeLambdaMethods as TypeLambdaMethods = TypeLambdaMethodsImpl
  protected val TypeLambdaMethodsImpl: TypeLambdaMethods

  trait TypeLambdaMethods:
    extension (self: TypeLambda):
      def paramNames: List[String]
      def paramBounds: List[TypeBounds]
      def param(idx: Int) : Type
      def resType: Type
    end extension
  end TypeLambdaMethods

  // ----- TypeBounds -----------------------------------------------

  /** Type bounds */
  type TypeBounds <: Type

  given TypeTest[Type, TypeBounds] = TypeBoundsTypeTest
  protected val TypeBoundsTypeTest: TypeTest[Type, TypeBounds]

  val TypeBounds: TypeBoundsModule

  trait TypeBoundsModule { this: TypeBounds.type =>
    def apply(low: Type, hi: Type): TypeBounds
    def unapply(x: TypeBounds): Option[(Type, Type)]
  }

  given TypeBoundsMethods as TypeBoundsMethods = TypeBoundsMethodsImpl
  protected val TypeBoundsMethodsImpl: TypeBoundsMethods

  trait TypeBoundsMethods:
    extension (self: TypeBounds):
      def low: Type
      def hi: Type
    end extension
  end TypeBoundsMethods

  // ----- NoPrefix -------------------------------------------------

  /** NoPrefix for a type selection */
  type NoPrefix <: Type

  given TypeTest[Type, NoPrefix] = NoPrefixTypeTest
  protected val NoPrefixTypeTest: TypeTest[Type, NoPrefix]

  val NoPrefix: NoPrefixModule

  trait NoPrefixModule { this: NoPrefix.type =>
    def unapply(x: NoPrefix): Boolean
  }

  ///////////////
  // CONSTANTS //
  ///////////////

  /** Constant value represented as the constant itself */
  type Constant <: AnyRef

  /** Constant value represented as the constant itself */
  val Constant: ConstantModule

  /** Constant value represented as the constant itself */
  trait ConstantModule { this: Constant.type =>

    /** Constant Boolean value */
    val Boolean: ConstantBooleanModule

    /** Constant Boolean value */
    trait ConstantBooleanModule { this: Boolean.type =>
      /** Create a constant Boolean value */
      def apply(x: Boolean): Constant
      /** Match Boolean value constant and extract its value */
      def unapply(constant: Constant): Option[Boolean]
    }

    /** Constant Byte value */
    val Byte: ConstantByteModule

    /** Constant Byte value */
    trait ConstantByteModule { this: Byte.type =>
      /** Create a constant Byte value */
      def apply(x: Byte): Constant
      /** Match Byte value constant and extract its value */
      def unapply(constant: Constant): Option[Byte]
    }

    /** Constant Short value */
    val Short: ConstantShortModule

    /** Constant Short value */
    trait ConstantShortModule { this: Short.type =>
      /** Create a constant Short value */
      def apply(x: Short): Constant
      /** Match Short value constant and extract its value */
      def unapply(constant: Constant): Option[Short]
    }

    /** Constant Int value */
    val Int: ConstantIntModule

    /** Constant Int value */
    trait ConstantIntModule { this: Int.type =>
      /** Create a constant Int value */
      def apply(x: Int): Constant
      /** Match Int value constant and extract its value */
      def unapply(constant: Constant): Option[Int]
    }

    /** Constant Long value */
    val Long: ConstantLongModule

    /** Constant Long value */
    trait ConstantLongModule { this: Long.type =>
      /** Create a constant Long value */
      def apply(x: Long): Constant
      /** Match Long value constant and extract its value */
      def unapply(constant: Constant): Option[Long]
    }

    /** Constant Float value */
    val Float: ConstantFloatModule

    /** Constant Float value */
    trait ConstantFloatModule { this: Float.type =>
      /** Create a constant Float value */
      def apply(x: Float): Constant
      /** Match Float value constant and extract its value */
      def unapply(constant: Constant): Option[Float]
    }

    /** Constant Double value */
    val Double: ConstantDoubleModule

    /** Constant Double value */
    trait ConstantDoubleModule { this: Double.type =>
      /** Create a constant Double value */
      def apply(x: Double): Constant
      /** Match Double value constant and extract its value */
      def unapply(constant: Constant): Option[Double]
    }

    /** Constant Char value */
    val Char: ConstantCharModule

    /** Constant Char value */
    trait ConstantCharModule { this: Char.type =>
      /** Create a constant Char value */
      def apply(x: Char): Constant
      /** Match Char value constant and extract its value */
      def unapply(constant: Constant): Option[Char]
    }

    /** Constant String value */
    val String: ConstantStringModule

    /** Constant String value */
    trait ConstantStringModule { this: String.type =>
      /** Create a constant String value */
      def apply(x: String): Constant
      /** Match String value constant and extract its value */
      def unapply(constant: Constant): Option[String]
    }

    /** Constant Unit value */
    val Unit: ConstantUnitModule

    /** Constant Unit value */
    trait ConstantUnitModule { this: Unit.type =>
      /** Create a constant Unit value */
      def apply(): Constant
      /** Match Unit value constant */
      def unapply(constant: Constant): Boolean
    }

    /** Constant null value */
    val Null: ConstantNullModule

    /** Constant null value */
    trait ConstantNullModule { this: Null.type =>
      /** Create a constant null value */
      def apply(): Constant
      /** Match null value constant */
      def unapply(constant: Constant): Boolean
    }

    /** Constant class value representing a `classOf[T]` */
    val ClassOf: ConstantClassOfModule

    /** Constant class value representing a `classOf[T]` */
    trait ConstantClassOfModule { this: ClassOf.type =>
      /** Create a constant class value representing `classOf[]` */
      def apply(tpe: Type): Constant
      /** Match a class value constant representing `classOf[]` and extract its type */
      def unapply(constant: Constant): Option[Type]
    }

  }

  given ConstantMethods as ConstantMethods = ConstantMethodsImpl
  protected val ConstantMethodsImpl: ConstantMethods

  trait ConstantMethods {
    extension (self: Constant):
      /** Returns the value of the constant */
      def value: Any

      /** Shows the tree as extractors */
      def showExtractors: String

      /** Shows the tree as fully typed source code */
      def show: String

      /** Shows the tree as fully typed source code */
      def showWith(syntaxHighlight: SyntaxHighlight): String
    end extension
  }

  /////////////////////
  // IMPLICIT SEARCH //
  /////////////////////

  val Implicits: ImplicitsModule

  trait ImplicitsModule { self: Implicits.type =>
    /** Find a given instance of type `T` in the current scope provided by the current enclosing splice.
     *  Return an `ImplicitSearchResult`.
     *
     *  @param tpe type of the implicit parameter
     */
    def search(tpe: Type): ImplicitSearchResult
  }

  /** Result of a given instance search */
  type ImplicitSearchResult <: AnyRef

  given TypeTest[ImplicitSearchResult, ImplicitSearchSuccess] = ImplicitSearchSuccessTypeTest
  protected val ImplicitSearchSuccessTypeTest: TypeTest[ImplicitSearchResult, ImplicitSearchSuccess]

  type ImplicitSearchSuccess <: ImplicitSearchResult

  given ImplicitSearchSuccessMethods as ImplicitSearchSuccessMethods = ImplicitSearchSuccessMethodsImpl
  protected val ImplicitSearchSuccessMethodsImpl: ImplicitSearchSuccessMethods

  trait ImplicitSearchSuccessMethods:
    extension (self: ImplicitSearchSuccess):
      def tree: Term
    end extension
  end ImplicitSearchSuccessMethods

  type ImplicitSearchFailure <: ImplicitSearchResult

  given TypeTest[ImplicitSearchResult, ImplicitSearchFailure] = ImplicitSearchFailureTypeTest
  protected val ImplicitSearchFailureTypeTest: TypeTest[ImplicitSearchResult, ImplicitSearchFailure]

  given ImplicitSearchFailureMethods as ImplicitSearchFailureMethods = ImplicitSearchFailureMethodsImpl
  protected val ImplicitSearchFailureMethodsImpl: ImplicitSearchFailureMethods

  trait ImplicitSearchFailureMethods:
    extension (self: ImplicitSearchFailure):
      def explanation: String
    end extension
  end ImplicitSearchFailureMethods

  type DivergingImplicit <: ImplicitSearchFailure

  given TypeTest[ImplicitSearchResult, DivergingImplicit] = DivergingImplicitTypeTest
  protected val DivergingImplicitTypeTest: TypeTest[ImplicitSearchResult, DivergingImplicit]

  type NoMatchingImplicits <: ImplicitSearchFailure

  given TypeTest[ImplicitSearchResult, NoMatchingImplicits] = NoMatchingImplicitsTypeTest
  protected val NoMatchingImplicitsTypeTest: TypeTest[ImplicitSearchResult, NoMatchingImplicits]

  type AmbiguousImplicits <: ImplicitSearchFailure

  given TypeTest[ImplicitSearchResult, AmbiguousImplicits] = AmbiguousImplicitsTypeTest
  protected val AmbiguousImplicitsTypeTest: TypeTest[ImplicitSearchResult, AmbiguousImplicits]

  /////////////
  // SYMBOLS //
  /////////////

  /** Symbol of a definition.
   *  Then can be compared with == to know if the definition is the same.
   */
  type Symbol <: AnyRef

  val Symbol: SymbolModule

  trait SymbolModule { this: Symbol.type =>

    /** Returns the symbol of the current enclosing definition */
    def currentOwner(using ctx: Context): Symbol

    /** Get package symbol if package is either defined in current compilation run or present on classpath. */
    def requiredPackage(path: String): Symbol

    /** Get class symbol if class is either defined in current compilation run or present on classpath. */
    def requiredClass(path: String): Symbol

    /** Get module symbol if module is either defined in current compilation run or present on classpath. */
    def requiredModule(path: String): Symbol

    /** Get method symbol if method is either defined in current compilation run or present on classpath. Throws if the method has an overload. */
    def requiredMethod(path: String): Symbol

    /** The class Symbol of a global class definition */
    def classSymbol(fullName: String): Symbol

    /** Generates a new method symbol with the given parent, name and type.
     *
     *  This symbol starts without an accompanying definition.
     *  It is the meta-programmer's responsibility to provide exactly one corresponding definition by passing
     *  this symbol to the DefDef constructor.
     *
     *  @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
     *        direct or indirect children of the reflection context's owner.
     */
    def newMethod(parent: Symbol, name: String, tpe: Type): Symbol

    /** Works as the other newMethod, but with additional parameters.
     *
     *  @param flags extra flags to with which the symbol should be constructed
     *  @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol.
     */
    def newMethod(parent: Symbol, name: String, tpe: Type, flags: Flags, privateWithin: Symbol): Symbol

    /** Generates a new val/var/lazy val symbol with the given parent, name and type.
     *
     *  This symbol starts without an accompanying definition.
     *  It is the meta-programmer's responsibility to provide exactly one corresponding definition by passing
     *  this symbol to the ValDef constructor.
     *
     *  Note: Also see Reflection.let
     *
     *  @param flags extra flags to with which the symbol should be constructed
     *  @param privateWithin the symbol within which this new method symbol should be private. May be noSymbol.
     *  @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
     *        direct or indirect children of the reflection context's owner.
     */
    def newVal(parent: Symbol, name: String, tpe: Type, flags: Flags, privateWithin: Symbol): Symbol

    /** Generates a pattern bind symbol with the given parent, name and type.
     *
     *  This symbol starts without an accompanying definition.
     *  It is the meta-programmer's responsibility to provide exactly one corresponding definition by passing
     *  this symbol to the BindDef constructor.
     *
     *  @param flags extra flags to with which the symbol should be constructed
     *  @note As a macro can only splice code into the point at which it is expanded, all generated symbols must be
     *        direct or indirect children of the reflection context's owner.
     */
    def newBind(parent: Symbol, name: String, flags: Flags, tpe: Type): Symbol

    /** Definition not available */
    def noSymbol: Symbol
  }

  given SymbolMethods as SymbolMethods = SymbolMethodsImpl
  protected val SymbolMethodsImpl: SymbolMethods

  trait SymbolMethods {
    extension (self: Symbol):

      /** Owner of this symbol. The owner is the symbol in which this symbol is defined. Throws if this symbol does not have an owner. */
      def owner: Symbol

      /** Owner of this symbol. The owner is the symbol in which this symbol is defined. Returns `NoSymbol` if this symbol does not have an owner. */
      def maybeOwner: Symbol

      /** Flags of this symbol */
      def flags: Flags

      /** This symbol is private within the resulting type */
      def privateWithin: Option[Type]

      /** This symbol is protected within the resulting type */
      def protectedWithin: Option[Type]

      /** The name of this symbol */
      def name: String

      /** The full name of this symbol up to the root package */
      def fullName: String

      /** The position of this symbol */
      def pos: Position

      def localContext: Context

      /** The documentation for this symbol, if any */
      def documentation: Option[Documentation]

      /** Tree of this definition
       *
       *  If this symbol `isClassDef` it will return `a `ClassDef`,
       *  if this symbol `isTypeDef` it will return `a `TypeDef`,
       *  if this symbol `isValDef` it will return `a `ValDef`,
       *  if this symbol `isDefDef` it will return `a `DefDef`
       *  if this symbol `isBind` it will return `a `Bind`,
       *  else will throw
       */
      def tree: Tree

      /** Annotations attached to this symbol */
      def annots: List[Term]

      /** Does this symbol come from a currently compiled source file? */
      def isDefinedInCurrentRun: Boolean

      /** Dummy val symbol that owns all statements within the initialization of the class.
       *  This may also contain local definitions such as classes defined in a `locally` block in the class.
       */
      def isLocalDummy: Boolean

      /** Is this symbol a class representing a refinement? */
      def isRefinementClass: Boolean

      /** Is this symbol an alias type? */
      def isAliasType: Boolean

      /** Is this symbol an anonymous class? */
      def isAnonymousClass: Boolean

      /** Is this symbol an anonymous function? */
      def isAnonymousFunction: Boolean

      /** Is this symbol an abstract type? */
      def isAbstractType: Boolean

      /** Is this the constructor of a class? */
      def isClassConstructor: Boolean

      /** Is this the definition of a type? */
      def isType: Boolean

      /** Is this the definition of a term? */
      def isTerm: Boolean

      /** Is this the definition of a PackageDef tree? */
      def isPackageDef: Boolean

      /** Is this the definition of a ClassDef tree? */
      def isClassDef: Boolean

      /** Is this the definition of a TypeDef tree */
      def isTypeDef: Boolean

      /** Is this the definition of a ValDef tree? */
      def isValDef: Boolean

      /** Is this the definition of a DefDef tree? */
      def isDefDef: Boolean

      /** Is this the definition of a Bind pattern? */
      def isBind: Boolean

      /** Does this symbol represent a no definition? */
      def isNoSymbol: Boolean

      /** Does this symbol represent a definition? */
      def exists: Boolean

      /** Fields directly declared in the class */
      def fields: List[Symbol]

      /** Field with the given name directly declared in the class */
      def field(name: String): Symbol

      /** Get non-private named methods defined directly inside the class */
      def classMethod(name: String): List[Symbol]

      /** Get all non-private methods defined directly inside the class, exluding constructors */
      def classMethods: List[Symbol]

      /** Type member directly declared in the class */
      def typeMembers: List[Symbol]

      /** Type member with the given name directly declared in the class */
      def typeMember(name: String): Symbol

      /** All members directly declared in the class */
      def members: List[Symbol]

      /** Get named non-private methods declared or inherited */
      def method(name: String): List[Symbol]

      /** Get all non-private methods declared or inherited */
      def methods: List[Symbol]

      /** The symbols of each type parameter list and value parameter list of this
        *  method, or Nil if this isn't a method.
        */
      def paramSymss: List[List[Symbol]]

      /** The primary constructor of a class or trait, `noSymbol` if not applicable. */
      def primaryConstructor: Symbol

      /** Fields of a case class type -- only the ones declared in primary constructor */
      def caseFields: List[Symbol]

      def isTypeParam: Boolean

      /** Signature of this definition */
      def signature: Signature

      /** The class symbol of the companion module class */
      def moduleClass: Symbol

      /** The symbol of the companion class */
      def companionClass: Symbol

      /** The symbol of the companion module */
      def companionModule: Symbol

      /** Shows the tree as extractors */
      def showExtractors: String

      /** Shows the tree as fully typed source code */
      def show: String

      /** Shows the tree as fully typed source code */
      def showWith(syntaxHighlight: SyntaxHighlight): String

      /** Case class or case object children of a sealed trait */
      def children: List[Symbol]
    end extension
  }

  ////////////////
  // SIGNATURES //
  ////////////////

  /** The signature of a method */
  type Signature <: AnyRef

  /** The signature of a method */
  val Signature: SignatureModule

  trait SignatureModule { this: Signature.type =>
    /** Matches the method signature and returns its parameters and result type. */
    def unapply(sig: Signature): Option[(List[String | Int], String)]
  }

  given SignatureMethods as SignatureMethods = SignatureMethodsImpl
  protected val SignatureMethodsImpl: SignatureMethods

  trait SignatureMethods {
    extension (self: Signature):

      /** The signatures of the method parameters.
        *
        *  Each *type parameter section* is represented by a single Int corresponding
        *  to the number of type parameters in the section.
        *  Each *term parameter* is represented by a String corresponding to the fully qualified
        *  name of the parameter type.
        */
      def paramSigs: List[String | Int]

      /** The signature of the result type */
      def resultSig: String

    end extension
  }

  //////////////////////////
  // STANDARD DEFINITIONS //
  //////////////////////////

  /** A value containing all standard definitions */
  val defn: DefnModule

  /** Defines standard symbols (and types via its base trait). */
  trait DefnModule { self: defn.type =>

    /** The module symbol of root package `_root_`. */
    def RootPackage: Symbol

    /** The class symbol of root package `_root_`. */
    def RootClass: Symbol

    /** The class symbol of empty package `_root_._empty_`. */
    def EmptyPackageClass: Symbol

    /** The module symbol of package `scala`. */
    def ScalaPackage: Symbol

    /** The class symbol of package `scala`. */
    def ScalaPackageClass: Symbol

    /** The class symbol of core class `scala.Any`. */
    def AnyClass: Symbol

    /** The class symbol of core class `scala.AnyVal`. */
    def AnyValClass: Symbol

    /** The class symbol of core class `java.lang.Object`. */
    def ObjectClass: Symbol

    /** The type symbol of core class `scala.AnyRef`. */
    def AnyRefClass: Symbol

    /** The class symbol of core class `scala.Null`. */
    def NullClass: Symbol

    /** The class symbol of core class `scala.Nothing`. */
    def NothingClass: Symbol

    /** The class symbol of primitive class `scala.Unit`. */
    def UnitClass: Symbol

    /** The class symbol of primitive class `scala.Byte`. */
    def ByteClass: Symbol

    /** The class symbol of primitive class `scala.Short`. */
    def ShortClass: Symbol

    /** The class symbol of primitive class `scala.Char`. */
    def CharClass: Symbol

    /** The class symbol of primitive class `scala.Int`. */
    def IntClass: Symbol

    /** The class symbol of primitive class `scala.Long`. */
    def LongClass: Symbol

    /** The class symbol of primitive class `scala.Float`. */
    def FloatClass: Symbol

    /** The class symbol of primitive class `scala.Double`. */
    def DoubleClass: Symbol

    /** The class symbol of primitive class `scala.Boolean`. */
    def BooleanClass: Symbol

    /** The class symbol of class `scala.String`. */
    def StringClass: Symbol

    /** The class symbol of class `java.lang.Class`. */
    def ClassClass: Symbol

    /** The class symbol of class `scala.Array`. */
    def ArrayClass: Symbol

    /** The module symbol of module `scala.Predef`. */
    def PredefModule: Symbol

    /** The method symbol of method `scala.Predef.classOf`. */
    def Predef_classOf: Symbol

    /** The module symbol of package `java.lang`. */
    def JavaLangPackage: Symbol

    /** The module symbol of module `scala.Array`. */
    def ArrayModule: Symbol

    /** The method symbol of method `apply` in class `scala.Array`. */
    def Array_apply: Symbol

    /** The method symbol of method `clone` in class `scala.Array`. */
    def Array_clone: Symbol

    /** The method symbol of method `length` in class `scala.Array`. */
    def Array_length: Symbol

    /** The method symbol of method `update` in class `scala.Array`. */
    def Array_update: Symbol

    /** A dummy class symbol that is used to indicate repeated parameters
     *  compiled by the Scala compiler.
     */
    def RepeatedParamClass: Symbol

    /** The class symbol of class `scala.annotation.reflection.Repeated` */
    def RepeatedAnnot: Symbol

    /** The class symbol of class `scala.Option`. */
    def OptionClass: Symbol

    /** The module symbol of module `scala.None`. */
    def NoneModule: Symbol

    /** The module symbol of module `scala.Some`. */
    def SomeModule: Symbol

    /** Function-like object that maps arity to symbols for classes `scala.Product` */
    def ProductClass: Symbol

    /** Function-like object that maps arity to symbols for classes `scala.FunctionX`.
     *   -  0th element is `Function0`
     *   -  1st element is `Function1`
     *   -  ...
     *   -  Nth element is `FunctionN`
     */
    def FunctionClass(arity: Int, isImplicit: Boolean = false, isErased: Boolean = false): Symbol

    /** Function-like object that maps arity to symbols for classes `scala.TupleX`.
     *   -  0th element is `NoSymbol`
     *   -  1st element is `NoSymbol`
     *   -  2st element is `Tuple2`
     *   -  ...
     *   - 22nd element is `Tuple22`
     *   - 23nd element is `NoSymbol`  // TODO update when we will have more tuples
     *   - ...
     */
    def TupleClass(arity: Int): Symbol

    /** Returns `true` if `sym` is a `Tuple1`, `Tuple2`, ... `Tuple22` */
    def isTupleClass(sym: Symbol): Boolean

    /** Contains Scala primitive value classes:
     *   - Byte
     *   - Short
     *   - Int
     *   - Long
     *   - Float
     *   - Double
     *   - Char
     *   - Boolean
     *   - Unit
     */
    def ScalaPrimitiveValueClasses: List[Symbol]

    /** Contains Scala numeric value classes:
     *   - Byte
     *   - Short
     *   - Int
     *   - Long
     *   - Float
     *   - Double
     *   - Char
     */
    def ScalaNumericValueClasses: List[Symbol]

  }

  ///////////////
  //   FLAGS   //
  ///////////////

  /** FlagSet of a Symbol */
  type Flags

  val Flags: FlagsModule

  trait FlagsModule { this: Flags.type =>

    /** Is this symbol `abstract` */
    def Abstract: Flags

    /** Was this symbol generated by Scala compiler */
    def Artifact: Flags

    /** Is this symbol `case` */
    def Case: Flags

    /** Is this symbol a getter for case class parameter */
    def CaseAccessor: Flags

    /** Is this symbol a type parameter marked as contravariant `-` */
    def Contravariant: Flags

    /** Is this symbol a type parameter marked as covariant `+` */
    def Covariant: Flags

    /** The empty set of flags */
    def EmptyFlags: Flags

    /** Is this symbol an enum */
    def Enum: Flags

    /** Is this symbol `erased` */
    def Erased: Flags

    /** Is this symbol a `def` defined in an `extension` */
    def ExtensionMethod: Flags

    /** Is this symbol a getter or a setter */
    def FieldAccessor: Flags

    /** Is this symbol `final` */
    def Final: Flags

    /** Is this symbol an inferable ("given") parameter */
    def Given: Flags

    /** Is this symbol a parameter with a default value? */
    def HasDefault: Flags

    /** Is this symbol `implicit` */
    def Implicit: Flags

    /** Is this symbol `inline` */
    def Inline: Flags

    /** Is this symbol defined in a Java class */
    def JavaDefined: Flags

    /** Is this symbol `lazy` */
    def Lazy: Flags

    /** Is this symbol local? Used in conjunction with private/private[Type] to mean private[this] extends Modifier proctected[this] */
    def Local: Flags

    /** Is this symbol marked as a macro. An inline method containing toplevel splices */
    def Macro: Flags

    /** Is this symbol a module class */
    def ModuleClass: Flags

    /** Is this symbol a `var` (when used on a ValDef) */
    def Mutable: Flags

    /** Is this symbol an object or its class (used for a ValDef or a ClassDef extends Modifier respectively) */
    def Object: Flags

    /** Is this symbol `override` */
    def Override: Flags

    /** Is this symbol a package */
    def Package: Flags

    /** Is this symbol a parameter */
    def Param: Flags

    /** Is this symbol a parameter accessor */
    def ParamAccessor: Flags

    /** Is this symbol `private` */
    def Private: Flags

    /** Is this symbol labeled private[this] */
    def PrivateLocal: Flags

    /** Is this symbol `protected` */
    def Protected: Flags

    /** Was this symbol imported from Scala2.x */
    def Scala2x: Flags

    /** Is this symbol `sealed` */
    def Sealed: Flags

    /** Is this symbol member that is assumed to be stable and realizable */
    def StableRealizable: Flags

    /** Is this symbol marked as static. Mapped to static Java member */
    def Static: Flags

    /** Is this symbol to be tagged Java Synthetic */
    def Synthetic: Flags

    /** Is this symbol a trait */
    def Trait: Flags
  }

  given FlagsMethods as FlagsMethods = FlagsMethodsImpl
  protected val FlagsMethodsImpl: FlagsMethods

  trait FlagsMethods {
    extension (self: Flags):
      /** Is the given flag set a subset of this flag sets */
      def is(that: Flags): Boolean

      /** Union of the two flag sets */
      def |(that: Flags): Flags

      /** Intersection of the two flag sets */
      def &(that: Flags): Flags

      /** Shows the tree as extractors */
      def showExtractors: String

      /** Shows the tree as fully typed source code */
      def show: String

      /** Shows the tree as fully typed source code */
      def showWith(syntaxHighlight: SyntaxHighlight): String

    end extension
  }

  ///////////////
  // POSITIONS //
  ///////////////

  // TODO: Should this be in the QuoteContext?
  // TODO: rename to enclosingPosition (as in scala.reflect)
  /** Root position of this tasty context. For macros it corresponds to the expansion site. */
  def rootPosition: Position

  /** Position in a source file */
  type Position <: AnyRef

  val Position: PositionModule

  trait PositionModule { this: Position.type => }

  given PositionMethods as PositionMethods = PositionMethodsImpl
  protected val PositionMethodsImpl: PositionMethods

  trait PositionMethods {
    extension (self: Position):

      /** The start offset in the source file */
      def start: Int

      /** The end offset in the source file */
      def end: Int

      /** Does this position exist */
      def exists: Boolean

      /** Source file in which this position is located */
      def sourceFile: SourceFile

      /** The start line in the source file */
      def startLine: Int

      /** The end line in the source file */
      def endLine: Int

      /** The start column in the source file */
      def startColumn: Int

      /** The end column in the source file */
      def endColumn: Int

      /** Source code within the position */
      def sourceCode: String

    end extension
  }

  /** Scala source file */
  type SourceFile <: AnyRef

  val SourceFile: SourceFileModule

  trait SourceFileModule { this: SourceFile.type => }

  given SourceFileMethods as SourceFileMethods = SourceFileMethodsImpl
  protected val SourceFileMethodsImpl: SourceFileMethods

  trait SourceFileMethods {
    extension (self: SourceFile):
      /** Path to this source file */
      def jpath: java.nio.file.Path

      /** Content of this source file */
      def content: String
    end extension
  }

  ///////////////
  //   Source  //
  ///////////////

  val Source: SourceModule

  trait SourceModule { this: Source.type =>

    /** Returns the source file being compiled. The path is relative to the current working directory. */
    def path: java.nio.file.Path

    /** Returns true if we've tried to reflect on a Java class. */
    def isJavaCompilationUnit: Boolean

    /** Returns true if we've tried to reflect on a Scala2 (non-Tasty) class. */
    def isScala2CompilationUnit: Boolean

    /** Returns true if we've tried to reflect on a class that's already loaded (e.g. Option). */
    def isAlreadyLoadedCompilationUnit: Boolean

    /** Class name of the current CompilationUnit */
    def compilationUnitClassname: String
  }

  ///////////////
  // REPORTING //
  ///////////////

  val Reporting: ReportingModule

  /** Module containg error and waring reporiting.
   *
   *  Also see scala.quoted.report
   */
  trait ReportingModule { self: Reporting.type =>
    /** Emits an error message */
    def error(msg: => String, pos: Position): Unit

    /** Emits an error at a specific range of a file */
    def error(msg: => String, source: SourceFile, start: Int, end: Int): Unit

    /** Emits an error message */
    def warning(msg: => String, pos: Position): Unit

    /** Emits a warning at a specific range of a file */
    def warning(msg: => String, source: SourceFile, start: Int, end: Int): Unit
  }


  ///////////////////
  // DOCUMENTATION //
  ///////////////////

  /** Attachment representing the documentation of a definition */
  type Documentation <: AnyRef

  val Documentation: DocumentationModule

  trait DocumentationModule { this: Documentation.type => }

  given DocumentationMethods as DocumentationMethods = DocumentationMethodsImpl
  protected val DocumentationMethodsImpl: DocumentationMethods

  trait DocumentationMethods {
    extension (self: Documentation):
      /** Raw documentation string */
      def raw: String

      /** Expanded documentation string, if any */
      def expanded: Option[String]

      /** List of usecases and their corresponding trees, if any */
      def usecases: List[(String, Option[DefDef])]

    end extension
  }

  ///////////////
  //   UTILS   //
  ///////////////

  /** TASTy Reflect tree accumulator */
  trait TreeAccumulator[X] extends reflect.TreeAccumulator[X] {
    val reflect: reflection.type = reflection
  }

  /** TASTy Reflect tree traverser */
  trait TreeTraverser extends reflect.TreeTraverser {
    val reflect: reflection.type = reflection
  }

  /** TASTy Reflect tree map */
  trait TreeMap extends reflect.TreeMap {
    val reflect: reflection.type = reflection
  }

  // TODO: extract from Reflection

  /** Bind the `rhs` to a `val` and use it in `body` */
  def let(rhs: Term)(body: Ident => Term): Term = {
    val sym = Symbol.newVal(Symbol.currentOwner, "x", rhs.tpe.widen, Flags.EmptyFlags, Symbol.noSymbol)
    Block(List(ValDef(sym, Some(rhs))), body(Ref(sym).asInstanceOf[Ident]))
  }

  /** Bind the given `terms` to names and use them in the `body` */
  def lets(terms: List[Term])(body: List[Term] => Term): Term = {
    def rec(xs: List[Term], acc: List[Term]): Term = xs match {
      case Nil => body(acc)
      case x :: xs => let(x) { (x: Term) => rec(xs, x :: acc) }
    }
    rec(terms, Nil)
  }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy