Skip to content

Error reporting improvements #99

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

Merged
merged 8 commits into from
Apr 4, 2014
1 change: 0 additions & 1 deletion src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Decorators._
import language.higherKinds
import collection.mutable.ListBuffer
import config.Printers._
import typer.ErrorReporting.InfoString
import typer.Mode

object desugar {
Expand Down
3 changes: 0 additions & 3 deletions src/dotty/tools/dotc/core/Contexts.scala
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,6 @@ object Contexts {

protected[dotc] val indentTab = " "

/** Should warnings and errors containing non-sensical strings be suppressed? */
private[dotc] var suppressNonSensicalErrors = true

def reset() = {
for ((_, set) <- uniqueSets) set.clear()
for (i <- 0 until classOfId.length) classOfId(i) = null
Expand Down
33 changes: 32 additions & 1 deletion src/dotty/tools/dotc/core/Decorators.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package core

import annotation.tailrec
import Symbols._
import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer
import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printing.Showable
import util.Positions.Position, util.SourcePosition
import collection.mutable.ListBuffer
import dotty.tools.dotc.transform.TreeTransforms._
Expand Down Expand Up @@ -136,5 +136,36 @@ object Decorators {

implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition =
ctx.source.atPos(pos)

/** The i"..." string interpolator adds two features to the s interpolator:
* 1) On all Showables, `show` is called instead of `toString`
* 2) Lists can be formatted using the desired separator between two `%` signs,
* eg `i"myList = (${myList}%, %)"`
*/
implicit class InfoString(val sc: StringContext) extends AnyVal {

def i(args: Any*)(implicit ctx: Context): String = {

def treatArg(arg: Any, suffix: String): (Any, String) = arg match {
case arg: Seq[_] if suffix.nonEmpty && suffix.head == '%' =>
val (rawsep, rest) = suffix.tail.span(_ != '%')
val sep = StringContext.treatEscapes(rawsep)
if (rest.nonEmpty) (arg map treatSingleArg mkString sep, rest.tail)
else (arg, suffix)
case _ =>
(treatSingleArg(arg), suffix)
}

def treatSingleArg(arg: Any) : Any = arg match {
case arg: Showable => arg.show
case _ => arg
}

val prefix :: suffixes = sc.parts.toList
val (args1, suffixes1) = (args, suffixes).zipped.map(treatArg(_, _)).unzip
new StringContext(prefix :: suffixes1.toList: _*).s(args1: _*)
}
}

}

33 changes: 16 additions & 17 deletions src/dotty/tools/dotc/reporting/Reporter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,27 @@ import collection.mutable
import config.Settings.Setting
import config.Printers
import java.lang.System.currentTimeMillis
import typer.ErrorReporting.DiagnosticString

object Reporter {

class Diagnostic(msgFn: => String, val pos: SourcePosition, val severity: Severity, base: ContextBase) extends Exception {
class Diagnostic(msgFn: => String, val pos: SourcePosition, val severity: Severity) extends Exception {
import DiagnosticString._
private var myMsg: String = null
private var myIsSuppressed: Boolean = false
private var myIsNonSensical: 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
if (myMsg == null) {
myMsg = msgFn
if (myMsg.contains(nonSensicalStartTag)) {
myIsNonSensical = true
// myMsg might be composed of several d"..." invocations -> nested nonsensical tags possible
myMsg = myMsg.replaceAllLiterally(nonSensicalStartTag, "").replaceAllLiterally(nonSensicalEndTag, "")
}
}
myMsg
}
def isSuppressed = { msg; myIsSuppressed }
def isNonSensical = { msg; myIsNonSensical }
def isSuppressed(implicit ctx: Context): Boolean = !ctx.settings.YshowSuppressedErrors.value && isNonSensical
override def toString = s"$severity at $pos: $msg"
override def getMessage() = msg

Expand All @@ -38,8 +39,8 @@ object Reporter {
else severity
}

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

class Severity(val level: Int) extends AnyVal {
override def toString = this match {
Expand Down Expand Up @@ -71,8 +72,6 @@ object Reporter {
case UncheckedWARNING => ctx.settings.unchecked
case FeatureWARNING => ctx.settings.feature
}

class SuppressedMessage extends Exception
}

import Reporter._
Expand Down Expand Up @@ -221,7 +220,7 @@ abstract class Reporter {
def report(d: Diagnostic)(implicit ctx: Context): Unit =
if (!isHidden(d)) {
doReport(d)
count(d.promotedSeverity.level) += 1
if (!d.isSuppressed) count(d.promotedSeverity.level) += 1
}

def incomplete(d: Diagnostic)(implicit ctx: Context): Unit =
Expand Down
1 change: 1 addition & 0 deletions src/dotty/tools/dotc/transform/TreeChecker.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import core.Symbols._
import core.Types._
import core.Constants._
import core.StdNames._
import core.Decorators._
import core.transform.Erasure.isUnboundedGeneric
import typer.ErrorReporting._
import ast.Trees._
Expand Down
14 changes: 7 additions & 7 deletions src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -588,9 +588,9 @@ trait Applications extends Compatibility { self: Typer =>
def extractorMemberType(tp: Type, name: Name) = {
val ref = tp member name
if (ref.isOverloaded)
errorType(s"Overloaded reference to ${ref.show} is not allowed in extractor", tree.pos)
errorType(i"Overloaded reference to $ref is not allowed in extractor", tree.pos)
else if (ref.info.isInstanceOf[PolyType])
errorType(s"Reference to polymorphic ${ref.show}: ${ref.info.show} is not allowed in extractor", tree.pos)
errorType(i"Reference to polymorphic $ref: ${ref.info} is not allowed in extractor", tree.pos)
else
ref.info.widenExpr.dealias
}
Expand Down Expand Up @@ -618,7 +618,7 @@ trait Applications extends Compatibility { self: Typer =>
if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil
else if (unapplyResult isRef defn.BooleanClass) Nil
else {
ctx.error(s"${unapplyResult.show} is not a valid result type of an unapply method of an extractor", tree.pos)
ctx.error(i"$unapplyResult is not a valid result type of an unapply method of an extractor", tree.pos)
Nil
}
}
Expand All @@ -636,7 +636,7 @@ trait Applications extends Compatibility { self: Typer =>
unapplyFn.tpe.widen match {
case mt: MethodType if mt.paramTypes.length == 1 && !mt.isDependent =>
val unapplyArgType = mt.paramTypes.head
unapp.println(s"unapp arg tpe = ${unapplyArgType.show}, pt = ${pt.show}")
unapp.println(i"unapp arg tpe = $unapplyArgType, pt = $pt")
def wpt = widenForMatchSelector(pt) // needed?
val ownType =
if (pt <:< unapplyArgType) {
Expand All @@ -647,7 +647,7 @@ trait Applications extends Compatibility { self: Typer =>
maximizeType(unapplyArgType) match {
case Some(tvar) =>
def msg =
i"""There is no best instantiation of pattern type $unapplyArgType
d"""There is no best instantiation of pattern type $unapplyArgType
|that makes it a subtype of selector type $pt.
|Non-variant type variable ${tvar.origin} cannot be uniquely instantiated.""".stripMargin
if (fromScala2x) {
Expand All @@ -671,7 +671,7 @@ trait Applications extends Compatibility { self: Typer =>
unapp.println("Neither sub nor super")
unapp.println(TypeComparer.explained(implicit ctx => unapplyArgType <:< wpt))
errorType(
i"Pattern type $unapplyArgType is neither a subtype nor a supertype of selector type $wpt",
d"Pattern type $unapplyArgType is neither a subtype nor a supertype of selector type $wpt",
tree.pos)
}

Expand All @@ -692,7 +692,7 @@ trait Applications extends Compatibility { self: Typer =>
case _ => args
}
if (argTypes.length != bunchedArgs.length) {
ctx.error(i"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos)
ctx.error(d"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos)
argTypes = argTypes.take(args.length) ++
List.fill(argTypes.length - args.length)(WildcardType)
}
Expand Down
24 changes: 12 additions & 12 deletions src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import util.{Stats, SimpleMap}
import util.common._
import Decorators._
import Uniques._
import ErrorReporting.{errorType, InfoString}
import ErrorReporting.{errorType, DiagnosticString}
import config.Printers._
import collection.mutable

Expand All @@ -27,7 +27,7 @@ trait Checking {
def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = {
if (!proto.isInstanceOf[SelectionProto]) {
val sym = tree.tpe.termSymbol
if ((sym is Package) || (sym is JavaModule)) ctx.error(i"$sym is not a value", tree.pos)
if ((sym is Package) || (sym is JavaModule)) ctx.error(d"$sym is not a value", tree.pos)
}
tree
}
Expand All @@ -38,21 +38,21 @@ trait Checking {
def substituted(tp: Type) = tp.substParams(poly, argTypes)
for ((arg, bounds) <- args zip poly.paramBounds) {
def notConforms(which: String, bound: Type) =
ctx.error(i"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos)
ctx.error(d"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos)
if (!(arg.tpe <:< substituted(bounds.hi))) notConforms("upper", bounds.hi)
if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo)
}
}

/** Check that type `tp` is stable. */
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.error(d"$tp is not stable", pos)

/** Check that type `tp` is a legal prefix for '#'.
* @return The type itself
*/
def checkLegalPrefix(tp: Type, pos: Position)(implicit ctx: Context): Unit =
if (!tp.isLegalPrefix) ctx.error(i"$tp is not a valid prefix for '#'", pos)
if (!tp.isLegalPrefix) ctx.error(d"$tp is not a valid prefix for '#'", pos)

/** Check that `tp` is a class type with a stable prefix. Also, if `isFirst` is
* false check that `tp` is a trait.
Expand All @@ -62,10 +62,10 @@ trait Checking {
tp.underlyingClassRef match {
case tref: TypeRef =>
checkStable(tref.prefix, pos)
if (traitReq && !(tref.symbol is Trait)) ctx.error(i"$tref is not a trait", pos)
if (traitReq && !(tref.symbol is Trait)) ctx.error(d"$tref is not a trait", pos)
tp
case _ =>
ctx.error(i"$tp is not a class type", pos)
ctx.error(d"$tp is not a class type", pos)
defn.ObjectClass.typeRef
}

Expand All @@ -74,7 +74,7 @@ trait Checking {
case tpt: untpd.DerivedTypeTree =>
case TypeTree(untpd.EmptyTree) =>
val resStr = if (defTree.isInstanceOf[untpd.DefDef]) "result " else ""
ctx.error(i"${resStr}type of implicit definition needs to be given explicitly", defTree.pos)
ctx.error(d"${resStr}type of implicit definition needs to be given explicitly", defTree.pos)
case _ =>
}

Expand All @@ -96,7 +96,7 @@ trait Checking {
case tp: RefinedType =>
tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where))
case tp @ TypeBounds(lo, hi) if !(lo <:< hi) =>
ctx.error(i"no type exists between low bound $lo and high bound $hi$where", pos)
ctx.error(d"no type exists between low bound $lo and high bound $hi$where", pos)
tp.derivedTypeAlias(hi)
case _ =>
tp
Expand All @@ -113,17 +113,17 @@ trait Checking {
typr.println(i"conflict? $decl $other")
if (decl.signature matches other.signature) {
def doubleDefError(decl: Symbol, other: Symbol): Unit = {
def ofType = if (decl.isType) "" else i": ${other.info}"
def ofType = if (decl.isType) "" else d": ${other.info}"
def explanation =
if (!decl.isSourceMethod) ""
else "\n (both definitions have the same erased type signature)"
ctx.error(i"$decl is already defined as $other$ofType$explanation", decl.pos)
ctx.error(d"$decl is already defined as $other$ofType$explanation", decl.pos)
}
if (decl is Synthetic) doubleDefError(other, decl)
else doubleDefError(decl, other)
}
if ((decl is HasDefaultParams) && (other is HasDefaultParams)) {
ctx.error(i"two or more overloaded variants of $decl have default arguments")
ctx.error(d"two or more overloaded variants of $decl have default arguments")
decl resetFlag HasDefaultParams
}
}
Expand Down
Loading