scala.tools.nsc.reporters.Reporter.scala Maven / Gradle / Ivy
The newest version!
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.tools.nsc
package reporters
import scala.annotation.{nowarn, unused}
import scala.collection.mutable
import scala.reflect.internal
import scala.reflect.internal.util.{Position, ScalaClassLoader}
/** This class exists for sbt compatibility. Global.reporter holds a FilteringReporter.
* The only Reporter that is *not* a FilteringReporter is the one created by sbt.
* The Global.reporter_= setter wraps that in a delegating [[MakeFilteringForwardingReporter]].
*/
abstract class Reporter extends internal.Reporter {
// used by sbt
@deprecated("Use echo, as internal.Reporter does not support unforced info", since="2.13.0")
final def info(pos: Position, msg: String, @unused force: Boolean): Unit = info0(pos, msg, INFO, force = true)
// overridden by sbt, IDE -- should not be in the reporting interface
// (IDE receives comments from ScaladocAnalyzer using this hook method)
// TODO: IDE should override a hook method in the parser instead
def comment(pos: Position, msg: String): Unit = ()
// used by sbt (via unit.cancel) to cancel a compile (see hasErrors)
// TODO: figure out how sbt uses this, come up with a separate interface for controlling the build
private[this] var _cancelled: Boolean = false
def cancelled: Boolean = _cancelled
def cancelled_=(b: Boolean): Unit = _cancelled = b
override def hasErrors: Boolean = super.hasErrors || cancelled
override def reset(): Unit = {
super.reset()
cancelled = false
}
}
object Reporter {
/** The usual way to create the configured reporter.
* Errors are reported through `settings.errorFn` and also by throwing an exception.
*/
def apply(settings: Settings): FilteringReporter = {
//val loader = ScalaClassLoader(getClass.getClassLoader) // apply does not make delegate
val loader = new ClassLoader(getClass.getClassLoader) with ScalaClassLoader
loader.create[FilteringReporter](settings.reporter.value, settings.errorFn)(settings)
}
}
/** The reporter used in a Global instance.
*
* It filters messages based on
* - settings.maxerrs / settings.maxwarns
* - positions (only one error at a position, no duplicate messages on a position)
*/
abstract class FilteringReporter extends Reporter {
def settings: Settings
@deprecatedOverriding("override the `doReport` overload (defined in reflect.internal.Reporter) instead", "2.13.12")
@deprecated("use the `doReport` overload instead", "2.13.12")
def doReport(pos: Position, msg: String, severity: Severity): Unit = doReport(pos, msg, severity, Nil)
// this should be the abstract method all the way up in reflect.internal.Reporter, but sbt compat
// the abstract override is commented-out to maintain binary compatibility for FilteringReporter subclasses
// override def doReport(pos: Position, msg: String, severity: Severity, actions: List[CodeAction]): Unit
@deprecatedOverriding("override `doReport` instead", "2.13.1") // overridden in scalameta for example
@nowarn("cat=deprecation")
protected def info0(pos: Position, msg: String, severity: Severity, force: Boolean): Unit =
// call the deprecated overload to support existing FilteringReporter subclasses (they override that overload)
doReport(pos, msg, severity)
private lazy val positions = mutable.Map[Position, Severity]() withDefaultValue INFO
private lazy val messages = mutable.Map[Position, List[String]]() withDefaultValue Nil
private def maxErrors: Int = settings.maxerrs.value
private def maxWarnings: Int = settings.maxwarns.value
override def filter(pos: Position, msg: String, severity: Severity): Int = {
import internal.Reporter.{ERROR => Error, WARNING => Warning, _}
def maxOk = severity match {
case Error => maxErrors < 0 || errorCount < maxErrors
case Warning => maxWarnings < 0 || warningCount < maxWarnings
case _ => true
}
// Invoked when an error or warning is filtered by position.
@inline def suppress = {
if (settings.prompt.value) doReport(pos, msg, severity, Nil)
else if (settings.isDebug) doReport(pos, s"[ suppressed ] $msg", severity, Nil)
Suppress
}
if (!duplicateOk(pos, severity, msg)) suppress else if (!maxOk) Count else Display
}
/** Returns `true` if the message should be reported. Messages are skipped if:
* - there was already some error at the position. After an error, no further
* messages at that position are issued.
* - the same warning/info message was already issued at the same position.
* Note: two positions are considered identical for logging if they have the same point.
*/
private def duplicateOk(pos: Position, severity: Severity, msg: String): Boolean = {
// was a prefix of the msg already reported at this position for purposes of suppressing repetition?
def matchAt(pos: Position, msg: String): Boolean = messages(pos).exists(msg.startsWith)
// always report at null / NoPosition
pos == null || !pos.isDefined || {
val fpos = pos.focus
val show = positions(fpos) match {
case internal.Reporter.ERROR => false // already error at position
case s if s.id > severity.id => false // already message higher than present severity
case `severity` => !matchAt(fpos, msg) // already issued this (in)exact message
case _ => true // good to go
}
if (show) {
positions(fpos) = severity
messages(fpos) ::= stripQuickfixable(msg)
}
show
}
}
private def stripQuickfixable(msg: String): String = {
val i = msg.indexOf(" [quickfixable]")
if (i > 0) msg.substring(0, i) else msg
}
override def reset(): Unit = {
super.reset()
positions.clear()
messages.clear()
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy