Skip to content

Error-specific Diagnostics subclasses #92

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import collection.mutable.ListBuffer
import config.Printers._
import typer.ErrorReporting.InfoString
import typer.Mode
import reporting.parser.errors._

object desugar {

Expand Down Expand Up @@ -349,9 +350,9 @@ object desugar {
val implicitWrappers =
if (mods is Implicit) {
if (ctx.owner is Package)
ctx.error("implicit classes may not be toplevel", cdef.pos)
ctx.report(ImplicitToplevelClass(cdef.pos))
if (mods is Case)
ctx.error("implicit classes may not case classes", cdef.pos)
ctx.report(ImplicitCaseClass(cdef.pos))

// implicit wrapper is typechecked in same scope as constructor, so
// we can reuse the constructor parameters; no derived params are needed.
Expand Down
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ object Contexts {
/** The current reporter */
def reporter: Reporter = typerState.reporter

def report(d: Diagnostic): Unit = reporter.report(d)

/** Is this a context for the members of a class definition? */
def isClassDefContext: Boolean =
owner.isClass && (owner ne outer.owner)
Expand Down
5 changes: 2 additions & 3 deletions src/dotty/tools/dotc/reporting/ConsoleReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package reporting
import scala.collection.mutable
import util.SourcePosition
import core.Contexts._
import Reporter._
import java.io.{ BufferedReader, IOException, PrintWriter }
import scala.reflect.internal.util._

Expand Down Expand Up @@ -41,9 +40,9 @@ class ConsoleReporter(
}

override def doReport(d: Diagnostic)(implicit ctx: Context): Unit =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In some teams people make a decision to treat warning as errors. It would be good to have a way to configure which types of diagnostics get which level of Severity.
IMHO there should be way to increase severity, but decreasing should be forbidden.

if (d.severity != ERROR || count(d.severity.level) <= ErrorLimit && !d.isSuppressed) {
if (d.severity != Severity.ERROR || count(d.severity.level) <= ErrorLimit && !d.isSuppressed) {
printMessageAndPos(label(d.severity) + d.msg, d.pos)
if (d.severity == ERROR && ctx.settings.prompt.value) displayPrompt()
if (d.severity == Severity.ERROR && ctx.settings.prompt.value) displayPrompt()
}

def displayPrompt(): Unit = {
Expand Down
67 changes: 1 addition & 66 deletions src/dotty/tools/dotc/reporting/Reporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,72 +10,7 @@ import collection.mutable
import config.Settings.Setting
import config.Printers
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._
import Severity._

trait Reporting { this: Context =>

Expand Down
25 changes: 25 additions & 0 deletions src/dotty/tools/dotc/reporting/Severity.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package dotty.tools.dotc.reporting

class Severity(val level: Int) extends AnyVal {
import Severity._

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"
}
}

object Severity {
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)
}
1 change: 0 additions & 1 deletion src/dotty/tools/dotc/reporting/StoreReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package reporting

import core.Contexts.Context
import collection.mutable
import Reporter.Diagnostic
import config.Printers._

/**
Expand Down
3 changes: 1 addition & 2 deletions src/dotty/tools/dotc/reporting/ThrowingReporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@ package reporting

import core.Contexts.Context
import collection.mutable
import Reporter._

/**
* This class implements a Reporter that stores all messages
*/
class ThrowingReporter(reportInfo: Reporter) extends Reporter {
protected def doReport(d: Diagnostic)(implicit ctx: Context): Unit =
if (d.severity == ERROR) throw d else reportInfo.report(d)
if (d.severity == Severity.ERROR) throw d else reportInfo.report(d)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ package reporting

import scala.collection.mutable
import util.{SourcePosition, SourceFile}
import Reporter.{Severity, Diagnostic}
import core.Contexts.Context

/**
Expand Down
77 changes: 77 additions & 0 deletions src/dotty/tools/dotc/reporting/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package dotty.tools.dotc

import core.Contexts._
import util.SourcePosition
import reporting.Severity._

package object reporting {

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)

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

val conditionalWarnings = List(DeprecationWARNING, UncheckedWARNING, FeatureWARNING)

private[reporting] 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

trait CompilerError { self: Diagnostic => }
trait CompilerWarning { self: Diagnostic => }
trait CompilerInfo { self: Diagnostic =>
def isVerbose: Boolean
}

trait ParserDiagnostic { self: Diagnostic => }
trait TyperDiagnostic { self: Diagnostic => }

class ParserError(msgFn: => String, pos: SourcePosition, base: ContextBase)
extends Diagnostic(msgFn, pos, ERROR, base) with CompilerError with ParserDiagnostic

class ParserWarning(msgFn: => String, pos: SourcePosition, base: ContextBase)
extends Diagnostic(msgFn, pos, WARNING, base) with CompilerWarning with ParserDiagnostic

class ParserInfo(msgFn: => String, val isVerbose: Boolean, pos: SourcePosition, base: ContextBase)
extends Diagnostic(msgFn, pos, if (isVerbose) VerboseINFO else INFO, base) with CompilerInfo with ParserDiagnostic

class TyperError(msgFn: => String, pos: SourcePosition, base: ContextBase)
extends Diagnostic(msgFn, pos, ERROR, base) with CompilerError with TyperDiagnostic

class TyperWarning(msgFn: => String, pos: SourcePosition, base: ContextBase)
extends Diagnostic(msgFn, pos, WARNING, base) with CompilerWarning with TyperDiagnostic

class TyperInfo(msgFn: => String, val isVerbose: Boolean, pos: SourcePosition, base: ContextBase)
extends Diagnostic(msgFn, pos, if (isVerbose) VerboseINFO else INFO, base) with CompilerInfo with TyperDiagnostic

}
24 changes: 24 additions & 0 deletions src/dotty/tools/dotc/reporting/parser/errors.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dotty.tools.dotc
package reporting
package parser

import core.Contexts._
import util.SourcePosition
import reporting.Severity._

/** all parser error classes */
object errors {

class ImplicitToplevelClass private[errors] (pos: SourcePosition, base: ContextBase) extends
ParserError("implicit classes may not be toplevel", pos, base)

def ImplicitToplevelClass(pos: SourcePosition)(implicit ctx: Context) =
new ImplicitToplevelClass(pos, ctx.base)

class ImplicitCaseClass private[errors] (pos: SourcePosition, base: ContextBase) extends
ParserError("implicit classes may not be case classes", pos, base)

def ImplicitCaseClass(pos: SourcePosition)(implicit ctx: Context) =
new ImplicitCaseClass(pos, ctx.base)

}
20 changes: 20 additions & 0 deletions src/dotty/tools/dotc/reporting/typer/errors.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package dotty.tools.dotc
package reporting
package typer

import core.Contexts._
import core.Types._
import util.SourcePosition
import reporting.Severity._
import dotty.tools.dotc.typer.ErrorReporting.InfoString

/** all typer error classes */
object errors {

class NotStableError private[errors] (msgFn: => String, pos: SourcePosition, base: ContextBase)
extends TyperError(msgFn, pos, base)

def NotStableError(tp: Type, pos: SourcePosition)(implicit ctx: Context) =
new NotStableError(i"$tp is not stable", pos, ctx.base)

}
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import Uniques._
import ErrorReporting.{errorType, InfoString}
import config.Printers._
import collection.mutable
import reporting.typer.errors._

trait NoChecking {
import tpd._
Expand Down Expand Up @@ -59,7 +60,7 @@ trait Checking extends NoChecking {

/** Check that type `tp` is stable. */
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isStable) ctx.error(i"$tp is not stable", pos)
if (!tp.isStable) ctx.report(NotStableError(tp, pos))

/** Check that type `tp` is a legal prefix for '#'.
* @return The type itself
Expand Down
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import Applications._, Implicits._
import util.Positions._
import printing.Showable
import printing.Disambiguation.disambiguated
import reporting.Reporter.SuppressedMessage
import reporting.SuppressedMessage

object ErrorReporting {

Expand Down
4 changes: 2 additions & 2 deletions test/test/CompilerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import scala.reflect.io._
import org.junit.Test
import scala.collection.mutable.ListBuffer
import dotty.tools.dotc.{Main, Bench, Driver}
import dotty.tools.dotc.reporting.Reporter
import dotty.tools.dotc.reporting.Severity

class CompilerTest extends DottyTest {

Expand All @@ -13,7 +13,7 @@ class CompilerTest extends DottyTest {
def compileArgs(args: Array[String], xerrors: Int = 0): Unit = {
val allArgs = args ++ defaultOptions
val processor = if (allArgs.exists(_.startsWith("#"))) Bench else Main
val nerrors = processor.process(allArgs).count(Reporter.ERROR.level)
val nerrors = processor.process(allArgs).count(Severity.ERROR.level)
assert(nerrors == xerrors, s"Wrong # of errors. Expected: $xerrors, found: $nerrors")
}

Expand Down