aboutsummaryrefslogtreecommitdiff
path: root/compiler/test/dotty/tools/dotc/reporting/TestReporter.scala
blob: 213181b5605db2e484d206f018e9ba19da7ead67 (plain)
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
152
package dotty.tools
package dotc
package reporting

import java.io.{ PrintStream, PrintWriter, File => JFile, FileOutputStream }
import java.text.SimpleDateFormat
import java.util.Date

import scala.collection.mutable

import util.SourcePosition
import core.Contexts._
import Reporter._
import diagnostic.{ Message, MessageContainer, NoExplanation }
import diagnostic.messages._
import interfaces.Diagnostic.{ ERROR, WARNING, INFO }

class TestReporter protected (outWriter: PrintWriter, filePrintln: String => Unit, logLevel: Int)
extends Reporter with UniqueMessagePositions with HideNonSensicalMessages with MessageRendering {
  import MessageContainer._

  protected final val _errorBuf = mutable.ArrayBuffer.empty[MessageContainer]
  final def errors: Iterator[MessageContainer] = _errorBuf.iterator

  protected final val _messageBuf = mutable.ArrayBuffer.empty[String]
  final def messages: Iterator[String] = _messageBuf.iterator

  private[this] var _didCrash = false
  final def compilerCrashed: Boolean = _didCrash

  final def flushToFile(): Unit =
    _messageBuf
      .iterator
      .map(_.replaceAll("\u001b\\[.*?m", ""))
      .foreach(filePrintln)

  final def flushToStdErr(): Unit =
    _messageBuf
      .iterator
      .foreach(System.err.println)

  final def inlineInfo(pos: SourcePosition): String =
    if (pos.exists) {
      if (pos.outer.exists)
        s"\ninlined at ${pos.outer}:\n" + inlineInfo(pos.outer)
      else ""
    }
    else ""

  def log(msg: String) =
    _messageBuf.append(msg)

  def logStackTrace(thrown: Throwable): Unit = {
    _didCrash = true
    val sw = new java.io.StringWriter
    val pw = new java.io.PrintWriter(sw)
    thrown.printStackTrace(pw)
    log(sw.toString)
  }

  /** Prints the message with the given position indication. */
  def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = {
    val msg = messageAndPos(m.contained, m.pos, diagnosticLevel(m))
    val extraInfo = inlineInfo(m.pos)

    if (m.level >= logLevel) {
      outWriter.println(msg)
      if (extraInfo.nonEmpty) outWriter.println(extraInfo)
    }

    _messageBuf.append(msg)
    if (extraInfo.nonEmpty) _messageBuf.append(extraInfo)
  }

  override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = {
    // Here we add extra information that we should know about the error message
    val extra = m.contained match {
      case pm: PatternMatchExhaustivity => s": ${pm.uncovered}"
      case _ => ""
    }

    m match {
      case m: Error => {
        _errorBuf.append(m)
        printMessageAndPos(m, extra)
      }
      case m =>
        printMessageAndPos(m, extra)
    }
  }
}

object TestReporter {
  private[this] var outFile: JFile = _
  private[this] var logWriter: PrintWriter = _

  private[this] def initLog() = if (logWriter eq null) {
    val df = new SimpleDateFormat("yyyy-MM-dd-HH:mm")
    val timestamp = df.format(new Date)
    new JFile("../testlogs").mkdirs()
    outFile = new JFile(s"../testlogs/tests-$timestamp.log")
    logWriter = new PrintWriter(new FileOutputStream(outFile, true))
  }

  def logPrintln(str: String) = {
    initLog()
    logWriter.println(str)
    logWriter.flush()
  }

  def logPrint(str: String): Unit = {
    initLog()
    logWriter.println(str)
  }

  def logFlush(): Unit =
    if (logWriter ne null) logWriter.flush()

  def logPath: String = {
    initLog()
    outFile.getCanonicalPath
  }

  def reporter(ps: PrintStream, logLevel: Int): TestReporter =
    new TestReporter(new PrintWriter(ps, true), logPrintln, logLevel)

  def simplifiedReporter(writer: PrintWriter): TestReporter = {
    val rep = new TestReporter(writer, logPrintln, WARNING) {
      /** Prints the message with the given position indication in a simplified manner */
      override def printMessageAndPos(m: MessageContainer, extra: String)(implicit ctx: Context): Unit = {
        def report() = {
          val msg = s"${m.pos.line + 1}: " + m.contained.kind + extra
          val extraInfo = inlineInfo(m.pos)

          writer.println(msg)
          _messageBuf.append(msg)

          if (extraInfo.nonEmpty) {
            writer.println(extraInfo)
            _messageBuf.append(extraInfo)
          }
        }
        m match {
          case m: Error => report()
          case m: Warning => report()
          case _ => ()
        }
      }
    }
    rep
  }
}