summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan@lightbend.com>2017-02-20 14:20:10 -0800
committerGitHub <noreply@github.com>2017-02-20 14:20:10 -0800
commit023a96afe30d04e6de771d0676b85b0889b89a37 (patch)
tree27a9342ae392a21bffad4c717f39d47bed5ac718
parente21ab425880b7c3fa4cca3c9503045823d250025 (diff)
parent05cc3e2271d2c2226ded33bef5a03f0c70c6f66d (diff)
downloadscala-023a96afe30d04e6de771d0676b85b0889b89a37.tar.gz
scala-023a96afe30d04e6de771d0676b85b0889b89a37.tar.bz2
scala-023a96afe30d04e6de771d0676b85b0889b89a37.zip
Merge pull request #5629 from som-snytt/issue/10120-quote-err
SI-10133 Require escaped single quote char lit
-rw-r--r--src/compiler/scala/tools/nsc/ast/parser/Scanners.scala24
-rw-r--r--src/repl/scala/tools/nsc/interpreter/Formatting.scala35
-rw-r--r--src/repl/scala/tools/nsc/interpreter/IMain.scala7
-rw-r--r--src/repl/scala/tools/nsc/interpreter/ReplReporter.scala33
-rw-r--r--test/files/jvm/interpreter.check8
-rw-r--r--test/files/neg/badtok-1.check8
-rw-r--r--test/files/neg/badtok-1.scala7
-rw-r--r--test/files/neg/t5856.check2
-rw-r--r--test/files/run/reify_newimpl_22.check2
-rw-r--r--test/files/run/reify_newimpl_23.check2
-rw-r--r--test/files/run/reify_newimpl_25.check2
-rw-r--r--test/files/run/reify_newimpl_26.check2
-rw-r--r--test/files/run/repl-colon-type.check8
-rw-r--r--test/files/run/t8918-unary-ids.check14
-rw-r--r--test/files/run/t9170.scala4
-rw-r--r--test/junit/scala/tools/nsc/interpreter/ScriptedTest.scala2
16 files changed, 84 insertions, 76 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
index 3ed1570c1c..d72002f0a7 100644
--- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
@@ -543,24 +543,36 @@ trait Scanners extends ScannersCommon {
}
fetchDoubleQuote()
case '\'' =>
+ def unclosedCharLit() = {
+ val msg = "unclosed character literal"
+ // previous token was Symbol contiguous with the orphan single quote at offset
+ if (token == SYMBOLLIT && offset == lastOffset) {
+ syntaxError(s"""$msg (or use " for string literal "$strVal")""")
+ } else {
+ syntaxError(msg)
+ }
+ }
def fetchSingleQuote() = {
nextChar()
if (isIdentifierStart(ch))
charLitOr(getIdentRest)
else if (isOperatorPart(ch) && (ch != '\\'))
charLitOr(getOperatorRest)
+ else if (ch == '\'') {
+ nextChar()
+ val advice = if (ch == '\'') { do nextChar() while (ch == '\''); " (use '\\'' for single quote)" } else ""
+ syntaxError(s"empty character literal${advice}")
+ }
else if (!isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape)) {
getLitChar()
- if (ch == '\'') {
+ if (ch != '\'') unclosedCharLit()
+ else {
nextChar()
token = CHARLIT
setStrVal()
- } else {
- syntaxError("unclosed character literal")
}
}
- else
- syntaxError("unclosed character literal")
+ else unclosedCharLit()
}
fetchSingleQuote()
case '.' =>
@@ -792,7 +804,7 @@ trait Scanners extends ScannersCommon {
next.token = kwArray(idx)
}
} else {
- syntaxError("invalid string interpolation: `$$', `$'ident or `$'BlockExpr expected")
+ syntaxError(s"invalid string interpolation $$$ch, expected: $$$$, $$identifier or $${expression}")
}
} else {
val isUnclosedLiteral = !isUnicodeEscape && (ch == SU || (!multiLine && (ch == CR || ch == LF)))
diff --git a/src/repl/scala/tools/nsc/interpreter/Formatting.scala b/src/repl/scala/tools/nsc/interpreter/Formatting.scala
deleted file mode 100644
index 4a9548730a..0000000000
--- a/src/repl/scala/tools/nsc/interpreter/Formatting.scala
+++ /dev/null
@@ -1,35 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2013 LAMP/EPFL
- * @author Paul Phillips
- */
-
-package scala.tools.nsc
-package interpreter
-
-import util.stringFromWriter
-
-class Formatting(indent: Int) {
-
- private val indentation = " " * indent
-
- private def indenting(code: String): Boolean = {
- /** Heuristic to avoid indenting and thereby corrupting """-strings and XML literals. */
- val tokens = List("\"\"\"", "</", "/>")
- val noIndent = (code contains "\n") && (tokens exists code.contains)
-
- !noIndent
- }
- /** Indent some code by the width of the scala> prompt.
- * This way, compiler error messages read better.
- */
- def indentCode(code: String) = stringFromWriter(str =>
- for (line <- code.lines) {
- if (indenting(code)) str print indentation
- str println line
- str.flush()
- }
- )
-}
-object Formatting {
- def forPrompt(prompt: String) = new Formatting(prompt.lines.toList.last.length)
-}
diff --git a/src/repl/scala/tools/nsc/interpreter/IMain.scala b/src/repl/scala/tools/nsc/interpreter/IMain.scala
index a351d2da95..2ae3b207b7 100644
--- a/src/repl/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/repl/scala/tools/nsc/interpreter/IMain.scala
@@ -111,11 +111,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
try body finally label = saved
}
- // the expanded prompt but without color escapes and without leading newline, for purposes of indenting
- lazy val formatting = Formatting.forPrompt(replProps.promptText)
lazy val reporter: ReplReporter = new ReplReporter(this)
- import formatting.indentCode
import reporter.{ printMessage, printUntruncatedMessage }
// This exists mostly because using the reporter too early leads to deadlock.
@@ -867,8 +864,8 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
|${preambleHeader format lineRep.readName}
|${envLines mkString (" ", ";\n ", ";\n")}
|$importsPreamble
- |${indentCode(toCompute)}""".stripMargin
- def preambleLength = preamble.length - toCompute.length - 1
+ |${toCompute}""".stripMargin
+ def preambleLength = preamble.length - toCompute.length
val generate = (m: MemberHandler) => m extraCodeToEvaluate Request.this
diff --git a/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala
index 3a0b69f41e..b01d242d44 100644
--- a/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala
+++ b/src/repl/scala/tools/nsc/interpreter/ReplReporter.scala
@@ -9,7 +9,7 @@ package interpreter
import reporters._
import IMain._
-import scala.reflect.internal.util.Position
+import scala.reflect.internal.util.{OffsetPosition, Position}
/** Like ReplGlobal, a layer for ensuring extra functionality.
*/
@@ -40,14 +40,25 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i
case INFO => RESET
}
+ private val promptLength = replProps.promptText.lines.toList.last.length
+ private val indentation = " " * promptLength
+
+ // colorized console labels
+ override protected def clabel(severity: Severity): String = {
+ val label0 = super.clabel(severity)
+ if (replProps.colorOk) s"${severityColor(severity)}${label0}${RESET}" else label0
+ }
+
+ // shift indentation for source text entered at prompt
override def print(pos: Position, msg: String, severity: Severity) {
- val prefix = (
- if (replProps.colorOk)
- severityColor(severity) + clabel(severity) + RESET
- else
- clabel(severity)
- )
- printMessage(pos, prefix + msg)
+ val adjusted =
+ if (pos.source.file.name == "<console>")
+ new OffsetPosition(pos.source, pos.offset.getOrElse(0)) {
+ override def lineContent = s"${indentation}${super.lineContent}"
+ override def lineCaret = s"${indentation}${super.lineCaret}"
+ }
+ else pos
+ super.print(adjusted, msg, severity)
}
override def printMessage(msg: String) {
@@ -63,12 +74,8 @@ class ReplReporter(intp: IMain) extends ConsoleReporter(intp.settings, Console.i
else Console.println("[init] " + msg)
}
- override def displayPrompt() {
- if (intp.totalSilence) ()
- else super.displayPrompt()
- }
+ override def displayPrompt() = if (!intp.totalSilence) super.displayPrompt()
override def rerunWithDetails(setting: reflect.internal.settings.MutableSettings#Setting, name: String) =
s"; for details, enable `:setting $name' or `:replay $name'"
-
}
diff --git a/test/files/jvm/interpreter.check b/test/files/jvm/interpreter.check
index 72d8d39fd0..6b712b93c7 100644
--- a/test/files/jvm/interpreter.check
+++ b/test/files/jvm/interpreter.check
@@ -278,13 +278,13 @@ scala> // both of the following should abort immediately:
scala> def x => y => z
<console>:1: error: '=' expected but '=>' found.
-def x => y => z
- ^
+ def x => y => z
+ ^
scala> [1,2,3]
<console>:1: error: illegal start of definition
-[1,2,3]
-^
+ [1,2,3]
+ ^
scala>
diff --git a/test/files/neg/badtok-1.check b/test/files/neg/badtok-1.check
index b05bc60161..5d5a8e1f35 100644
--- a/test/files/neg/badtok-1.check
+++ b/test/files/neg/badtok-1.check
@@ -4,4 +4,10 @@ badtok-1.scala:2: error: unclosed character literal
badtok-1.scala:2: error: unclosed character literal
'42'
^
-two errors found
+badtok-1.scala:6: error: empty character literal (use '\'' for single quote)
+'''
+^
+badtok-1.scala:9: error: unclosed character literal (or use " for string literal "abc")
+'abc'
+ ^
+four errors found
diff --git a/test/files/neg/badtok-1.scala b/test/files/neg/badtok-1.scala
index 706e794946..cc7dc6cecc 100644
--- a/test/files/neg/badtok-1.scala
+++ b/test/files/neg/badtok-1.scala
@@ -1,2 +1,9 @@
// bug 989
'42'
+
+
+// SI-10133
+'''
+
+// SI-10120
+'abc'
diff --git a/test/files/neg/t5856.check b/test/files/neg/t5856.check
index 08a61bdc07..306cc04177 100644
--- a/test/files/neg/t5856.check
+++ b/test/files/neg/t5856.check
@@ -1,4 +1,4 @@
-t5856.scala:10: error: invalid string interpolation: `$$', `$'ident or `$'BlockExpr expected
+t5856.scala:10: error: invalid string interpolation $", expected: $$, $identifier or ${expression}
val s9 = s"$"
^
t5856.scala:10: error: unclosed string literal
diff --git a/test/files/run/reify_newimpl_22.check b/test/files/run/reify_newimpl_22.check
index 985f646579..b2f4d5624e 100644
--- a/test/files/run/reify_newimpl_22.check
+++ b/test/files/run/reify_newimpl_22.check
@@ -15,7 +15,7 @@ scala> {
}
println(code.eval)
}
-<console>:19: free term: Ident(TermName("x")) defined by res0 in <console>:18:14
+<console>:19: free term: Ident(TermName("x")) defined by res0 in <console>:18:7
val code = reify {
^
2
diff --git a/test/files/run/reify_newimpl_23.check b/test/files/run/reify_newimpl_23.check
index f60113c69f..abf314b26a 100644
--- a/test/files/run/reify_newimpl_23.check
+++ b/test/files/run/reify_newimpl_23.check
@@ -14,7 +14,7 @@ scala> def foo[T]{
}
println(code.eval)
}
-<console>:17: free type: Ident(TypeName("T")) defined by foo in <console>:16:16
+<console>:17: free type: Ident(TypeName("T")) defined by foo in <console>:16:9
val code = reify {
^
foo: [T]=> Unit
diff --git a/test/files/run/reify_newimpl_25.check b/test/files/run/reify_newimpl_25.check
index 9104d8df0b..d446caa91a 100644
--- a/test/files/run/reify_newimpl_25.check
+++ b/test/files/run/reify_newimpl_25.check
@@ -5,7 +5,7 @@ scala> {
val tt = implicitly[TypeTag[x.type]]
println(tt)
}
-<console>:15: free term: Ident(TermName("x")) defined by res0 in <console>:14:14
+<console>:15: free term: Ident(TermName("x")) defined by res0 in <console>:14:7
val tt = implicitly[TypeTag[x.type]]
^
TypeTag[x.type]
diff --git a/test/files/run/reify_newimpl_26.check b/test/files/run/reify_newimpl_26.check
index cbb21854ba..099231bf62 100644
--- a/test/files/run/reify_newimpl_26.check
+++ b/test/files/run/reify_newimpl_26.check
@@ -4,7 +4,7 @@ scala> def foo[T]{
val tt = implicitly[WeakTypeTag[List[T]]]
println(tt)
}
-<console>:13: free type: Ident(TypeName("T")) defined by foo in <console>:11:16
+<console>:13: free type: Ident(TypeName("T")) defined by foo in <console>:11:9
val tt = implicitly[WeakTypeTag[List[T]]]
^
foo: [T]=> Unit
diff --git a/test/files/run/repl-colon-type.check b/test/files/run/repl-colon-type.check
index 5b7a3c7506..3fdd318df6 100644
--- a/test/files/run/repl-colon-type.check
+++ b/test/files/run/repl-colon-type.check
@@ -1,8 +1,8 @@
scala> :type List[1, 2, 3]
<console>:1: error: identifier expected but integer literal found.
-List[1, 2, 3]
- ^
+ List[1, 2, 3]
+ ^
scala> :type List(1, 2, 3)
List[Int]
@@ -38,8 +38,8 @@ scala> :type protected lazy val f = 5
Access to protected lazy value f not permitted because
enclosing object $eval in package $line13 is not a subclass of
object $iw where target is defined
- lazy val $result = f
- ^
+ lazy val $result = f
+ ^
scala> :type def f = 5
=> Int
diff --git a/test/files/run/t8918-unary-ids.check b/test/files/run/t8918-unary-ids.check
index 92f02371c7..b1f16ed56a 100644
--- a/test/files/run/t8918-unary-ids.check
+++ b/test/files/run/t8918-unary-ids.check
@@ -5,3 +5,17 @@ Expected 41 lines, got 39
-Type in expressions to have them evaluated.
-Type :help for more information.
+@@ -14,4 +12,4 @@
+ <console>:1: error: illegal start of simple expression
+-- if (true) 1 else 2
+- ^
++ - if (true) 1 else 2
++ ^
+
+@@ -19,4 +17,4 @@
+ <console>:1: error: ';' expected but integer literal found.
+-- - 1
+- ^
++ - - 1
++ ^
+
diff --git a/test/files/run/t9170.scala b/test/files/run/t9170.scala
index 87471fb129..cb7971235f 100644
--- a/test/files/run/t9170.scala
+++ b/test/files/run/t9170.scala
@@ -48,8 +48,8 @@ object Y {
def f[A](a: => A): Int at line 12 and
def f[A](a: => Either[Exception,A]): Int at line 13
have same type after erasure: (a: Function0)Int
- def f[A](a: => Either[Exception, A]) = 2
- ^
+ def f[A](a: => Either[Exception, A]) = 2
+ ^
scala> :quit"""
}
diff --git a/test/junit/scala/tools/nsc/interpreter/ScriptedTest.scala b/test/junit/scala/tools/nsc/interpreter/ScriptedTest.scala
index 01d17110d6..e9f1336623 100644
--- a/test/junit/scala/tools/nsc/interpreter/ScriptedTest.scala
+++ b/test/junit/scala/tools/nsc/interpreter/ScriptedTest.scala
@@ -96,7 +96,7 @@ class ScriptedTest {
}
@Test def `on compile error`(): Unit = {
val engine = scripted
- val err = "not found: value foo in def f = foo at line number 11 at column number 16"
+ val err = "not found: value foo in def f = foo at line number 11 at column number 9"
assertThrows[ScriptException](engine.compile("def f = foo"), _ == err)
}
}