Skip to content

Commit fa098bd

Browse files
committed
Merge pull request #99 from samuelgruetter/infostring2
Error reporting improvements
2 parents 932d396 + 762caa2 commit fa098bd

15 files changed

+142
-125
lines changed

src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import Decorators._
99
import language.higherKinds
1010
import collection.mutable.ListBuffer
1111
import config.Printers._
12-
import typer.ErrorReporting.InfoString
1312
import typer.Mode
1413

1514
object desugar {

src/dotty/tools/dotc/core/Contexts.scala

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,9 +511,6 @@ object Contexts {
511511

512512
protected[dotc] val indentTab = " "
513513

514-
/** Should warnings and errors containing non-sensical strings be suppressed? */
515-
private[dotc] var suppressNonSensicalErrors = true
516-
517514
def reset() = {
518515
for ((_, set) <- uniqueSets) set.clear()
519516
for (i <- 0 until classOfId.length) classOfId(i) = null

src/dotty/tools/dotc/core/Decorators.scala

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package core
33

44
import annotation.tailrec
55
import Symbols._
6-
import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer
6+
import Contexts._, Names._, Phases._, printing.Texts._, printing.Printer, printing.Showable
77
import util.Positions.Position, util.SourcePosition
88
import collection.mutable.ListBuffer
99
import dotty.tools.dotc.transform.TreeTransforms._
@@ -136,5 +136,36 @@ object Decorators {
136136

137137
implicit def sourcePos(pos: Position)(implicit ctx: Context): SourcePosition =
138138
ctx.source.atPos(pos)
139+
140+
/** The i"..." string interpolator adds two features to the s interpolator:
141+
* 1) On all Showables, `show` is called instead of `toString`
142+
* 2) Lists can be formatted using the desired separator between two `%` signs,
143+
* eg `i"myList = (${myList}%, %)"`
144+
*/
145+
implicit class InfoString(val sc: StringContext) extends AnyVal {
146+
147+
def i(args: Any*)(implicit ctx: Context): String = {
148+
149+
def treatArg(arg: Any, suffix: String): (Any, String) = arg match {
150+
case arg: Seq[_] if suffix.nonEmpty && suffix.head == '%' =>
151+
val (rawsep, rest) = suffix.tail.span(_ != '%')
152+
val sep = StringContext.treatEscapes(rawsep)
153+
if (rest.nonEmpty) (arg map treatSingleArg mkString sep, rest.tail)
154+
else (arg, suffix)
155+
case _ =>
156+
(treatSingleArg(arg), suffix)
157+
}
158+
159+
def treatSingleArg(arg: Any) : Any = arg match {
160+
case arg: Showable => arg.show
161+
case _ => arg
162+
}
163+
164+
val prefix :: suffixes = sc.parts.toList
165+
val (args1, suffixes1) = (args, suffixes).zipped.map(treatArg(_, _)).unzip
166+
new StringContext(prefix :: suffixes1.toList: _*).s(args1: _*)
167+
}
168+
}
169+
139170
}
140171

src/dotty/tools/dotc/reporting/Reporter.scala

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,26 +10,27 @@ import collection.mutable
1010
import config.Settings.Setting
1111
import config.Printers
1212
import java.lang.System.currentTimeMillis
13+
import typer.ErrorReporting.DiagnosticString
1314

1415
object Reporter {
1516

16-
class Diagnostic(msgFn: => String, val pos: SourcePosition, val severity: Severity, base: ContextBase) extends Exception {
17+
class Diagnostic(msgFn: => String, val pos: SourcePosition, val severity: Severity) extends Exception {
18+
import DiagnosticString._
1719
private var myMsg: String = null
18-
private var myIsSuppressed: Boolean = false
20+
private var myIsNonSensical: Boolean = false
1921
def msg: String = {
20-
if (myMsg == null)
21-
try myMsg = msgFn
22-
catch {
23-
case ex: SuppressedMessage =>
24-
myIsSuppressed = true
25-
val saved = base.suppressNonSensicalErrors
26-
base.suppressNonSensicalErrors = false
27-
try myMsg = msgFn
28-
finally base.suppressNonSensicalErrors = saved
22+
if (myMsg == null) {
23+
myMsg = msgFn
24+
if (myMsg.contains(nonSensicalStartTag)) {
25+
myIsNonSensical = true
26+
// myMsg might be composed of several d"..." invocations -> nested nonsensical tags possible
27+
myMsg = myMsg.replaceAllLiterally(nonSensicalStartTag, "").replaceAllLiterally(nonSensicalEndTag, "")
2928
}
29+
}
3030
myMsg
3131
}
32-
def isSuppressed = { msg; myIsSuppressed }
32+
def isNonSensical = { msg; myIsNonSensical }
33+
def isSuppressed(implicit ctx: Context): Boolean = !ctx.settings.YshowSuppressedErrors.value && isNonSensical
3334
override def toString = s"$severity at $pos: $msg"
3435
override def getMessage() = msg
3536

@@ -38,8 +39,8 @@ object Reporter {
3839
else severity
3940
}
4041

41-
def Diagnostic(msgFn: => String, pos: SourcePosition, severity: Severity)(implicit ctx: Context) =
42-
new Diagnostic(msgFn, pos, severity, ctx.base)
42+
def Diagnostic(msgFn: => String, pos: SourcePosition, severity: Severity) =
43+
new Diagnostic(msgFn, pos, severity)
4344

4445
class Severity(val level: Int) extends AnyVal {
4546
override def toString = this match {
@@ -71,8 +72,6 @@ object Reporter {
7172
case UncheckedWARNING => ctx.settings.unchecked
7273
case FeatureWARNING => ctx.settings.feature
7374
}
74-
75-
class SuppressedMessage extends Exception
7675
}
7776

7877
import Reporter._
@@ -221,7 +220,7 @@ abstract class Reporter {
221220
def report(d: Diagnostic)(implicit ctx: Context): Unit =
222221
if (!isHidden(d)) {
223222
doReport(d)
224-
count(d.promotedSeverity.level) += 1
223+
if (!d.isSuppressed) count(d.promotedSeverity.level) += 1
225224
}
226225

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

src/dotty/tools/dotc/transform/TreeChecker.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import core.Symbols._
1010
import core.Types._
1111
import core.Constants._
1212
import core.StdNames._
13+
import core.Decorators._
1314
import core.transform.Erasure.isUnboundedGeneric
1415
import typer.ErrorReporting._
1516
import ast.Trees._

src/dotty/tools/dotc/typer/Applications.scala

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -588,9 +588,9 @@ trait Applications extends Compatibility { self: Typer =>
588588
def extractorMemberType(tp: Type, name: Name) = {
589589
val ref = tp member name
590590
if (ref.isOverloaded)
591-
errorType(s"Overloaded reference to ${ref.show} is not allowed in extractor", tree.pos)
591+
errorType(i"Overloaded reference to $ref is not allowed in extractor", tree.pos)
592592
else if (ref.info.isInstanceOf[PolyType])
593-
errorType(s"Reference to polymorphic ${ref.show}: ${ref.info.show} is not allowed in extractor", tree.pos)
593+
errorType(i"Reference to polymorphic $ref: ${ref.info} is not allowed in extractor", tree.pos)
594594
else
595595
ref.info.widenExpr.dealias
596596
}
@@ -618,7 +618,7 @@ trait Applications extends Compatibility { self: Typer =>
618618
if (unapplyResult derivesFrom defn.SeqClass) seqSelector :: Nil
619619
else if (unapplyResult isRef defn.BooleanClass) Nil
620620
else {
621-
ctx.error(s"${unapplyResult.show} is not a valid result type of an unapply method of an extractor", tree.pos)
621+
ctx.error(i"$unapplyResult is not a valid result type of an unapply method of an extractor", tree.pos)
622622
Nil
623623
}
624624
}
@@ -636,7 +636,7 @@ trait Applications extends Compatibility { self: Typer =>
636636
unapplyFn.tpe.widen match {
637637
case mt: MethodType if mt.paramTypes.length == 1 && !mt.isDependent =>
638638
val unapplyArgType = mt.paramTypes.head
639-
unapp.println(s"unapp arg tpe = ${unapplyArgType.show}, pt = ${pt.show}")
639+
unapp.println(i"unapp arg tpe = $unapplyArgType, pt = $pt")
640640
def wpt = widenForMatchSelector(pt) // needed?
641641
val ownType =
642642
if (pt <:< unapplyArgType) {
@@ -647,7 +647,7 @@ trait Applications extends Compatibility { self: Typer =>
647647
maximizeType(unapplyArgType) match {
648648
case Some(tvar) =>
649649
def msg =
650-
i"""There is no best instantiation of pattern type $unapplyArgType
650+
d"""There is no best instantiation of pattern type $unapplyArgType
651651
|that makes it a subtype of selector type $pt.
652652
|Non-variant type variable ${tvar.origin} cannot be uniquely instantiated.""".stripMargin
653653
if (fromScala2x) {
@@ -671,7 +671,7 @@ trait Applications extends Compatibility { self: Typer =>
671671
unapp.println("Neither sub nor super")
672672
unapp.println(TypeComparer.explained(implicit ctx => unapplyArgType <:< wpt))
673673
errorType(
674-
i"Pattern type $unapplyArgType is neither a subtype nor a supertype of selector type $wpt",
674+
d"Pattern type $unapplyArgType is neither a subtype nor a supertype of selector type $wpt",
675675
tree.pos)
676676
}
677677

@@ -692,7 +692,7 @@ trait Applications extends Compatibility { self: Typer =>
692692
case _ => args
693693
}
694694
if (argTypes.length != bunchedArgs.length) {
695-
ctx.error(i"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos)
695+
ctx.error(d"wrong number of argument patterns for $qual; expected: ($argTypes%, %)", tree.pos)
696696
argTypes = argTypes.take(args.length) ++
697697
List.fill(argTypes.length - args.length)(WildcardType)
698698
}

src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import util.{Stats, SimpleMap}
1414
import util.common._
1515
import Decorators._
1616
import Uniques._
17-
import ErrorReporting.{errorType, InfoString}
17+
import ErrorReporting.{errorType, DiagnosticString}
1818
import config.Printers._
1919
import collection.mutable
2020

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

4747
/** Check that type `tp` is stable. */
4848
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
49-
if (!tp.isStable) ctx.error(i"$tp is not stable", pos)
49+
if (!tp.isStable) ctx.error(d"$tp is not stable", pos)
5050

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

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

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

@@ -96,7 +96,7 @@ trait Checking {
9696
case tp: RefinedType =>
9797
tp.derivedRefinedType(tp.parent, tp.refinedName, checkFeasible(tp.refinedInfo, pos, where))
9898
case tp @ TypeBounds(lo, hi) if !(lo <:< hi) =>
99-
ctx.error(i"no type exists between low bound $lo and high bound $hi$where", pos)
99+
ctx.error(d"no type exists between low bound $lo and high bound $hi$where", pos)
100100
tp.derivedTypeAlias(hi)
101101
case _ =>
102102
tp
@@ -113,17 +113,17 @@ trait Checking {
113113
typr.println(i"conflict? $decl $other")
114114
if (decl.signature matches other.signature) {
115115
def doubleDefError(decl: Symbol, other: Symbol): Unit = {
116-
def ofType = if (decl.isType) "" else i": ${other.info}"
116+
def ofType = if (decl.isType) "" else d": ${other.info}"
117117
def explanation =
118118
if (!decl.isSourceMethod) ""
119119
else "\n (both definitions have the same erased type signature)"
120-
ctx.error(i"$decl is already defined as $other$ofType$explanation", decl.pos)
120+
ctx.error(d"$decl is already defined as $other$ofType$explanation", decl.pos)
121121
}
122122
if (decl is Synthetic) doubleDefError(other, decl)
123123
else doubleDefError(decl, other)
124124
}
125125
if ((decl is HasDefaultParams) && (other is HasDefaultParams)) {
126-
ctx.error(i"two or more overloaded variants of $decl have default arguments")
126+
ctx.error(d"two or more overloaded variants of $decl have default arguments")
127127
decl resetFlag HasDefaultParams
128128
}
129129
}

0 commit comments

Comments
 (0)