summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala6
-rw-r--r--src/compiler/scala/tools/nsc/util/CharArrayReader.scala26
-rw-r--r--src/reflect/scala/reflect/internal/util/Position.scala33
-rw-r--r--src/reflect/scala/reflect/internal/util/SourceFile.scala44
-rw-r--r--test/files/neg/t8015-ffa.check6
-rw-r--r--test/files/neg/t8015-ffa.scala8
-rw-r--r--test/files/neg/t8015-ffb.check6
-rw-r--r--test/files/neg/t8015-ffb.flags1
-rw-r--r--test/files/neg/t8015-ffb.scala11
-rw-r--r--test/files/run/t8015-ffc.scala7
10 files changed, 113 insertions, 35 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 87bd498ea1..32c15b04aa 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -206,9 +206,9 @@ trait Scanners extends ScannersCommon {
token = kwArray(idx)
if (token == IDENTIFIER && allowIdent != name) {
if (name == nme.MACROkw)
- syntaxError(name+" is now a reserved word; usage as an identifier is disallowed")
+ syntaxError(s"$name is now a reserved word; usage as an identifier is disallowed")
else if (emitIdentifierDeprecationWarnings)
- deprecationWarning(name+" is now a reserved word; usage as an identifier is deprecated")
+ deprecationWarning(s"$name is now a reserved word; usage as an identifier is deprecated")
}
}
}
@@ -389,7 +389,7 @@ trait Scanners extends ScannersCommon {
// println("blank line found at "+lastOffset+":"+(lastOffset to idx).map(buf(_)).toList)
return true
}
- if (idx == end) return false
+ if (idx == end) return false
} while (ch <= ' ')
}
idx += 1; ch = buf(idx)
diff --git a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala
index f116e4af34..e6f95eb0d6 100644
--- a/src/compiler/scala/tools/nsc/util/CharArrayReader.scala
+++ b/src/compiler/scala/tools/nsc/util/CharArrayReader.scala
@@ -46,7 +46,7 @@ abstract class CharArrayReader extends CharArrayReaderData { self =>
def isUnicodeEscape = charOffset == lastUnicodeOffset
/** Advance one character; reducing CR;LF pairs to just LF */
- final def nextChar() {
+ final def nextChar(): Unit = {
if (charOffset >= buf.length) {
ch = SU
} else {
@@ -54,7 +54,10 @@ abstract class CharArrayReader extends CharArrayReaderData { self =>
ch = c
charOffset += 1
if (c == '\\') potentialUnicode()
- else if (c < ' ') { skipCR(); potentialLineEnd() }
+ if (ch < ' ') {
+ skipCR()
+ potentialLineEnd()
+ }
}
}
@@ -74,7 +77,7 @@ abstract class CharArrayReader extends CharArrayReaderData { self =>
}
/** Interpret \\uxxxx escapes */
- private def potentialUnicode() {
+ private def potentialUnicode() = {
def evenSlashPrefix: Boolean = {
var p = charOffset - 2
while (p >= 0 && buf(p) == '\\') p -= 1
@@ -105,13 +108,17 @@ abstract class CharArrayReader extends CharArrayReaderData { self =>
}
/** replace CR;LF by LF */
- private def skipCR() {
- if (ch == CR)
- if (charOffset < buf.length && buf(charOffset) == LF) {
- charOffset += 1
- ch = LF
+ private def skipCR() =
+ if (ch == CR && charOffset < buf.length)
+ buf(charOffset) match {
+ case LF =>
+ charOffset += 1
+ ch = LF
+ case '\\' =>
+ if (lookaheadReader.getu == LF)
+ potentialUnicode()
+ case _ =>
}
- }
/** Handle line ends */
private def potentialLineEnd() {
@@ -132,5 +139,6 @@ abstract class CharArrayReader extends CharArrayReaderData { self =>
def error(offset: Int, msg: String) = self.error(offset, msg)
/** A mystery why CharArrayReader.nextChar() returns Unit */
def getc() = { nextChar() ; ch }
+ def getu() = { require(buf(charOffset) == '\\') ; ch = '\\' ; charOffset += 1 ; potentialUnicode() ; ch }
}
}
diff --git a/src/reflect/scala/reflect/internal/util/Position.scala b/src/reflect/scala/reflect/internal/util/Position.scala
index 15cfda26b5..4032ec1157 100644
--- a/src/reflect/scala/reflect/internal/util/Position.scala
+++ b/src/reflect/scala/reflect/internal/util/Position.scala
@@ -202,12 +202,31 @@ private[util] trait InternalPositionImpl {
def line: Int = if (hasSource) source.offsetToLine(point) + 1 else 0
def column: Int = if (hasSource) calculateColumn() else 0
def lineContent: String = if (hasSource) source.lineToString(line - 1) else ""
- def lineCarat: String = if (hasSource) " " * (column - 1) + "^" else ""
-
- def showError(msg: String): String = finalPosition match {
- case FakePos(fmsg) => s"$fmsg $msg"
- case NoPosition => msg
- case pos => s"${pos.line}: $msg\n${pos.lineContent}\n${pos.lineCarat}"
+ def lineCaret: String = if (hasSource) " " * (column - 1) + "^" else ""
+ @deprecated("use `lineCaret`", since="2.11.0")
+ def lineCarat: String = lineCaret
+
+ def showError(msg: String): String = {
+ def escaped(s: String) = {
+ def u(c: Int) = f"\\u$c%04x"
+ def uable(c: Int) = (c < 0x20 && c != '\t') || c == 0x7F
+ if (s exists (c => uable(c))) {
+ val sb = new StringBuilder
+ s foreach (c => sb append (if (uable(c)) u(c) else c))
+ sb.toString
+ } else s
+ }
+ def errorAt(p: Pos) = {
+ def where = p.line
+ def content = escaped(p.lineContent)
+ def indicator = p.lineCaret
+ f"$where: $msg%n$content%n$indicator"
+ }
+ finalPosition match {
+ case FakePos(fmsg) => s"$fmsg $msg"
+ case NoPosition => msg
+ case pos => errorAt(pos)
+ }
}
def showDebug: String = toString
def show = (
@@ -254,7 +273,7 @@ private[util] trait DeprecatedPosition {
@deprecated("use `finalPosition`", "2.11.0")
def inUltimateSource(source: SourceFile): Position = source positionInUltimateSource this
- @deprecated("use `lineCarat`", "2.11.0")
+ @deprecated("use `lineCaret`", since="2.11.0")
def lineWithCarat(maxWidth: Int): (String, String) = ("", "")
@deprecated("Use `withSource(source)` and `withShift`", "2.11.0")
diff --git a/src/reflect/scala/reflect/internal/util/SourceFile.scala b/src/reflect/scala/reflect/internal/util/SourceFile.scala
index 3b6c57e955..9866b043bb 100644
--- a/src/reflect/scala/reflect/internal/util/SourceFile.scala
+++ b/src/reflect/scala/reflect/internal/util/SourceFile.scala
@@ -16,12 +16,13 @@ import scala.reflect.internal.Chars._
/** abstract base class of a source file used in the compiler */
abstract class SourceFile {
- def content : Array[Char] // normalized, must end in SU
- def file : AbstractFile
- def isLineBreak(idx : Int) : Boolean
+ def content: Array[Char] // normalized, must end in SU
+ def file : AbstractFile
+ def isLineBreak(idx: Int): Boolean
+ def isEndOfLine(idx: Int): Boolean
def isSelfContained: Boolean
def length : Int
- def position(offset: Int) : Position = {
+ def position(offset: Int): Position = {
assert(offset < length, file + ": " + offset + " >= " + length)
Position.offset(this, offset)
}
@@ -36,8 +37,12 @@ abstract class SourceFile {
override def toString() = file.name
def path = file.path
- def lineToString(index: Int): String =
- content drop lineToOffset(index) takeWhile (c => !isLineBreakChar(c.toChar)) mkString ""
+ def lineToString(index: Int): String = {
+ val start = lineToOffset(index)
+ var end = start
+ while (!isEndOfLine(end)) end += 1
+ content.slice(start, end) mkString ""
+ }
@tailrec
final def skipWhitespace(offset: Int): Int =
@@ -52,6 +57,7 @@ object NoSourceFile extends SourceFile {
def content = Array()
def file = NoFile
def isLineBreak(idx: Int) = false
+ def isEndOfLine(idx: Int) = false
def isSelfContained = true
def length = -1
def offsetToLine(offset: Int) = -1
@@ -128,18 +134,24 @@ class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extend
super.identifier(pos)
}
- 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.
- if (ch == CR) (idx + 1 == length) || (content(idx + 1) != LF)
- else isLineBreakChar(ch)
- }
+ private def charAtIsEOL(idx: Int)(p: Char => Boolean) = {
+ // don't identify the CR in CR LF as a line break, since LF will do.
+ def notCRLF0 = content(idx) != CR || idx + 1 >= length || content(idx + 1) != LF
+
+ idx < length && notCRLF0 && p(content(idx))
+ }
+
+ def isLineBreak(idx: Int) = charAtIsEOL(idx)(isLineBreakChar)
+
+ def isEndOfLine(idx: Int) = charAtIsEOL(idx) {
+ case CR | LF => true
+ case _ => false
+ }
def calculateLineIndices(cs: Array[Char]) = {
val buf = new ArrayBuffer[Int]
buf += 0
- for (i <- 0 until cs.length) if (isLineBreak(i)) buf += i + 1
+ for (i <- 0 until cs.length) if (isEndOfLine(i)) buf += i + 1
buf += cs.length // sentinel, so that findLine below works smoother
buf.toArray
}
@@ -149,8 +161,8 @@ class BatchSourceFile(val file : AbstractFile, val content0: Array[Char]) extend
private var lastLine = 0
- /** Convert offset to line in this source file
- * Lines are numbered from 0
+ /** Convert offset to line in this source file.
+ * Lines are numbered from 0.
*/
def offsetToLine(offset: Int): Int = {
val lines = lineIndices
diff --git a/test/files/neg/t8015-ffa.check b/test/files/neg/t8015-ffa.check
new file mode 100644
index 0000000000..0f28be7fe7
--- /dev/null
+++ b/test/files/neg/t8015-ffa.check
@@ -0,0 +1,6 @@
+t8015-ffa.scala:7: error: type mismatch;
+ found : String("3")
+ required: Int
+ val i: Int = "3" // error line 7 (was 8)
+ ^
+one error found
diff --git a/test/files/neg/t8015-ffa.scala b/test/files/neg/t8015-ffa.scala
new file mode 100644
index 0000000000..60876d9139
--- /dev/null
+++ b/test/files/neg/t8015-ffa.scala
@@ -0,0 +1,8 @@
+
+package foo
+
+//------- object Next
+
+trait F {
+ val i: Int = "3" // error line 7 (was 8)
+}
diff --git a/test/files/neg/t8015-ffb.check b/test/files/neg/t8015-ffb.check
new file mode 100644
index 0000000000..9b2171ea47
--- /dev/null
+++ b/test/files/neg/t8015-ffb.check
@@ -0,0 +1,6 @@
+t8015-ffb.scala:10: warning: side-effecting nullary methods are discouraged: suggest defining as `def w()` instead
+ def w = { x\u000c() } // ^L is colored blue on this screen, hardly visible
+ ^
+error: No warnings can be incurred under -Xfatal-warnings.
+one warning found
+one error found
diff --git a/test/files/neg/t8015-ffb.flags b/test/files/neg/t8015-ffb.flags
new file mode 100644
index 0000000000..7949c2afa2
--- /dev/null
+++ b/test/files/neg/t8015-ffb.flags
@@ -0,0 +1 @@
+-Xlint -Xfatal-warnings
diff --git a/test/files/neg/t8015-ffb.scala b/test/files/neg/t8015-ffb.scala
new file mode 100644
index 0000000000..dbdd942555
--- /dev/null
+++ b/test/files/neg/t8015-ffb.scala
@@ -0,0 +1,11 @@
+
+trait G {
+ val c: Char = '\u000a' // disallowed!
+ def x\u000d\u000a = 9 // as nl
+ def y() = x
+ def z() = {
+ y()\u000a() // was Int does not take parameters
+ }
+ def v = y()\u000c() // was Int does not take parameters
+ def w = { x () } // ^L is colored blue on this screen, hardly visible
+}
diff --git a/test/files/run/t8015-ffc.scala b/test/files/run/t8015-ffc.scala
new file mode 100644
index 0000000000..fe6781be42
--- /dev/null
+++ b/test/files/run/t8015-ffc.scala
@@ -0,0 +1,7 @@
+
+object Test extends App {
+ val ms = """This is a long multiline string
+ with \u000d\u000a CRLF embedded."""
+ assert(ms.lines.size == 3, s"lines.size ${ms.lines.size}")
+ assert(ms contains "\r\n CRLF", "no CRLF")
+}