alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Scala example source code file (TreeCheckers.scala)

This example Scala source code file (TreeCheckers.scala) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Scala tags/keywords

compilationunit, context, emptytree, list, nosymbol, nosymbol, phasemap, symbol, symbol, tree, tree, type, unit, unit

The Scala TreeCheckers.scala source code

/* NSC -- new Scala compiler
 * Copyright 2005-2011 LAMP/EPFL
 * @author  Martin Odersky
 */

package scala.tools.nsc
package typechecker

import scala.tools.nsc.symtab.Flags._
import scala.collection.mutable
import mutable.{ HashMap, HashSet, ListBuffer }
import util.returning

abstract class TreeCheckers extends Analyzer {
  import global._
  
  private def classstr(x: AnyRef) = x.getClass.getName split """\\.|\\$""" last;
  private def typestr(x: Type)    = " (tpe = " + x + ")"
  private def treestr(t: Tree)    = t + " [" + classstr(t) + "]" + typestr(t.tpe)
  private def ownerstr(s: Symbol) = "'" + s + "'" + s.locationString
  private def wholetreestr(t: Tree) = nodeToString(t) + "\n"
  
  private def beststr(t: Tree) = "<" + {
    if (t.symbol != null && t.symbol != NoSymbol) "sym=" + ownerstr(t.symbol)
    else if (t.tpe.isComplete) "tpe=" + typestr(t.tpe)
    else t match {
      case x: DefTree => "name=" + x.name
      case x: RefTree => "reference=" + x.name
      case _          => "clazz=" + classstr(t)
    }
  } + ">"
    
  /** This is a work in progress, don't take it too seriously.
   */
  object SymbolTracker extends Traverser {
    type PhaseMap = HashMap[Symbol, List[Tree]]
    val maps: ListBuffer[(Phase, PhaseMap)] = ListBuffer()
    def prev          = maps.init.last._2
    def latest        = maps.last._2
    val defSyms       = new HashMap[Symbol, List[DefTree]]
    val newSyms       = new HashSet[Symbol]
    val movedMsgs     = new ListBuffer[String]
    def sortedNewSyms = newSyms.toList.distinct sortBy (_.name.toString)
    
    def inPrev(sym: Symbol) = {
      (maps.size >= 2) && (prev contains sym)
    }
    def record(sym: Symbol, tree: Tree) = {
      if (latest contains sym) latest(sym) = latest(sym) :+ tree
      else latest(sym) = List(tree)
      
      if (inPrev(sym)) {
        val prevTrees = prev(sym)
                
        if (prevTrees exists (t => (t eq tree) || (t.symbol == sym))) ()
        else if (prevTrees exists (_.symbol.owner == sym.owner.implClass)) {
          errorFn("Noticed " + ownerstr(sym) + " moving to implementation class.")
        }
        else {
          val s1 = (prevTrees map wholetreestr).sorted.distinct
          val s2 = wholetreestr(tree)
          if (s1 contains s2) ()
          else movedMsgs += ("\n** %s moved:\n** Previously:\n%s\n** Currently:\n%s".format(ownerstr(sym), s1 mkString ", ", s2))
        }
      }
      else newSyms += sym
    }
    def reportChanges(): Unit = {
      // new symbols
      if (newSyms.nonEmpty) {
        val str = 
          if (settings.debug.value) "New symbols: " + (sortedNewSyms mkString " ")
          else newSyms.size + " new symbols."
          
        newSyms.clear()
        errorFn(str)
      }
      
      // moved symbols
      movedMsgs foreach errorFn
      movedMsgs.clear()
      
      // duplicate defs
      for ((sym, defs) <- defSyms ; if defs.size > 1) {
        errorFn("%s DefTrees with symbol '%s': %s".format(defs.size, ownerstr(sym), defs map beststr mkString ", "))
      }
      defSyms.clear()
    }
    
    def check(ph: Phase, unit: CompilationUnit): Unit = {
      if (maps.isEmpty || maps.last._1 != ph)
        maps += ((ph, new PhaseMap))
      
      traverse(unit.body)
      reportChanges()
    }
    override def traverse(tree: Tree): Unit = {
      val sym    = tree.symbol
      if (sym != null && sym != NoSymbol) {
        record(sym, tree)
        tree match {
          case x: DefTree =>
            if (defSyms contains sym) defSyms(sym) = defSyms(sym) :+ x
            else defSyms(sym) = List(x)
          case _ => ()
        }
      }
      
      super.traverse(tree)
    }
  }

  lazy val tpeOfTree = new HashMap[Tree, Type]
  
  def posstr(p: Position) = 
    try p.source.path + ":" + p.line
    catch { case _: UnsupportedOperationException => p.toString }
  
  def errorFn(msg: Any): Unit                = println("[check: %s] %s".format(phase.prev, msg))
  def errorFn(pos: Position, msg: Any): Unit = errorFn(posstr(pos) + ": " + msg)

  def assertFn(cond: Boolean, msg: => Any) =
    if (!cond) errorFn(msg)

  def checkTrees() {
    if (settings.verbose.value)
      Console.println("[consistency check at the beginning of phase " + phase + "]")
    
    currentRun.units foreach check
  }
  
  def printingTypings[T](body: => T): T = {
    val saved = global.printTypings
    global.printTypings = true
    val result = body
    global.printTypings = saved
    result
  }
  def runWithUnit[T](unit: CompilationUnit)(body: => Unit): Unit = {
    val unit0 = currentRun.currentUnit
    currentRun.currentUnit = unit
    body
    currentRun.advanceUnit
    assertFn(currentRun.currentUnit == unit, "currentUnit is " + currentRun.currentUnit + ", but unit is " + unit)
    currentRun.currentUnit = unit0
  }
  def check(unit: CompilationUnit) {
    informProgress("checking "+unit)
    val context = rootContext(unit)
    context.checking = true
    tpeOfTree.clear
    SymbolTracker.check(phase, unit)
    val checker = new TreeChecker(context)
    runWithUnit(unit) {
      checker.precheck.traverse(unit.body)
      checker.typed(unit.body)
      checker.postcheck.traverse(unit.body)
    }
  }

  override def newTyper(context: Context): Typer = new TreeChecker(context)

  class TreeChecker(context0: Context) extends Typer(context0) {
    override protected def typerAddSyntheticMethods(templ: Template, clazz: Symbol, context: Context): Template = {
      // If we don't intercept this all the synthetics get added at every phase,
      // with predictably unfortunate results.
      templ
    }
    
    // XXX check for tree.original on TypeTrees.
    private def treesDiffer(t1: Tree, t2: Tree) =
      errorFn(t1.pos, "trees differ\n old: " + treestr(t1) + "\n new: " + treestr(t2))
    private def typesDiffer(tree: Tree, tp1: Type, tp2: Type) =
      errorFn(tree.pos, "types differ\n old: " + tp1 + "\n new: " + tp2 + "\n tree: " + tree)
    private def ownersDiffer(tree: Tree, shouldBe: Symbol) = {
      val sym = tree.symbol
      errorFn(tree.pos, sym + " has wrong owner: " + ownerstr(sym.owner) + ", should be: " + ownerstr(shouldBe))
    }
    
    /** XXX Disabled reporting of position errors until there is less noise. */
    private def noPos(t: Tree) =
      () // errorFn("no pos: " + treestr(t))
    private def noType(t: Tree) =
      errorFn(t.pos, "no type: " + treestr(t))

    private def checkSym(t: Tree) =
      if (t.symbol == NoSymbol)
        errorFn(t.pos, "no symbol: " + treestr(t))

    override def typed(tree: Tree, mode: Int, pt: Type): Tree = returning(tree) {
      case EmptyTree | TypeTree() => ()
      case _ if tree.tpe != null  =>
        tpeOfTree.getOrElseUpdate(tree, {
          val saved = tree.tpe
          tree.tpe = null
          saved
        })
        super.typed(tree, mode, pt) match {
          case _: Literal     => ()
          case x if x ne tree => treesDiffer(tree, x)
          case _              => ()
        }
      case _ => ()
    }

    object precheck extends Traverser {
      override def traverse(tree: Tree) {
        val sym = tree.symbol
        def accessed = sym.accessed
        def fail(msg: String) = errorFn(tree.pos, msg + classstr(tree) + " / " + tree)
        
        tree match {
          case DefDef(_, _, _, _, _, _) =>
            if (sym.hasAccessorFlag && !sym.isDeferred) {
              sym.tpe.resultType match {
                case _: ConstantType  => ()
                case _                =>
                  checkSym(tree)
                  /** XXX: lots of syms show up here with accessed == NoSymbol. */
                  if (accessed != NoSymbol) {
                    val agetter = accessed.getter(sym.owner)
                    val asetter = accessed.setter(sym.owner)

                    assertFn(agetter == sym || asetter == sym,
                      sym + " is getter or setter, but accessed sym " + accessed + " shows " + agetter + " and " + asetter
                    )
                  }
              }
            }
          case ValDef(_, _, _, _) =>
            if (sym.hasGetter && !sym.isOuterField) {
              assertFn(sym.getter(sym.owner) != NoSymbol, ownerstr(sym) + " has getter but cannot be found. " + sym.ownerChain)
            }
          case Apply(fn, args) =>
            if (args exists (_ == EmptyTree))
              errorFn(tree.pos, "Apply arguments to " + fn + " contains an empty tree: " + args)
              
          case Select(qual, name) =>
            checkSym(tree)
          case This(_) =>
            checkSym(tree)
            if (sym.isStatic && sym.hasModuleFlag) ()
            else if (currentOwner.ownerChain takeWhile (_ != sym) exists (_ == NoSymbol))
              return fail("tree symbol "+sym+" does not point to enclosing class; tree = ")
          
          /** XXX: temporary while Import nodes are arriving untyped. */
          case Import(_, _) =>
            return
          case _ =>
        }

        if (tree.pos == NoPosition && tree != EmptyTree)
          noPos(tree)
        else if (tree.tpe == null && phase.id > currentRun.typerPhase.id)
          noType(tree)
        else if (tree.isDef) {
          checkSym(tree)
          
          tree match {
            case x: PackageDef    =>
              if (sym.ownerChain contains currentOwner) ()
              else fail(sym + " owner chain does not contain currentOwner " + currentOwner)
            case _ =>
              def cond(s: Symbol) = !s.isTerm || s.isMethod || s == sym.owner

              if (sym.owner != currentOwner) {
                val expected = currentOwner.ownerChain find (x => cond(x)) getOrElse fail("DefTree can't find owner: ")
                if (sym.owner != expected)
                  fail("Expected owner %s (out of %s), found %s: ".format(expected, currentOwner.ownerChain, sym.owner))
              }
          }
        }
        super.traverse(tree)
      }
    }

    object postcheck extends Traverser {
      override def traverse(tree: Tree) {
        tree match {
          case EmptyTree | TypeTree() => ()
          case _ =>
            tpeOfTree get tree foreach { oldtpe =>
              if (oldtpe =:= tree.tpe) ()
              else typesDiffer(tree, oldtpe, tree.tpe)
              
              tree.tpe = oldtpe
              super.traverse(tree)
            }
        }
      }
    }
  }
}

Other Scala examples (source code examples)

Here is a short list of links related to this Scala TreeCheckers.scala source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.