/* NSC -- new Scala compiler
* Copyright 2005-2013 LAMP/EPFL
* @author Paul Phillips
*/
package scala
package reflect
package internal
import util._
trait TypeDebugging {
self: SymbolTable =>
import definitions._
/** There's a whole lot of implementation detail which is nothing but noise when
* you are trying to see what's going on. This is my attempt to filter it out.
*/
object noPrint extends (Tree => Boolean) {
def skipScalaName(name: Name) = name match {
case tpnme.Any | tpnme.Nothing | tpnme.AnyRef => true
case _ => false
}
def skipRefTree(t: RefTree) = t match {
case Select(Select(Ident(nme.ROOTPKG), nme.scala_), name) if skipScalaName(name) => true
case Select(sel, name) if sel.symbol == ScalaPackage && skipScalaName(name) => true
case Select(Super(This(tpnme.EMPTY), tpnme.EMPTY), nme.CONSTRUCTOR) => true
case Ident(nme.ROOTPKG) => true
case _ => skipSym(t.symbol)
}
def skipSym(sym: Symbol): Boolean = sym match {
case null => false
case NothingClass | AnyClass => true
case PredefModule => true
case ObjectClass => true
case _ => sym.hasPackageFlag
}
def skipType(tpe: Type): Boolean = (tpe eq null) || skipSym(tpe.typeSymbolDirect)
def skip(t: Tree): Boolean = t match {
case EmptyTree => true
case PackageDef(_, _) => true
case t: RefTree => skipRefTree(t)
case TypeBoundsTree(lo, hi) => skip(lo) && skip(hi)
case Block(Nil, expr) => skip(expr)
case Apply(fn, Nil) => skip(fn)
case Block(stmt :: Nil, expr) => skip(stmt) && skip(expr)
case DefDef(_, nme.CONSTRUCTOR, Nil, ListOfNil, _, rhs) => skip(rhs)
case Literal(Constant(())) => true
case tt @ TypeTree() => skipType(tt.tpe)
case _ => skipSym(t.symbol)
}
def apply(t: Tree) = skip(t)
}
/** Light color wrappers.
*/
object typeDebug {
import scala.Console._
private val colorsOk = sys.props contains "scala.color"
private def inColor(s: String, color: String) = if (colorsOk && s != "") color + s + RESET else s
private def inBold(s: String, color: String) = if (colorsOk && s != "") color + BOLD + s + RESET else s
def inLightRed(s: String) = inColor(s, RED)
def inLightGreen(s: String) = inColor(s, GREEN)
def inLightMagenta(s: String) = inColor(s, MAGENTA)
def inLightCyan(s: String): String = inColor(s, CYAN)
def inGreen(s: String): String = inBold(s, GREEN)
def inRed(s: String): String = inBold(s, RED)
def inBlue(s: String): String = inBold(s, BLUE)
def inCyan(s: String): String = inBold(s, CYAN)
def inMagenta(s: String) = inBold(s, MAGENTA)
def resetColor(s: String): String = if (colorsOk) s + RESET else s
private def to_s(x: Any): String = x match {
// otherwise case classes are caught looking like products
case _: Tree | _: Type => "" + x
case x: TraversableOnce[_] => x mkString ", "
case x: Product => x.productIterator mkString ("(", ", ", ")")
case _ => "" + x
}
def ptBlock(label: String, pairs: (String, Any)*): String = {
if (pairs.isEmpty) label + "{ }"
else {
val width = (pairs map (_._1.length)).max
val fmt = "%-" + (width + 1) + "s %s"
val strs = pairs map { case (k, v) => fmt.format(k, to_s(v)) }
strs.mkString(label + " {\n ", "\n ", "\n}")
}
}
def ptLine(pairs: (String, Any)*): String = (
pairs
map { case (k, v) => (k, to_s(v)) }
filterNot { case (_, v) => v == "" }
map { case ("", v) => v ; case (k, v) => s"$k=$v" }
mkString ", "
)
def ptTree(t: Tree): String = t match {
case PackageDef(pid, _) => s"package $pid"
case ModuleDef(_, name, _) => s"object $name"
case DefDef(_, name, tparams, _, _, _) => "def " + name + ptTypeParams(tparams)
case ClassDef(_, name, Nil, _) if t.symbol != null && t.symbol.isModuleClass => s"module class $name"
case ClassDef(_, name, tparams, _) => "class " + name + ptTypeParams(tparams)
case td: TypeDef => ptTypeParam(td)
case TypeBoundsTree(lo, hi) =>
val lo_s = if (noPrint(lo)) "" else " >: " + ptTree(lo)
val hi_s = if (noPrint(hi)) "" else " <: " + ptTree(hi)
lo_s + hi_s
case _ if (t.symbol eq null) || (t.symbol eq NoSymbol) => to_s(t)
case _ => "" + t.symbol.tpe
}
def ptTypeParam(td: TypeDef): String = {
val TypeDef(mods, name, tparams, rhs) = td
name + ptTypeParams(tparams) + ptTree(rhs)
}
def ptTypeParams(tparams: List[TypeDef]): String = str brackets (tparams map ptTypeParam)
object str {
def parentheses(xs: List[_]): String = xs.mkString("(", ", ", ")")
def brackets(xs: List[_]): String = if (xs.isEmpty) "" else xs.mkString("[", ", ", "]")
def tparams(tparams: List[Type]): String = brackets(tparams map debug)
def parents(ps: List[Type]): String = (ps map debug).mkString(" with ")
def refine(defs: Scope): String = defs.toList.mkString("{", " ;\n ", "}")
def bounds(lo: Type, hi: Type): String = {
val lo_s = if (typeIsNothing(lo)) "" else s" >: $lo"
val hi_s = if (typeIsAny(hi)) "" else s" <: $hi"
lo_s + hi_s
}
}
import str._
private def debug(tp: Type): String = tp match {
case TypeRef(pre, sym, args) => s"${debug(pre)}.${sym.nameString}.${tparams(args)}"
case ThisType(sym) => s"${sym.nameString}.this"
case SingleType(pre, sym) => s"${debug(pre)}.${sym.nameString}.type"
case RefinedType(ps, decls) => s"${parents(ps)} ${refine(decls)}"
case ClassInfoType(ps, decls, clazz) => s"class ${clazz.nameString} ${parents(ps)} ${refine(decls)}"
case PolyType(tparams, result) => s"${brackets(tparams)}${debug(result)}"
case TypeBounds(lo, hi) => bounds(lo, hi)
case tv @ TypeVar(_, _) => "" + tv
case ExistentialType(tparams, qtpe) => s"forSome ${brackets(tparams)} ${debug(qtpe)}"
case _ => s"?${shortClassOfInstance(tp)}?" // tp.toString might produce cyclic error...
}
def debugString(tp: Type) = debug(tp)
}
def paramString(tp: Type) = typeDebug.str parentheses (tp.params map (_.defString))
def typeParamsString(tp: Type) = typeDebug.str brackets (tp.typeParams map (_.defString))
def typeArgsString(tp: Type) = typeDebug.str brackets (tp.typeArgs map (_.safeToString))
def debugString(tp: Type) = typeDebug debugString tp
}