From d00572d159a3a15664afbf09d1ff914df4d2c512 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 25 Jun 2015 12:01:32 +0200 Subject: [PATCH 1/5] Avoid crasher when first token of a program is in error This used to give a crash in SourcePositiom, promoted by feeding its calculations with a negative offset. --- src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- src/dotty/tools/dotc/util/SourceFile.scala | 5 ++++- test/dotc/tests.scala | 1 + tests/neg/firstError.scala | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/neg/firstError.scala diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index b790772455a6..2a42a7fa9bcf 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -192,7 +192,7 @@ object Parsers { case _ => if (mustStartStat && in.isAfterLineEnd() && - isLeqIndented(in.offset, lastStatOffset)) + isLeqIndented(in.offset, lastStatOffset max 0)) return } in.nextToken() diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala index 45119a881538..6e2ac7d79741 100644 --- a/src/dotty/tools/dotc/util/SourceFile.scala +++ b/src/dotty/tools/dotc/util/SourceFile.scala @@ -103,7 +103,10 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) { lastLine } - def startOfLine(offset: Int): Int = lineToOffset(offsetToLine(offset)) + def startOfLine(offset: Int): Int = { + require(offset >= 0) + lineToOffset(offsetToLine(offset)) + } def nextLine(offset: Int): Int = lineToOffset(offsetToLine(offset) + 1 min lineIndices.length - 1) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 1aa35e3ee66b..520abc222f1a 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -140,6 +140,7 @@ class tests extends CompilerTest { @Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2) @Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5) @Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2) + @Test def neg_firstError = compileFile(negDir, "firstError", xerrors = 3) @Test def run_all = runFiles(runDir) diff --git a/tests/neg/firstError.scala b/tests/neg/firstError.scala new file mode 100644 index 000000000000..317adcceddcc --- /dev/null +++ b/tests/neg/firstError.scala @@ -0,0 +1,4 @@ +. + +\u890u3084eu + From de1042ac04085b496b006c6567d8e737ca5d8ee7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 25 Jun 2015 12:50:59 +0200 Subject: [PATCH 2/5] Make columns start at 0. Lines already start at 0, so columns should, too. --- src/dotty/tools/dotc/reporting/ConsoleReporter.scala | 2 +- src/dotty/tools/dotc/util/SourceFile.scala | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala index f07f43a63675..26e6324eb803 100644 --- a/src/dotty/tools/dotc/reporting/ConsoleReporter.scala +++ b/src/dotty/tools/dotc/reporting/ConsoleReporter.scala @@ -25,7 +25,7 @@ class ConsoleReporter( printMessage(pos.lineContents.stripLineEnd) def printColumnMarker(pos: SourcePosition) = - if (pos.exists) { printMessage(" " * (pos.column - 1) + "^") } + if (pos.exists) { printMessage(" " * pos.column + "^") } /** Prints the message. */ def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() } diff --git a/src/dotty/tools/dotc/util/SourceFile.scala b/src/dotty/tools/dotc/util/SourceFile.scala index 6e2ac7d79741..5e8f85e2806f 100644 --- a/src/dotty/tools/dotc/util/SourceFile.scala +++ b/src/dotty/tools/dotc/util/SourceFile.scala @@ -72,7 +72,7 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) { def positionInUltimateSource(position: SourcePosition): SourcePosition = SourcePosition(underlying, position.pos shift start) - def isLineBreak(idx: Int) = + private def isLineBreak(idx: Int) = if (idx >= length) false else { val ch = content(idx) // don't identify the CR in CR LF as a line break, since LF will do. @@ -80,7 +80,7 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) { else isLineBreakChar(ch) } - def calculateLineIndices(cs: Array[Char]) = { + private def calculateLineIndices(cs: Array[Char]) = { val buf = new ArrayBuffer[Int] buf += 0 for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1 @@ -103,25 +103,29 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) { lastLine } + /** The index of the first character of the line containing position `offset` */ def startOfLine(offset: Int): Int = { require(offset >= 0) lineToOffset(offsetToLine(offset)) } + /** The start index of the line following the one containing position `offset` */ def nextLine(offset: Int): Int = lineToOffset(offsetToLine(offset) + 1 min lineIndices.length - 1) + /** The contents of the line containing position `offset` */ def lineContents(offset: Int): String = content.slice(startOfLine(offset), nextLine(offset)).mkString + /** The column corresponding to `offset`, starting at 0 */ def column(offset: Int): Int = { var idx = startOfLine(offset) var col = 0 while (idx != offset) { - col += (if (content(idx) == '\t') tabInc - col % tabInc else 1) + col += (if (content(idx) == '\t') (tabInc - col) % tabInc else 1) idx += 1 } - col + 1 + col } override def toString = file.toString From 9e77285fc4487330da1bc2ed210dadda92de9303 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 25 Jun 2015 12:51:58 +0200 Subject: [PATCH 3/5] Document that lines and columns start at 0. And adjust for it in DottyBackendInterface --- src/dotty/tools/backend/jvm/DottyBackendInterface.scala | 2 +- src/dotty/tools/dotc/util/SourcePosition.scala | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala index 855147ac44ff..d7a5c7be03b5 100644 --- a/src/dotty/tools/backend/jvm/DottyBackendInterface.scala +++ b/src/dotty/tools/backend/jvm/DottyBackendInterface.scala @@ -480,7 +480,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{ implicit def positionHelper(a: Position): PositionHelper = new PositionHelper { def isDefined: Boolean = a.exists - def line: Int = sourcePos(a).line + def line: Int = sourcePos(a).line + 1 def finalPosition: Position = a } diff --git a/src/dotty/tools/dotc/util/SourcePosition.scala b/src/dotty/tools/dotc/util/SourcePosition.scala index 3d6352a1a0f3..c88cbc78b181 100644 --- a/src/dotty/tools/dotc/util/SourcePosition.scala +++ b/src/dotty/tools/dotc/util/SourcePosition.scala @@ -10,7 +10,11 @@ case class SourcePosition(source: SourceFile, pos: Position) { def exists = pos.exists def lineContents: String = source.lineContents(point) + + /** The line of the position, starting at 0 */ def line: Int = source.offsetToLine(point) + + /** The column of the position, starting at 0 */ def column: Int = source.column(point) override def toString = From 41944a049064fffc3065d3077fad7c832bb444a2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 25 Jun 2015 22:14:23 +0200 Subject: [PATCH 4/5] Update check file. Line numbers were wrong in previous file. --- tests/run/origins.check | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/run/origins.check b/tests/run/origins.check index 771baf9fde10..54d58296b9b6 100644 --- a/tests/run/origins.check +++ b/tests/run/origins.check @@ -1,6 +1,6 @@ >> Origins tag 'boop' logged 65 calls from 3 distinguished sources. - 50 Test$.$anonfun$f3$1(origins.scala:21) - 10 Test$.$anonfun$f2$1(origins.scala:20) - 5 Test$.$anonfun$f1$1(origins.scala:19) + 50 Test$.$anonfun$f3$1(origins.scala:16) + 10 Test$.$anonfun$f2$1(origins.scala:15) + 5 Test$.$anonfun$f1$1(origins.scala:14) From 49ae820f3e8d08cf5db4a1165df980328172b42f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 25 Jun 2015 22:42:07 +0200 Subject: [PATCH 5/5] Updated check file. Seems partest adds code to the sources so that line numbers are not the same. --- tests/run/origins.check | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/run/origins.check b/tests/run/origins.check index 54d58296b9b6..db9bf29a4f80 100644 --- a/tests/run/origins.check +++ b/tests/run/origins.check @@ -1,6 +1,6 @@ >> Origins tag 'boop' logged 65 calls from 3 distinguished sources. - 50 Test$.$anonfun$f3$1(origins.scala:16) - 10 Test$.$anonfun$f2$1(origins.scala:15) - 5 Test$.$anonfun$f1$1(origins.scala:14) + 50 Test$.$anonfun$f3$1(origins.scala:22) + 10 Test$.$anonfun$f2$1(origins.scala:21) + 5 Test$.$anonfun$f1$1(origins.scala:20)