Skip to content

Commit

Permalink
Merge pull request #8282 from som-snytt/forward/charlit
Browse files Browse the repository at this point in the history
Reject char literal of raw newline
  • Loading branch information
abgruszecki committed Apr 7, 2020
2 parents 26032db + dff31fb commit 33a6f9e
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 15 deletions.
16 changes: 11 additions & 5 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Expand Up @@ -812,19 +812,25 @@ object Scanners {
}
fetchDoubleQuote()
case '\'' =>
def fetchSingleQuote() = {
def fetchSingleQuote(): Unit = {
nextChar()
if (isIdentifierStart(ch))
if isIdentifierStart(ch) then
charLitOr { getIdentRest(); QUOTEID }
else if (isOperatorPart(ch) && (ch != '\\'))
else if isOperatorPart(ch) && ch != '\\' then
charLitOr { getOperatorRest(); QUOTEID }
else ch match {
case '{' | '[' | ' ' | '\t' if lookaheadChar() != '\'' =>
token = QUOTE
case _ =>
case _ if !isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape) =>
val isEmptyCharLit = (ch == '\'')
getLitChar()
if (ch == '\'') finishCharLit()
if ch == '\'' then
if isEmptyCharLit then error("empty character literal (use '\\'' for single quote)")
else finishCharLit()
else if isEmptyCharLit then error("empty character literal")
else error("unclosed character literal")
case _ =>
error("unclosed character literal")
}
}
fetchSingleQuote()
Expand Down
33 changes: 23 additions & 10 deletions compiler/test/dotty/tools/vulpix/ParallelTesting.scala
Expand Up @@ -109,9 +109,9 @@ trait ParallelTesting extends RunnerOrchestration { self =>
case source: JointCompilationSource => {
source.sourceFiles.map(_.getPath).foreach { path =>
sb.append(delimiter)
sb += '''
sb += '\''
sb.append(path)
sb += '''
sb += '\''
sb += ' '
}
sb.toString + "\n\n"
Expand All @@ -123,9 +123,9 @@ trait ParallelTesting extends RunnerOrchestration { self =>
files.map(_.getPath).foreach { path =>
fsb.append(delimiter)
lineLen = 8
fsb += '''
fsb += '\''
fsb.append(path)
fsb += '''
fsb += '\''
fsb += ' '
}
fsb.append("\n\n")
Expand Down Expand Up @@ -666,13 +666,20 @@ trait ParallelTesting extends RunnerOrchestration { self =>
errorMap.put("nopos", noposErrors + existing)
}

val possibleTypos = List("//error" -> "// error", "//nopos-error" -> "// nopos-error")
val anyposErrors = line.toSeq.sliding("// anypos-error".length).count(_.unwrap == "// anypos-error")
if (anyposErrors > 0) {
val anypos = errorMap.get("anypos")
val existing: Integer = if (anypos eq null) 0 else anypos
errorMap.put("anypos", anyposErrors + existing)
}

val possibleTypos = List("//error" -> "// error", "//nopos-error" -> "// nopos-error", "//anypos-error" -> "// anypos-error")
for ((possibleTypo, expected) <- possibleTypos) {
if (line.contains(possibleTypo))
echo(s"Warning: Possible typo in error tag in file ${file.getCanonicalPath}:$lineNbr: found `$possibleTypo` but expected `$expected`")
}

expectedErrors += noposErrors + errors
expectedErrors += anyposErrors + noposErrors + errors
}
}

Expand All @@ -691,15 +698,21 @@ trait ParallelTesting extends RunnerOrchestration { self =>

val errors = errorMap.get(key)

def missing = { echo(s"Error reported in ${pos1.source}, but no annotation found") ; false }

if (errors ne null) {
if (errors == 1) errorMap.remove(key)
else errorMap.put(key, errors - 1)
true
}
else {
echo(s"Error reported in ${pos1.source}, but no annotation found")
false
}
else if key == "nopos" then
missing
else
errorMap.get("anypos") match
case null => missing
case 1 => errorMap.remove("anypos") ; true
case slack => if slack < 1 then missing
else errorMap.put("anypos", slack - 1) ; true
}
}

Expand Down
3 changes: 3 additions & 0 deletions compiler/test/dotty/tools/vulpix/VulpixUnitTests.scala
Expand Up @@ -49,6 +49,9 @@ class VulpixUnitTests extends ParallelTesting {
@Test def negNoPositionAnnot: Unit =
compileFile("tests/vulpix-tests/unit/negNoPositionAnnots.scala", defaultOptions).expectFailure.checkExpectedErrors()

@Test def negAnyPositionAnnot: Unit =
compileFile("tests/vulpix-tests/unit/negAnyPositionAnnots.scala", defaultOptions).checkExpectedErrors()

@Test def runCompileFail: Unit =
compileFile("tests/vulpix-tests/unit/posFail1Error.scala", defaultOptions).expectFailure.checkRuns()

Expand Down
24 changes: 24 additions & 0 deletions tests/neg/t6810.check
@@ -0,0 +1,24 @@
-- Error: tests/neg/t6810.scala:5:10 -----------------------------------------------------------------------------------
5 | val y = '
| ^
| unclosed character literal
-- Error: tests/neg/t6810.scala:12:10 ----------------------------------------------------------------------------------
12 | val Y = "
| ^
| unclosed string literal
-- Error: tests/neg/t6810.scala:13:0 -----------------------------------------------------------------------------------
13 |" // error obviously not
|^
|unclosed string literal
-- Error: tests/neg/t6810.scala:24:6 -----------------------------------------------------------------------------------
24 | val `
| ^
| unclosed quoted identifier
-- Error: tests/neg/t6810.scala:25:0 -----------------------------------------------------------------------------------
25 |` = EOL // error not raw string literals aka triple-quoted, multiline strings
|^
|unclosed quoted identifier
-- Error: tests/neg/t6810.scala:30:10 ----------------------------------------------------------------------------------
30 | val b = '
| ^
| unclosed character literal
33 changes: 33 additions & 0 deletions tests/neg/t6810.scala
@@ -0,0 +1,33 @@

trait t6810 {
val x = '\u000A' // char literals accept arbitrary unicode escapes
// anypos-error so as not to interfere with the following bad syntax
val y = '
' // but not embedded EOL sequences not represented as escapes
println(); // scanner firewall
val z = '\n' // normally, expect this escape

val X = "\u000A" // it's the same as ordinary string literals
// anypos-error so as not to interfere with the following bad syntax
val Y = "
" // error obviously not
val Z = "\n" // normally, expect this escape

val A = """
""" // which is what these are for
val B = s"""
""" // or the same for interpolated strings

import System.{lineSeparator => EOL}
val `\u000A` = EOL // backquoted identifiers are arbitrary string literals
// anypos-error so as not to interfere with the following bad syntax
val `
` = EOL // error not raw string literals aka triple-quoted, multiline strings

val firebreak = 42 // help parser recovery, could also use rbrace

val a = '\u000D' // similar treatment of CR
val b = '' // anypos-error CR seen as EOL by scanner; FSR, error only on open quote, unlike `y`
println(); // scanner firewall
val c = '\r' // traditionally
}
Expand Down
5 changes: 5 additions & 0 deletions tests/vulpix-tests/unit/negAnyPositionAnnots.scala
@@ -0,0 +1,5 @@
object Foo {
def bar: Int = "LOL"

// anypos-error
}

0 comments on commit 33a6f9e

Please sign in to comment.