aboutsummaryrefslogblamecommitdiff
path: root/src/dotty/tools/dotc/reporting/Reporter.scala
blob: 913290a00a23f6c64a34494296d79dcd18b5c981 (plain) (tree)
1
2
3
4
5
6
7
8
9




                      
                                              
                                  

                                         
                              

                                         
                 
 







                                                                                                                            
                                 







                                                          
                                                      
                                   



                                                                                   

   


                                                                                                    























                                                                                      







                                                                                        



                 
                                  


                                                                            
                                                      

                                                                          
                                               

                                                                                        
                                                             

                                                                                      
                                                           

                                                                                    
                                                         

                                                                             
                                                  
 

                                                                             
                                                
   

                                                                                                              
                                                         
 
                                 
                                                     
                                                                     
 
                                      
                           
 



                                                       
 
                                      
                           




                                            
 
                                                                               

                                                    

                                                                                      


                                                                                                           

           



                                                                                                                   






                                                                                            
 
                                                                                    
                         

                                                                             
                                             

                        
                                                                              

                        
         
                                                            
                      


                       

                           

                                                       
     
   

                                                                            

 



                                                                    
                         
 

                                                                    
 
                                                                        











                                                                         

                                                                      






                                                                      
                                                                                           



                                                                       
 
                                             
 




                                                          
 

                                                              
 

                                            
 
                                    
 

                                                   







                                                











                                                                       

                               
                   


                                                   

                                                                              

   
                                           








                                                                                               

   
                                               
 
package dotty.tools
package dotc
package reporting

import core.Contexts._
import util.{SourcePosition, NoSourcePosition}
import util.{SourceFile, NoSource}
import core.Decorators.PhaseListDecorator
import collection.mutable
import config.Settings.Setting
import java.lang.System.currentTimeMillis

object Reporter {

  class Diagnostic(msgFn: => String, val pos: SourcePosition, val severity: Severity, base: ContextBase) extends Exception {
    private var myMsg: String = null
    private var myIsSuppressed: Boolean = false
    def msg: String = {
      if (myMsg == null)
        try myMsg = msgFn
        catch {
          case ex: SuppressedMessage =>
            myIsSuppressed = true
            val saved = base.suppressNonSensicalErrors
            base.suppressNonSensicalErrors = false
            try myMsg = msgFn
            finally base.suppressNonSensicalErrors = saved
        }
      myMsg
    }
    def isSuppressed = { msg; myIsSuppressed }
    override def toString = s"$severity at $pos: $msg"
    override def getMessage() = msg

    def promotedSeverity(implicit ctx: Context): Severity =
      if (isConditionalWarning(severity) && enablingOption(severity).value) WARNING
      else severity
  }

  def Diagnostic(msgFn: => String, pos: SourcePosition, severity: Severity)(implicit ctx: Context) =
    new Diagnostic(msgFn, pos, severity, ctx.base)

  class Severity(val level: Int) extends AnyVal {
    override def toString = this match {
      case VerboseINFO => "VerboseINFO"
      case INFO => "INFO"
      case DeprecationWARNING => "DeprecationWARNING"
      case UncheckedWARNING => "UncheckedWARNING"
      case FeatureWARNING => "FeatureWARNING"
      case WARNING => "WARNING"
      case ERROR => "ERROR"
    }
  }

  final val VerboseINFO = new Severity(0)
  final val INFO = new Severity(1)
  final val DeprecationWARNING = new Severity(2)
  final val UncheckedWARNING = new Severity(3)
  final val FeatureWARNING = new Severity(4)
  final val WARNING = new Severity(5)
  final val ERROR = new Severity(6)

  def isConditionalWarning(s: Severity) =
    DeprecationWARNING.level <= s.level && s.level <= FeatureWARNING.level

  val conditionalWarnings = List(DeprecationWARNING, UncheckedWARNING, FeatureWARNING)

  private def enablingOption(warning: Severity)(implicit ctx: Context) = warning match {
    case DeprecationWARNING => ctx.settings.deprecation
    case UncheckedWARNING   => ctx.settings.unchecked
    case FeatureWARNING     => ctx.settings.feature
  }

  class SuppressedMessage extends Exception
}

import Reporter._

trait Reporting { this: Context =>

  /** For sending messages that are printed only if -verbose is set */
  def inform(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
    reporter.report(Diagnostic(msg, pos, VerboseINFO))

  def echo(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
    reporter.report(Diagnostic(msg, pos, INFO))

  def deprecationWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
    reporter.report(Diagnostic(msg, pos, DeprecationWARNING))

  def uncheckedWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
    reporter.report(Diagnostic(msg, pos, UncheckedWARNING))

  def featureWarning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
    reporter.report(Diagnostic(msg, pos, FeatureWARNING))

  def warning(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
    reporter.report(Diagnostic(msg, pos, WARNING))

  def error(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = {
    // println("*** ERROR: " + msg) // !!! DEBUG
    reporter.report(Diagnostic(msg, pos, ERROR))
  }

  def incompleteInputError(msg: String, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit =
    reporter.incomplete(Diagnostic(msg, pos, ERROR))(ctx)

  def log(msg: => String): Unit =
    if (this.settings.log.value.containsPhase(phase))
      echo(s"[log ${ctx.phasesStack.reverse.mkString(" -> ")}] $msg")

  def debuglog(msg: => String): Unit =
    if (ctx.debug) log(msg)

  def informTime(msg: => String, start: Long): Unit = {
    def elapsed = s" in ${currentTimeMillis - start}ms"
    informProgress(msg + elapsed)
  }

  def informProgress(msg: => String) =
    inform("[" + msg + "]")

  def trace[T](msg: => String)(value: T) = {
    log(msg + " " + value)
    value
  }

  def debugwarn(msg: => String, pos: SourcePosition = NoSourcePosition): Unit =
    if (this.settings.debug.value) warning(msg, pos)

  def debugTraceIndented[T](question: => String, show: Boolean = false)(op: => T): T =
    conditionalTraceIndented(this.settings.debugTrace.value, question, show)(op)

  def conditionalTraceIndented[T](cond: Boolean, question: => String, show: Boolean = false)(op: => T): T =
    if (cond) traceIndented(question, show)(op)
    else op

  def traceIndented[T](printer: config.Printers.Printer, question: => String, show: Boolean = false)(op: => T): T =
    if (printer ne config.Printers.noPrinter) traceIndented(question, show)(op)
    else op

  def traceIndented[T](question: => String, show: Boolean = false)(op: => T): T = {
    def resStr(res: Any): String = res match {
      case res: printing.Showable if show => res.show
      case _ => String.valueOf(res)
    }
    traceIndented[T](s"==> $question?", (res: Any) => s"<== $question = ${resStr(res)}")(op)
  }

  def traceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = {
    var finalized = false
    var logctx = this
    while (logctx.reporter.isInstanceOf[StoreReporter]) logctx = logctx.outer
    def finalize(result: Any, note: String) =
      if (!finalized) {
        base.indent -= 1
        logctx.log(s"${base.indentTab * base.indent}${trailing(result)}$note")
        finalized = true
      }
    try {
      logctx.log(s"${base.indentTab * base.indent}$leading")
      base.indent += 1
      val res = op
      finalize(res, "")
      res
    } catch {
      case ex: Throwable =>
        finalize("<missing>", s" (with exception $ex)")
        throw ex
    }
  }

  def errorsReported: Boolean = outersIterator exists (_.reporter.hasErrors)
}

/**
 * This interface provides methods to issue information, warning and
 * error messages.
 */
abstract class Reporter {

  /** Report a diagnostic */
  protected def doReport(d: Diagnostic)(implicit ctx: Context): Unit

 /** Whether very long lines can be truncated.  This exists so important
   *  debugging information (like printing the classpath) is not rendered
   *  invisible due to the max message length.
   */
  private var _truncationOK: Boolean = true
  def truncationOK = _truncationOK
  def withoutTruncating[T](body: => T): T = {
    val saved = _truncationOK
    _truncationOK = false
    try body
    finally _truncationOK = saved
  }

  type ErrorHandler = Diagnostic => Context => Unit
  private var incompleteHandler: ErrorHandler = d => c => report(d)(c)
  def withIncompleteHandler[T](handler: ErrorHandler)(op: => T): T = {
    val saved = incompleteHandler
    incompleteHandler = handler
    try op
    finally incompleteHandler = saved
  }

  protected def isHidden(d: Diagnostic)(implicit ctx: Context) = d.promotedSeverity match {
    case VerboseINFO => !ctx.settings.verbose.value
    case DeprecationWARNING | UncheckedWARNING | FeatureWARNING => true
    case _ => false
  }

  val count = new Array[Int](ERROR.level + 1)

  def report(d: Diagnostic)(implicit ctx: Context): Unit =
    if (!isHidden(d)) {
      doReport(d)
      count(d.promotedSeverity.level) += 1
    }

  def incomplete(d: Diagnostic)(implicit ctx: Context): Unit =
    incompleteHandler(d)(ctx)

  def hasErrors   = count(ERROR.level) > 0
  def hasWarnings = count(WARNING.level) > 0

  def errorCounts: Any = count.clone

  def wasSilent[T](counts: Any): Boolean = {
    val prevCount = counts.asInstanceOf[Array[Int]]
    var i = 0
    while (i < count.length) {
      if (prevCount(i) != count(i)) return false
      i += 1
    }
    true
  }

  /** Returns a string meaning "n elements". */
  private def countElementsAsString(n: Int, elements: String): String =
    n match {
      case 0 => "no "    + elements + "s"
      case 1 => "one "   + elements
      case 2 => "two "   + elements + "s"
      case 3 => "three " + elements + "s"
      case 4 => "four "  + elements + "s"
      case _ => n + " " + elements + "s"
    }

  protected def label(severity: Severity): String = severity match {
    case ERROR   => "error: "
    case WARNING => "warning: "
    case _    => ""
  }

  protected def countString(severity: Severity) = {
    assert(severity.level >= WARNING.level)
    countElementsAsString(count(severity.level), label(severity).dropRight(2))
  }

  def printSummary(implicit ctx: Context) {
    if (count(WARNING.level) > 0) ctx.echo(countString(WARNING) + " found")
    if (  count(ERROR.level) > 0) ctx.echo(countString(ERROR  ) + " found")
    for (cwarning <- conditionalWarnings) {
      val unreported = count(cwarning.level)
      if (unreported > 0) {
        val what = enablingOption(cwarning).name.tail
        ctx.warning(s"there were $unreported $what warning(s); re-run with -$what for details")
      }
    }
  }

  def flush()(implicit ctx: Context): Unit = {}
}