From f23ff3abba8663a0e7f64f79b556efd36cc86a83 Mon Sep 17 00:00:00 2001 From: Felix Mulder Date: Thu, 29 Sep 2016 17:10:45 +0200 Subject: Improve positions for MemberDefs using `namePos` --- src/dotty/tools/dotc/ast/Desugar.scala | 7 +++++-- src/dotty/tools/dotc/parsing/Parsers.scala | 20 +++++++++++++++++--- src/dotty/tools/dotc/printing/Formatting.scala | 10 ++++------ src/dotty/tools/dotc/reporting/ConsoleReporter.scala | 1 + .../dotc/reporting/UniqueMessagePositions.scala | 16 +++++++++------- .../tools/dotc/reporting/diagnostic/messages.scala | 19 ++++++++++++++++--- src/dotty/tools/dotc/transform/TailRec.scala | 13 +++++++++---- src/dotty/tools/dotc/typer/Checking.scala | 8 ++++---- src/dotty/tools/dotc/typer/Typer.scala | 2 +- tests/neg/applydynamic_sip.scala | 6 +++--- tests/neg/assignments.scala | 2 +- tests/neg/dynamicApplyDynamicTest3.scala | 2 +- tests/neg/i1424.scala | 2 +- tests/neg/tailcall/t6574.scala | 2 +- tests/repl/errmsgs.check | 16 ++++++++-------- tests/repl/imports.check | 2 +- 16 files changed, 82 insertions(+), 46 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index ecb6a3212..af34164dc 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -9,6 +9,7 @@ import Decorators._ import language.higherKinds import collection.mutable.ListBuffer import util.Property +import reporting.diagnostic.messages._ object desugar { import untpd._ @@ -71,7 +72,9 @@ object desugar { val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol if (local.exists) (defctx.owner.thisType select local).dealias - else throw new Error(s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}") + else throw new java.lang.Error( + s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}" + ) case _ => mapOver(tp) } @@ -281,7 +284,7 @@ object desugar { val constrVparamss = if (constr1.vparamss.isEmpty) { // ensure parameter list is non-empty if (isCaseClass) - ctx.error("case class needs to have at least one parameter list", cdef.pos) + ctx.error(CaseClassMissingParamList(cdef), cdef.namePos) ListOfNil } else constr1.vparamss.nestedMap(toDefParam) diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 620cc1273..6d40107a8 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -215,6 +215,9 @@ object Parsers { } } + def warning(msg: Message, sourcePos: SourcePosition) = + ctx.warning(msg, sourcePos) + def warning(msg: Message, offset: Int = in.offset) = ctx.warning(msg, source atPos Position(offset)) @@ -1006,6 +1009,7 @@ object Parsers { DoWhile(body, cond) } case TRY => + val tryOffset = in.offset atPos(in.skipToken()) { val body = expr() val handler = @@ -1014,16 +1018,26 @@ object Parsers { expr() } else EmptyTree + // A block ends before RBRACE, if next token is RBRACE, simply add 1 + def realEnd(pos: Position) = + if (in.token == RBRACE) pos.end + 1 + else pos.end + handler match { - case Block(Nil, EmptyTree) => - syntaxError(new EmptyCatchBlock(body), handler.pos) + case Block(Nil, EmptyTree) => syntaxError( + new EmptyCatchBlock(body), + Position(tryOffset, realEnd(handler.pos)) + ) case _ => } val finalizer = if (in.token == FINALLY) { accept(FINALLY); expr() } else { - if (handler.isEmpty) warning(EmptyCatchAndFinallyBlock(body)) + if (handler.isEmpty) warning( + EmptyCatchAndFinallyBlock(body), + source atPos Position(tryOffset, realEnd(body.pos)) + ) EmptyTree } ParsedTry(body, handler, finalizer) diff --git a/src/dotty/tools/dotc/printing/Formatting.scala b/src/dotty/tools/dotc/printing/Formatting.scala index 95ac03647..9cbf07914 100644 --- a/src/dotty/tools/dotc/printing/Formatting.scala +++ b/src/dotty/tools/dotc/printing/Formatting.scala @@ -74,9 +74,9 @@ object Formatting { } class SyntaxFormatter(sc: StringContext) extends StringFormatter(sc) { - override protected def showArg(arg: Any)(implicit ctx: Context): String = { - if (ctx.settings.color.value != "never") arg match { - case arg: Showable => + override protected def showArg(arg: Any)(implicit ctx: Context): String = + arg match { + case arg: Showable if ctx.settings.color.value != "never" => val highlighted = SyntaxHighlighting(wrapNonSensical(arg, super.showArg(arg))) new String(highlighted.toArray) @@ -84,12 +84,10 @@ object Formatting { hl.show case hb: HighlightBuffer => hb.toString - case str: String => + case str: String if ctx.settings.color.value != "never" => new String(SyntaxHighlighting(str).toArray) case _ => super.showArg(arg) } - else super.showArg(arg) - } } private def wrapNonSensical(arg: Any, str: String)(implicit ctx: Context): String = { diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index d96ff48a4..6ebd53bea 100644 --- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -108,6 +108,7 @@ class ConsoleReporter( |${Blue("Explanation")} |${Blue("===========")}""".stripMargin) printMessage(m.explanation) + if (m.explanation.lastOption != Some('\n')) printMessage("") } override def doReport(m: MessageContainer)(implicit ctx: Context): Unit = { diff --git a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala index c5ff8cb6b..6fd971c2a 100644 --- a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala +++ b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala @@ -7,10 +7,8 @@ import util.{SourcePosition, SourceFile} import core.Contexts.Context import diagnostic.MessageContainer -/** - * This trait implements `isHidden` so that multiple messages per position - * are suppressed, unless they are of increasing severity. - */ +/** This trait implements `isHidden` so that multiple messages per position + * are suppressed, unless they are of increasing severity. */ trait UniqueMessagePositions extends Reporter { private val positions = new mutable.HashMap[(SourceFile, Int), Int] @@ -21,10 +19,14 @@ trait UniqueMessagePositions extends Reporter { override def isHidden(m: MessageContainer)(implicit ctx: Context): Boolean = super.isHidden(m) || { m.pos.exists && { - positions get (ctx.source, m.pos.point) match { - case Some(level) if level >= m.level => true - case _ => positions((ctx.source, m.pos.point)) = m.level; false + var shouldHide = false + for (pos <- m.pos.start to m.pos.end) { + positions get (ctx.source, pos) match { + case Some(level) if level >= m.level => shouldHide = true + case _ => positions((ctx.source, pos)) = m.level + } } + shouldHide } } } diff --git a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 4553a2d22..14978449a 100644 --- a/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -166,9 +166,22 @@ object messages { } } + case class CaseClassMissingParamList(cdef: untpd.TypeDef)(implicit ctx: Context) + extends Message(4) { + val kind = "Syntax" + val msg = + hl"""|A ${"case class"} must have at least one parameter list""" + + val explanation = + hl"""|${cdef.name} must have at least one parameter list, if you would rather + |have a singleton representation of ${cdef.name}, use a "${"case object"}". + |Or, add an explicit `()' as a parameter list to ${cdef.name}.""".stripMargin + } + + // Type Errors ------------------------------------------------------------ // case class DuplicateBind(bind: untpd.Bind, tree: untpd.CaseDef)(implicit ctx: Context) - extends Message(4) { + extends Message(5) { val kind = "Naming" val msg = em"duplicate pattern variable: `${bind.name}`" @@ -195,7 +208,7 @@ object messages { } case class MissingIdent(tree: untpd.Ident, treeKind: String, name: String)(implicit ctx: Context) - extends Message(5) { + extends Message(6) { val kind = "Missing Identifier" val msg = em"not found: $treeKind$name" @@ -206,7 +219,7 @@ object messages { } case class TypeMismatch(found: Type, expected: Type, whyNoMatch: String = "", implicitFailure: String = "")(implicit ctx: Context) - extends Message(6) { + extends Message(7) { val kind = "Type Mismatch" private val (where, printCtx) = Formatting.disambiguateTypes(found, expected) private val (fnd, exp) = Formatting.typeDiff(found, expected)(printCtx) diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index b345dda61..065bcb397 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -145,17 +145,22 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete }) Block(List(labelDef), ref(label).appliedToArgss(vparamss0.map(_.map(x=> ref(x.symbol))))) }} else { - if (mandatory) - ctx.error("TailRec optimisation not applicable, method not tail recursive", dd.pos) + if (mandatory) ctx.error( + "TailRec optimisation not applicable, method not tail recursive", + // FIXME: want to report this error on `dd.namePos`, but + // because of extension method getting a weird pos, it is + // better to report on symbol so there's no overlap + sym.pos + ) dd.rhs } }) } case d: DefDef if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) => - ctx.error("TailRec optimisation not applicable, method is neither private nor final so can be overridden", d.pos) + ctx.error("TailRec optimisation not applicable, method is neither private nor final so can be overridden", sym.pos) d case d if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) => - ctx.error("TailRec optimisation not applicable, not a method", d.pos) + ctx.error("TailRec optimisation not applicable, not a method", sym.pos) d case _ => tree } diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index b02b0ad21..7ba66e3d8 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -45,7 +45,7 @@ object Checking { for ((arg, which, bound) <- ctx.boundsViolations(args, boundss, instantiate)) ctx.error( ex"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}", - arg.pos) + arg.pos.focus) } /** Check that type arguments `args` conform to corresponding bounds in `poly` @@ -98,9 +98,9 @@ object Checking { checkWildcardHKApply(tycon.tpe.appliedTo(args.map(_.tpe)), tree.pos) checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply)) case Select(qual, name) if name.isTypeName => - checkRealizable(qual.tpe, qual.pos) + checkRealizable(qual.tpe, qual.pos.focus) case SingletonTypeTree(ref) => - checkRealizable(ref.tpe, ref.pos) + checkRealizable(ref.tpe, ref.pos.focus) case _ => } traverseChildren(tree) @@ -378,7 +378,7 @@ object Checking { if (tp.symbol.is(Private) && !accessBoundary(sym).isContainedIn(tp.symbol.owner)) { errors = (em"non-private $sym refers to private ${tp.symbol}\n in its type signature ${sym.info}", - pos) :: errors + sym.pos) :: errors tp } else mapOver(tp) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index a4dc2f871..bbb20bcf5 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1259,7 +1259,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) .withType(dummy.nonMemberTermRef) checkVariance(impl1) - if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.pos) + if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls.typeRef, cdef.namePos) val cdef1 = assignType(cpy.TypeDef(cdef)(name, impl1, Nil), cls) if (ctx.phase.isTyper && cdef1.tpe.derivesFrom(defn.DynamicClass) && !ctx.dynamicsEnabled) { val isRequired = parents1.exists(_.tpe.isRef(defn.DynamicClass)) diff --git a/tests/neg/applydynamic_sip.scala b/tests/neg/applydynamic_sip.scala index 7b131e7ff..86cff5fc4 100644 --- a/tests/neg/applydynamic_sip.scala +++ b/tests/neg/applydynamic_sip.scala @@ -18,9 +18,9 @@ object Test extends App { } val bad1 = new Bad1 bad1.sel // error - bad1.sel(1) // error // error - bad1.sel(a = 1) // error // error - bad1.sel = 1 // error // error + bad1.sel(1) // error + bad1.sel(a = 1) // error + bad1.sel = 1 // error class Bad2 extends Dynamic { def selectDynamic = 1 diff --git a/tests/neg/assignments.scala b/tests/neg/assignments.scala index 5be107717..273419cb5 100644 --- a/tests/neg/assignments.scala +++ b/tests/neg/assignments.scala @@ -13,7 +13,7 @@ object assignments { x = x + 1 x *= 2 - x_= = 2 // error should give missing arguments + // error reassignment to val + x_= = 2 // error should give missing arguments } var c = new C diff --git a/tests/neg/dynamicApplyDynamicTest3.scala b/tests/neg/dynamicApplyDynamicTest3.scala index 61d3c9677..d68132b02 100644 --- a/tests/neg/dynamicApplyDynamicTest3.scala +++ b/tests/neg/dynamicApplyDynamicTest3.scala @@ -3,5 +3,5 @@ import scala.language.dynamics class Foo extends scala.Dynamic object DynamicTest { - new Foo().bazApply _ // error // error + new Foo().bazApply _ // error } diff --git a/tests/neg/i1424.scala b/tests/neg/i1424.scala index 3586260c1..8eba32842 100644 --- a/tests/neg/i1424.scala +++ b/tests/neg/i1424.scala @@ -1,3 +1,3 @@ class Test { - (x: Int) => x // error: not a legal self type clause // error: package x is not a value // error: package x is not a value + (x: Int) => x // error: not a legal self type clause // error: not found x } diff --git a/tests/neg/tailcall/t6574.scala b/tests/neg/tailcall/t6574.scala index d9ba2882d..462ef800f 100644 --- a/tests/neg/tailcall/t6574.scala +++ b/tests/neg/tailcall/t6574.scala @@ -4,7 +4,7 @@ class Bad[X, Y](val v: Int) extends AnyVal { println("tail") } - @annotation.tailrec final def differentTypeArgs : Unit = { // error + @annotation.tailrec final def differentTypeArgs: Unit = { // error {(); new Bad[String, Unit](0)}.differentTypeArgs // error } } diff --git a/tests/repl/errmsgs.check b/tests/repl/errmsgs.check index 2a65fd949..066d98d0f 100644 --- a/tests/repl/errmsgs.check +++ b/tests/repl/errmsgs.check @@ -1,34 +1,34 @@ scala> class Inv[T](x: T) defined class Inv scala> val x: List[String] = List(1) --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 4 |val x: List[String] = List(1) | ^ | found: Int(1) | required: String | scala> val y: List[List[String]] = List(List(1)) --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 4 |val y: List[List[String]] = List(List(1)) | ^ | found: Int(1) | required: String | scala> val z: (List[String], List[Int]) = (List(1), List("a")) --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 4 |val z: (List[String], List[Int]) = (List(1), List("a")) | ^ | found: Int(1) | required: String | --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 4 |val z: (List[String], List[Int]) = (List(1), List("a")) | ^^^ | found: String("a") | required: Int | scala> val a: Inv[String] = new Inv(new Inv(1)) --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 5 |val a: Inv[String] = new Inv(new Inv(1)) | ^^^^^ | found: Inv[T] @@ -36,7 +36,7 @@ scala> val a: Inv[String] = new Inv(new Inv(1)) | | where: T is a type variable with constraint >: Int(1) scala> val b: Inv[String] = new Inv(1) --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 5 |val b: Inv[String] = new Inv(1) | ^ | found: Int(1) @@ -57,7 +57,7 @@ scala> abstract class C { } } } --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 9 | var y: T = x | ^ | found: C.this.T(C.this.x) @@ -65,7 +65,7 @@ scala> abstract class C { | | where: T is a type in class C | T' is a type in the initalizer of value s which is an alias of String --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 13 | val z: T = y | ^ | found: T(y) diff --git a/tests/repl/imports.check b/tests/repl/imports.check index 9743d2b94..26b725637 100644 --- a/tests/repl/imports.check +++ b/tests/repl/imports.check @@ -7,7 +7,7 @@ defined module o scala> import o._ import o._ scala> buf += xs --- [E006] Type Mismatch Error: ------------------------------------------------------------------------------- +-- [E007] Type Mismatch Error: ------------------------------------------------------------------------------- 11 |buf += xs | ^^ | found: scala.collection.immutable.List[Int](o.xs) -- cgit v1.2.3