summaryrefslogtreecommitdiff
path: root/src/reflect/scala/reflect/internal/TypeDebugging.scala
blob: 58359e66d92a98276239d8da3ea89c2e8aff0609 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/* 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 = scala.util.Properties.coloredOutputEnabled
    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.rawInfo.safeToString
    }
    def ptTypeParam(td: TypeDef): String = {
      val TypeDef(_, 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 debugString(tp: Type)      = typeDebug debugString tp
}