import scala.tools.partest._
import scala.tools.nsc._

object Test extends DirectTest {

  override def extraSettings: String = "-usejavacp"

  def code = """
    class testAnn extends annotation.TypeConstraint

    class A(param: Double) extends { val x: Int = 1; val y = "two"; type T = A } with AnyRef {
      val inferField = ("str": @testAnn)
      val annotField: Boolean @testAnn = false

      val lub1 = List('c', (1: Int @testAnn), "")
      val lub2 = if (annotField) (1: @testAnn) else 2

      def foo[T <: Int] = 0
      foo[Int @testAnn]

      var count = 0

      math.random // some statement

      def method: String = {
        math.random
        val f = inferField

        def nested(): String = {
          if(count == 1)
            return f
          "huhu"
        }
        nested()
      }

      def this(str: String) {
        this(str.toDouble)
        math.random
        count += 1
      }
    }
  """.trim


  def show() {
    val global = newCompiler()
    import global._
    import analyzer._

    val output = collection.mutable.ListBuffer[String]()

    object annotChecker extends AnnotationChecker {
      def hasTestAnn(tps: Type*) = {
        tps exists (_.annotations.map(_.toString) contains "testAnn")
      }

      def annotationsConform(tpe1: Type, tpe2: Type): Boolean = {
        if (hasTestAnn(tpe1, tpe2))
          output += s"annotationsConform($tpe1, $tpe2)"
        true
      }

      override def annotationsLub(tp: Type, ts: List[Type]): Type = {
        if (hasTestAnn(ts: _*))
          output += s"lub($ts)"
        tp
      }

      override def adaptBoundsToAnnotations(bounds: List[TypeBounds], tparams: List[Symbol], targs: List[Type]): List[TypeBounds] = {
        if (hasTestAnn(targs: _*))
          output += s"adaptBoundsToAnnots($bounds, $tparams, $targs)"
        bounds
      }
    }

    object analyzerPlugin extends AnalyzerPlugin {
      def treeClass(t: Tree) = t.getClass.toString.split('.').last

      override def pluginsPt(pt: Type, typer: Typer, tree: Tree, mode: Int): Type = {
        output += s"pluginsPt($pt, ${treeClass(tree)})"
        pt
      }
  
      override def pluginsTyped(tpe: Type, typer: Typer, tree: Tree, mode: Int, pt: Type): Type = {
        output += s"pluginsTyped($tpe, ${treeClass(tree)})"
        tpe
      }

      override def pluginsTypeSig(tpe: Type, typer: Typer, defTree: Tree, pt: Type): Type = {
        output += s"pluginsTypeSig(${defTree.symbol}, ${treeClass(defTree)})"
        tpe
      }

      override def pluginsTypeSigAccessor(tpe: Type, typer: Typer, tree: ValDef, sym: Symbol): Type = {
        output += s"pluginsTypeSigAccessor(${tree.symbol})"
        tpe
      }


      override def canAdaptAnnotations(tree: Tree, typer: Typer, mode: Int, pt: Type): Boolean = {
        output += s"canAdaptAnnotations(${treeClass(tree)}, $pt)"
        false
      }

      override def pluginsTypedReturn(tpe: Type, typer: Typer, tree: Return, pt: Type): Type = {
        output += s"pluginsTypedReturn($tree, $pt)"
        tpe
      }

    }

    addAnnotationChecker(annotChecker)
    addAnalyzerPlugin(analyzerPlugin)
    compileString(global)(code)

    val res = output.groupBy(identity).mapValues(_.size).map { case (k,v) => s"$k [$v]" }.toList.sorted
    println(res.mkString("\n"))
  }

}
