Skip to content

Commit d4e80f4

Browse files
committed
Merge pull request #695 from dotty-staging/fix/source-positions
Avoid crasher when first token of a program is in error
2 parents 52b720a + 49ae820 commit d4e80f4

File tree

8 files changed

+27
-11
lines changed

8 files changed

+27
-11
lines changed

src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -480,7 +480,7 @@ class DottyBackendInterface()(implicit ctx: Context) extends BackendInterface{
480480

481481
implicit def positionHelper(a: Position): PositionHelper = new PositionHelper {
482482
def isDefined: Boolean = a.exists
483-
def line: Int = sourcePos(a).line
483+
def line: Int = sourcePos(a).line + 1
484484
def finalPosition: Position = a
485485
}
486486

src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ object Parsers {
192192
case _ =>
193193
if (mustStartStat &&
194194
in.isAfterLineEnd() &&
195-
isLeqIndented(in.offset, lastStatOffset))
195+
isLeqIndented(in.offset, lastStatOffset max 0))
196196
return
197197
}
198198
in.nextToken()

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class ConsoleReporter(
2525
printMessage(pos.lineContents.stripLineEnd)
2626

2727
def printColumnMarker(pos: SourcePosition) =
28-
if (pos.exists) { printMessage(" " * (pos.column - 1) + "^") }
28+
if (pos.exists) { printMessage(" " * pos.column + "^") }
2929

3030
/** Prints the message. */
3131
def printMessage(msg: String): Unit = { writer.print(msg + "\n"); writer.flush() }

src/dotty/tools/dotc/util/SourceFile.scala

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,15 +72,15 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) {
7272
def positionInUltimateSource(position: SourcePosition): SourcePosition =
7373
SourcePosition(underlying, position.pos shift start)
7474

75-
def isLineBreak(idx: Int) =
75+
private def isLineBreak(idx: Int) =
7676
if (idx >= length) false else {
7777
val ch = content(idx)
7878
// don't identify the CR in CR LF as a line break, since LF will do.
7979
if (ch == CR) (idx + 1 == length) || (content(idx + 1) != LF)
8080
else isLineBreakChar(ch)
8181
}
8282

83-
def calculateLineIndices(cs: Array[Char]) = {
83+
private def calculateLineIndices(cs: Array[Char]) = {
8484
val buf = new ArrayBuffer[Int]
8585
buf += 0
8686
for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1
@@ -103,22 +103,29 @@ case class SourceFile(file: AbstractFile, content: Array[Char]) {
103103
lastLine
104104
}
105105

106-
def startOfLine(offset: Int): Int = lineToOffset(offsetToLine(offset))
106+
/** The index of the first character of the line containing position `offset` */
107+
def startOfLine(offset: Int): Int = {
108+
require(offset >= 0)
109+
lineToOffset(offsetToLine(offset))
110+
}
107111

112+
/** The start index of the line following the one containing position `offset` */
108113
def nextLine(offset: Int): Int =
109114
lineToOffset(offsetToLine(offset) + 1 min lineIndices.length - 1)
110115

116+
/** The contents of the line containing position `offset` */
111117
def lineContents(offset: Int): String =
112118
content.slice(startOfLine(offset), nextLine(offset)).mkString
113119

120+
/** The column corresponding to `offset`, starting at 0 */
114121
def column(offset: Int): Int = {
115122
var idx = startOfLine(offset)
116123
var col = 0
117124
while (idx != offset) {
118-
col += (if (content(idx) == '\t') tabInc - col % tabInc else 1)
125+
col += (if (content(idx) == '\t') (tabInc - col) % tabInc else 1)
119126
idx += 1
120127
}
121-
col + 1
128+
col
122129
}
123130

124131
override def toString = file.toString

src/dotty/tools/dotc/util/SourcePosition.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ case class SourcePosition(source: SourceFile, pos: Position) {
1010
def exists = pos.exists
1111

1212
def lineContents: String = source.lineContents(point)
13+
14+
/** The line of the position, starting at 0 */
1315
def line: Int = source.offsetToLine(point)
16+
17+
/** The column of the position, starting at 0 */
1418
def column: Int = source.column(point)
1519

1620
override def toString =

test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ class tests extends CompilerTest {
142142
@Test def neg_shadowedImplicits = compileFile(negDir, "arrayclone-new", xerrors = 2)
143143
@Test def neg_traitParamsTyper = compileFile(negDir, "traitParamsTyper", xerrors = 5)
144144
@Test def neg_traitParamsMixin = compileFile(negDir, "traitParamsMixin", xerrors = 2)
145+
@Test def neg_firstError = compileFile(negDir, "firstError", xerrors = 3)
145146

146147
@Test def run_all = runFiles(runDir)
147148

tests/neg/firstError.scala

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
.
2+
3+
\u890u3084eu
4+

tests/run/origins.check

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
>> Origins tag 'boop' logged 65 calls from 3 distinguished sources.
33

4-
50 Test$.$anonfun$f3$1(origins.scala:21)
5-
10 Test$.$anonfun$f2$1(origins.scala:20)
6-
5 Test$.$anonfun$f1$1(origins.scala:19)
4+
50 Test$.$anonfun$f3$1(origins.scala:22)
5+
10 Test$.$anonfun$f2$1(origins.scala:21)
6+
5 Test$.$anonfun$f1$1(origins.scala:20)

0 commit comments

Comments
 (0)