diff options
author | Felix Mulder <felix.mulder@gmail.com> | 2016-11-02 11:08:28 +0100 |
---|---|---|
committer | Guillaume Martres <smarter@ubuntu.com> | 2016-11-22 01:35:07 +0100 |
commit | 8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch) | |
tree | a8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/reporting/Reporter.scala | |
parent | 6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff) | |
download | dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2 dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip |
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/reporting/Reporter.scala')
-rw-r--r-- | compiler/src/dotty/tools/dotc/reporting/Reporter.scala | 296 |
1 files changed, 296 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/reporting/Reporter.scala b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala new file mode 100644 index 000000000..8477cfe28 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/reporting/Reporter.scala @@ -0,0 +1,296 @@ +package dotty.tools +package dotc +package reporting + +import core.Contexts._ +import util.{SourcePosition, NoSourcePosition} +import core.Decorators.PhaseListDecorator +import collection.mutable +import config.Printers +import java.lang.System.currentTimeMillis +import core.Mode +import dotty.tools.dotc.core.Symbols.Symbol +import diagnostic.messages._ +import diagnostic._ +import Message._ + +object Reporter { + /** Convert a SimpleReporter into a real Reporter */ + def fromSimpleReporter(simple: interfaces.SimpleReporter): Reporter = + new Reporter with UniqueMessagePositions with HideNonSensicalMessages { + override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = m match { + case m: ConditionalWarning if !m.enablingOption.value => + case _ => + simple.report(m) + } + } +} + +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 = + if (this.settings.verbose.value) this.echo(msg, pos) + + def echo(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new Info(msg, pos)) + + def deprecationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new DeprecationWarning(msg, pos)) + + def migrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new MigrationWarning(msg, pos)) + + def uncheckedWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new UncheckedWarning(msg, pos)) + + def featureWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new FeatureWarning(msg, pos)) + + def featureWarning(feature: String, featureDescription: String, isScala2Feature: Boolean, + featureUseSite: Symbol, required: Boolean, pos: SourcePosition): Unit = { + val req = if (required) "needs to" else "should" + val prefix = if (isScala2Feature) "scala." else "dotty." + val fqname = prefix + "language." + feature + + val explain = { + if (reporter.isReportedFeatureUseSite(featureUseSite)) "" + else { + reporter.reportNewFeatureUseSite(featureUseSite) + s""" + |This can be achieved by adding the import clause 'import $fqname' + |or by setting the compiler option -language:$feature. + |See the Scala docs for value $fqname for a discussion + |why the feature $req be explicitly enabled.""" + } + } + + val msg = s"$featureDescription $req be enabled\nby making the implicit value $fqname visible.$explain" + if (required) error(msg, pos) + else reporter.report(new FeatureWarning(msg, pos)) + } + + def warning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new Warning(msg, pos)) + + def strictWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + if (this.settings.strict.value) error(msg, pos) + else reporter.report { + new ExtendMessage(() => msg)(_ + "\n(This would be an error under strict mode)").warning(pos) + } + + def error(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report(new Error(msg, pos)) + + def errorOrMigrationWarning(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + if (ctx.scala2Mode) migrationWarning(msg, pos) else error(msg, pos) + + def restrictionError(msg: => Message, pos: SourcePosition = NoSourcePosition): Unit = + reporter.report { + new ExtendMessage(() => msg)(m => s"Implementation restriction: $m").error(pos) + } + + def incompleteInputError(msg: => Message, pos: SourcePosition = NoSourcePosition)(implicit ctx: Context): Unit = + reporter.incomplete(new Error(msg, pos))(ctx) + + /** Log msg if settings.log contains the current phase. + * See [[config.CompilerCommand#explainAdvanced]] for the exact meaning of + * "contains" here. + */ + def log(msg: => String, pos: SourcePosition = NoSourcePosition): Unit = + if (this.settings.log.value.containsPhase(phase)) + echo(s"[log ${ctx.phasesStack.reverse.mkString(" -> ")}] $msg", pos) + + 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) + + @inline + def debugTraceIndented[TD](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => TD): TD = + conditionalTraceIndented(this.settings.debugTrace.value, question, printer, show)(op) + + @inline + def conditionalTraceIndented[TC](cond: Boolean, question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => TC): TC = + if (cond) traceIndented[TC](question, printer, show)(op) + else op + + @inline + def traceIndented[T](question: => String, printer: Printers.Printer = Printers.default, show: Boolean = false)(op: => T): T = + if (printer eq config.Printers.noPrinter) op + else doTraceIndented[T](question, printer, show)(op) + + private def doTraceIndented[T](question: => String, printer: Printers.Printer = Printers.default, 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) + } + // Avoid evaluating question multiple time, since each evaluation + // may cause some extra logging output. + lazy val q: String = question + doTraceIndented[T](s"==> $q?", (res: Any) => s"<== $q = ${resStr(res)}")(op) + } + + def doTraceIndented[T](leading: => String, trailing: Any => String)(op: => T): T = + if (ctx.mode.is(Mode.Printing)) op + else { + 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 + } + } + + /** Implements a fold that applies the function `f` to the result of `op` if + * there are no new errors in the reporter + * + * @param op operation checked for errors + * @param f function applied to result of op + * @return either the result of `op` if it had errors or the result of `f` + * applied to it + */ + def withNoError[A, B >: A](op: => A)(f: A => B): B = { + val before = reporter.errorCount + val op0 = op + + if (reporter.errorCount > before) op0 + else f(op0) + } +} + +/** + * This interface provides methods to issue information, warning and + * error messages. + */ +abstract class Reporter extends interfaces.ReporterResult { + + /** Report a diagnostic */ + def doReport(m: MessageContainer)(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 = MessageContainer => 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 + } + + var errorCount = 0 + var warningCount = 0 + def hasErrors = errorCount > 0 + def hasWarnings = warningCount > 0 + private var errors: List[Error] = Nil + def allErrors = errors + + /** Have errors been reported by this reporter, or in the + * case where this is a StoreReporter, by an outer reporter? + */ + def errorsReported = hasErrors + + private[this] var reportedFeaturesUseSites = Set[Symbol]() + def isReportedFeatureUseSite(featureTrait: Symbol): Boolean = reportedFeaturesUseSites.contains(featureTrait) + def reportNewFeatureUseSite(featureTrait: Symbol): Unit = reportedFeaturesUseSites += featureTrait + + val unreportedWarnings = new mutable.HashMap[String, Int] { + override def default(key: String) = 0 + } + + def report(m: MessageContainer)(implicit ctx: Context): Unit = + if (!isHidden(m)) { + doReport(m)(ctx.addMode(Mode.Printing)) + m match { + case m: ConditionalWarning if !m.enablingOption.value => unreportedWarnings(m.enablingOption.name) += 1 + case m: Warning => warningCount += 1 + case m: Error => + errors = m :: errors + errorCount += 1 + case m: Info => // nothing to do here + // match error if d is something else + } + } + + def incomplete(m: MessageContainer)(implicit ctx: Context): Unit = + incompleteHandler(m)(ctx) + + /** Summary of warnings and errors */ + def summary: String = { + val b = new mutable.ListBuffer[String] + if (warningCount > 0) + b += countString(warningCount, "warning") + " found" + if (errorCount > 0) + b += countString(errorCount, "error") + " found" + for ((settingName, count) <- unreportedWarnings) + b += s"there were $count ${settingName.tail} warning(s); re-run with $settingName for details" + b.mkString("\n") + } + + /** Print the summary of warnings and errors */ + def printSummary(implicit ctx: Context): Unit = { + val s = summary + if (s != "") ctx.echo(s) + } + + /** Returns a string meaning "n elements". */ + protected def countString(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" + } + + /** Should this diagnostic not be reported at all? */ + def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = ctx.mode.is(Mode.Printing) + + /** Does this reporter contain not yet reported errors or warnings? */ + def hasPending: Boolean = false + + /** Issue all error messages in this reporter to next outer one, or make sure they are written. */ + def flush()(implicit ctx: Context): Unit = {} +} |