diff --git a/src/main/scala/leon/Reporter.scala b/src/main/scala/leon/Reporter.scala index e5015a46dc12553db9f3f6c7086aa90943c76814..4e4963bdbc22c6a72bd6953c6761fa6af0ddcec3 100644 --- a/src/main/scala/leon/Reporter.scala +++ b/src/main/scala/leon/Reporter.scala @@ -10,83 +10,157 @@ import purescala.PrettyPrinter import scala.annotation.implicitNotFound abstract class Reporter(settings: Settings) { - def infoFunction(msg: Any) : Unit - def warningFunction(msg: Any) : Unit - def errorFunction(msg: Any) : Unit - def debugFunction(msg: Any) : Unit - def fatalErrorFunction(msg: Any) : Nothing - // This part of the implementation is non-negociable. + abstract class Severity + case object INFO extends Severity + case object WARNING extends Severity + case object ERROR extends Severity + case object FATAL extends Severity + case class DEBUG(section: DebugSection) extends Severity + + case class Message(severity: Severity, position: Position, msg: Any); + private var _errorCount : Int = 0 private var _warningCount : Int = 0 final def errorCount : Int = _errorCount final def warningCount : Int = _warningCount - final def info(msg: Any) = infoFunction(msg) - final def warning(msg: Any) = { - _warningCount += 1 - warningFunction(msg) + def account(msg: Message): Message = { + msg.severity match { + case WARNING => _warningCount += 1 + case ERROR | FATAL => _errorCount += 1 + case _ => + } + + msg } - final def error(msg: Any) = { - _errorCount += 1 - errorFunction(msg) + + def emit(msg: Message): Unit + + def onFatal(): Nothing = { + throw LeonFatalError(None) } - final def fatalError(msg: Any) = { - _errorCount += 1 - fatalErrorFunction(msg) + + final def info(pos: Position, msg: Any): Unit = emit(account(Message(INFO, pos, msg))) + final def warning(pos: Position, msg: Any): Unit = emit(account(Message(WARNING, pos, msg))) + final def error(pos: Position, msg: Any): Unit = emit(account(Message(ERROR, pos, msg))) + + final def fatalError(pos: Position, msg: Any): Nothing = { + emit(account(Message(FATAL, pos, msg))) + onFatal() } + def terminateIfError() = { + if (errorCount > 0) { + _errorCount = 0 + _warningCount = 0 + fatalError("There were errors.") + } + } + + // Debugging private val debugMask = settings.debugSections.foldLeft(0){ _ | _.mask } def ifDebug(body: (Any => Unit) => Any)(implicit section: DebugSection) = { if ((debugMask & section.mask) == section.mask) { - body(debugFunction) + body( { (msg: Any) => emit(account(Message(DEBUG(section), NoPosition, msg))) } ) } } def whenDebug(section: DebugSection)(body: (Any => Unit) => Any) { if ((debugMask & section.mask) == section.mask) { - body(debugFunction) + body( { (msg: Any) => emit(account(Message(DEBUG(section), NoPosition, msg))) } ) } } - def debug(msg: => Any)(implicit section: DebugSection) = { + + def debug(pos: Position, msg: => Any)(implicit section: DebugSection): Unit = { ifDebug{ debug => debug(msg) }(section) } + + + // No-position alternatives + final def info(msg: Any): Unit = info(NoPosition, msg) + final def warning(msg: Any): Unit = warning(NoPosition, msg) + final def error(msg: Any): Unit = error(NoPosition, msg) + final def fatalError(msg: Any): Nothing = fatalError(NoPosition, msg) + final def debug(msg: => Any)(implicit section: DebugSection): Unit = debug(NoPosition, msg) } class DefaultReporter(settings: Settings) extends Reporter(settings) { - protected val errorPfx = "[ Error ] " - protected val warningPfx = "[Warning] " - protected val infoPfx = "[ Info ] " - protected val fatalPfx = "[ Fatal ] " - protected val debugPfx = "[ Debug ] " + protected def severityToPrefix(sev: Severity): String = sev match { + case ERROR => "["+Console.RED +" Error "+Console.RESET+"]" + case WARNING => "["+Console.YELLOW +"Warning"+Console.RESET+"]" + case INFO => "["+Console.BLUE +" Info "+Console.RESET+"]" + case FATAL => "["+Console.RED+Console.BOLD +" Fatal "+Console.RESET+"]" + case DEBUG(_) => "["+Console.MAGENTA +" Debug "+Console.RESET+"]" + } - def output(msg: String) : Unit = println(msg) + def emit(msg: Message) = { + val posString = if (msg.position != NoPosition) { msg.position+": " } else { "" } - protected def reline(pfx: String, msg: String) : String = { - val color = if(pfx == errorPfx || pfx == fatalPfx) { - Console.RED - } else if(pfx == warningPfx) { - Console.YELLOW - } else if(pfx == debugPfx) { - Console.MAGENTA + println(reline(severityToPrefix(msg.severity), posString + msg.msg.toString)) + printLineContent(msg.position) + } + + protected var linesOf = Map[java.io.File, List[String]]() + + def getLine(pos: Position): Option[String] = { + val lines = linesOf.get(pos.file) match { + case Some(lines) => + lines + case None => + val lines = if (pos == NoPosition) { + Nil + } else { + scala.io.Source.fromFile(pos.file).getLines.toList + } + + linesOf += pos.file -> lines + lines + } + + if (lines.size > pos.line-1 && pos.line >= 0) { + Some(lines(pos.line-1)) } else { - Console.BLUE + None } - "[" + color + pfx.substring(1, pfx.length-2) + Console.RESET + "] " + - msg.replaceAll("\n", "\n" + (" " * (pfx.size))) } - def errorFunction(msg: Any) = output(reline(errorPfx, msg.toString)) - def warningFunction(msg: Any) = output(reline(warningPfx, msg.toString)) - def infoFunction(msg: Any) = output(reline(infoPfx, msg.toString)) - def debugFunction(msg: Any) = output(reline(debugPfx, msg.toString)) - def fatalErrorFunction(msg: Any) = { - output(reline(fatalPfx, msg.toString)); - throw LeonFatalError(None) + val prefixSize = 10 + + val blankPrefix = " " * prefixSize + + def printLineContent(pos: Position): Unit = { + getLine(pos) match { + case Some(line) => + println(blankPrefix+line) + pos match { + case rp: RangePosition => + val bp = rp.focusBegin + val ep = rp.focusEnd + + val carret = if (bp.line == ep.line) { + val width = Math.max(ep.col - bp.col, 1) + ("^" * width) + } else { + val width = Math.max(line.length-bp.col, 1) + ("^" * width)+"..." + } + + println(blankPrefix+(" " * (bp.col - 1) + Console.RED+carret+Console.RESET)) + + case op: OffsetPosition => + println(blankPrefix+(" " * (op.col - 1) + Console.RED+"^"+Console.RESET)) + } + case None => + } + } + + protected def reline(pfx: String, msg: String) : String = { + pfx+" "+msg.replaceAll("\n", "\n" + (" " * prefixSize)) } } diff --git a/src/test/scala/leon/test/TestSilentReporter.scala b/src/test/scala/leon/test/TestSilentReporter.scala index 295c04be176d695648ec9bc5aab93c86dbf96758..ab12c99509c116c57061f37df01d07286882b35a 100644 --- a/src/test/scala/leon/test/TestSilentReporter.scala +++ b/src/test/scala/leon/test/TestSilentReporter.scala @@ -4,5 +4,5 @@ package leon package test class TestSilentReporter extends DefaultReporter(Settings()) { - override def output(msg: String) : Unit = { } + override def emit(msg: Message): Unit = { } }