diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/ast/parser/Scanners.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/ast/parser/Scanners.scala | 133 |
1 files changed, 96 insertions, 37 deletions
diff --git a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala index cd41c75298..0618f5d06e 100644 --- a/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala +++ b/src/compiler/scala/tools/nsc/ast/parser/Scanners.scala @@ -10,7 +10,7 @@ import scala.reflect.internal.util._ import scala.reflect.internal.Chars._ import Tokens._ import scala.annotation.{ switch, tailrec } -import scala.collection.{ mutable, immutable } +import scala.collection.mutable import mutable.{ ListBuffer, ArrayBuffer } import scala.tools.nsc.ast.parser.xml.Utility.isNameStart import scala.language.postfixOps @@ -35,7 +35,28 @@ trait ScannersCommon { // things to fill in, in addition to buf, decodeUni which come from CharArrayReader def error(off: Offset, msg: String): Unit def incompleteInputError(off: Offset, msg: String): Unit - def deprecationWarning(off: Offset, msg: String): Unit + def deprecationWarning(off: Offset, msg: String, since: String): Unit + } + + // Hooks for ScaladocUnitScanner and ScaladocJavaUnitScanner + trait DocScanner { + protected def beginDocComment(prefix: String): Unit = {} + protected def processCommentChar(): Unit = {} + protected def finishDocComment(): Unit = {} + + private var lastDoc: DocComment = null + // get last doc comment + def flushDoc(): DocComment = try lastDoc finally lastDoc = null + def registerDocComment(raw: String, pos: Position) = { + lastDoc = DocComment(raw, pos) + signalParsedDocComment(raw, pos) + } + + /** To prevent doc comments attached to expressions from leaking out of scope + * onto the next documentable entity, they are discarded upon passing a right + * brace, bracket, or parenthesis. + */ + def discardDocBuffer(): Unit = {} } def createKeywordArray(keywords: Seq[(Name, Token)], defaultToken: Token): (Token, Array[Token]) = { @@ -103,11 +124,11 @@ trait Scanners extends ScannersCommon { } } - abstract class Scanner extends CharArrayReader with TokenData with ScannerData with ScannerCommon { + abstract class Scanner extends CharArrayReader with TokenData with ScannerData with ScannerCommon with DocScanner { private def isDigit(c: Char) = java.lang.Character isDigit c private var openComments = 0 - protected def putCommentChar(): Unit = nextChar() + final protected def putCommentChar(): Unit = { processCommentChar(); nextChar() } @tailrec private def skipLineComment(): Unit = ch match { case SU | CR | LF => @@ -134,8 +155,6 @@ trait Scanners extends ScannersCommon { case SU => incompleteInputError("unclosed comment") case _ => putCommentChar() ; skipNestedComments() } - def skipDocComment(): Unit = skipNestedComments() - def skipBlockComment(): Unit = skipNestedComments() private def skipToCommentEnd(isLineComment: Boolean): Unit = { nextChar() @@ -147,27 +166,23 @@ trait Scanners extends ScannersCommon { // Check for the amazing corner case of /**/ if (ch == '/') nextChar() - else - skipDocComment() + else { + beginDocComment("/**") + skipNestedComments() + } } - else skipBlockComment() + else skipNestedComments() } } /** @pre ch == '/' * Returns true if a comment was skipped. */ - def skipComment(): Boolean = ch match { - case '/' | '*' => skipToCommentEnd(isLineComment = ch == '/') ; true + final def skipComment(): Boolean = ch match { + case '/' | '*' => skipToCommentEnd(isLineComment = ch == '/') ; finishDocComment(); true case _ => false } - def flushDoc(): DocComment = null - /** To prevent doc comments attached to expressions from leaking out of scope - * onto the next documentable entity, they are discarded upon passing a right - * brace, bracket, or parenthesis. - */ - def discardDocBuffer(): Unit = () def isAtEnd = charOffset >= buf.length @@ -208,7 +223,7 @@ trait Scanners extends ScannersCommon { if (name == nme.MACROkw) syntaxError(s"$name is now a reserved word; usage as an identifier is disallowed") else if (emitIdentifierDeprecationWarnings) - deprecationWarning(s"$name is now a reserved word; usage as an identifier is deprecated") + deprecationWarning(s"$name is a reserved word (since 2.10.0); usage as an identifier is deprecated", "2.10.0") } } } @@ -246,6 +261,14 @@ trait Scanners extends ScannersCommon { private def inMultiLineInterpolation = inStringInterpolation && sepRegions.tail.nonEmpty && sepRegions.tail.head == STRINGPART + /** Are we in a `${ }` block? such that RBRACE exits back into multiline string. */ + private def inMultiLineInterpolatedExpression = { + sepRegions match { + case RBRACE :: STRINGLIT :: STRINGPART :: rest => true + case _ => false + } + } + /** read next token and return last offset */ def skipToken(): Offset = { @@ -312,7 +335,7 @@ trait Scanners extends ScannersCommon { lastOffset -= 1 } if (inStringInterpolation) fetchStringPart() else fetchToken() - if(token == ERROR) { + if (token == ERROR) { if (inMultiLineInterpolation) sepRegions = sepRegions.tail.tail else if (inStringInterpolation) @@ -363,6 +386,17 @@ trait Scanners extends ScannersCommon { next copyFrom this this copyFrom prev } + } else if (token == COMMA) { + // SIP-27 Trailing Comma (multi-line only) support + // If a comma is followed by a new line & then a closing paren, bracket or brace + // then it is a trailing comma and is ignored + val saved = new ScannerData {} copyFrom this + fetchToken() + if (afterLineEnd() && (token == RPAREN || token == RBRACKET || token == RBRACE)) { + /* skip the trailing comma */ + } else if (token == EOF) { // e.g. when the REPL is parsing "val List(x, y, _*," + /* skip the trailing comma */ + } else this copyFrom saved } // print("["+this+"]") @@ -515,7 +549,7 @@ trait Scanners extends ScannersCommon { charLitOr(getIdentRest) else if (isOperatorPart(ch) && (ch != '\\')) charLitOr(getOperatorRest) - else { + else if (!isAtEnd && (ch != SU && ch != CR && ch != LF || isUnicodeEscape)) { getLitChar() if (ch == '\'') { nextChar() @@ -525,6 +559,8 @@ trait Scanners extends ScannersCommon { syntaxError("unclosed character literal") } } + else + syntaxError("unclosed character literal") } fetchSingleQuote() case '.' => @@ -545,7 +581,8 @@ trait Scanners extends ScannersCommon { case ')' => nextChar(); token = RPAREN case '}' => - nextChar(); token = RBRACE + if (inMultiLineInterpolatedExpression) nextRawChar() else nextChar() + token = RBRACE case '[' => nextChar(); token = LBRACKET case ']' => @@ -690,7 +727,7 @@ trait Scanners extends ScannersCommon { private def unclosedStringLit(): Unit = syntaxError("unclosed string literal") - private def getRawStringLit(): Unit = { + @tailrec private def getRawStringLit(): Unit = { if (ch == '\"') { nextRawChar() if (isTripleQuote()) { @@ -707,7 +744,7 @@ trait Scanners extends ScannersCommon { } } - @scala.annotation.tailrec private def getStringPart(multiLine: Boolean): Unit = { + @tailrec private def getStringPart(multiLine: Boolean): Unit = { def finishStringPart() = { setStrVal() token = STRINGPART @@ -822,7 +859,7 @@ trait Scanners extends ScannersCommon { if (settings.future) syntaxError(start, msg("unsupported")) else - deprecationWarning(start, msg("deprecated")) + deprecationWarning(start, msg("deprecated"), "2.11.0") putChar(oct.toChar) } else { ch match { @@ -946,23 +983,45 @@ trait Scanners extends ScannersCommon { def intVal: Long = intVal(negated = false) - /** Convert current strVal, base to double value + private val zeroFloat = raw"[0.]+(?:[eE][+-]?[0-9]+)?[fFdD]?".r + + /** Convert current strVal, base to float value. */ - def floatVal(negated: Boolean): Double = { - val limit: Double = if (token == DOUBLELIT) Double.MaxValue else Float.MaxValue + def floatVal(negated: Boolean): Float = { try { - val value: Double = java.lang.Double.valueOf(strVal).doubleValue() - if (value > limit) + val value: Float = java.lang.Float.parseFloat(strVal) + if (value > Float.MaxValue) syntaxError("floating point number too large") + if (value == 0.0f && !zeroFloat.pattern.matcher(strVal).matches) + syntaxError("floating point number too small") if (negated) -value else value } catch { case _: NumberFormatException => syntaxError("malformed floating point number") + 0.0f + } + } + + def floatVal: Float = floatVal(negated = false) + + /** Convert current strVal, base to double value. + */ + def doubleVal(negated: Boolean): Double = { + try { + val value: Double = java.lang.Double.parseDouble(strVal) + if (value > Double.MaxValue) + syntaxError("double precision floating point number too large") + if (value == 0.0d && !zeroFloat.pattern.matcher(strVal).matches) + syntaxError("double precision floating point number too small") + if (negated) -value else value + } catch { + case _: NumberFormatException => + syntaxError("malformed double precision floating point number") 0.0 } } - def floatVal: Double = floatVal(negated = false) + def doubleVal: Double = doubleVal(negated = false) def checkNoLetter(): Unit = { if (isIdentifierPart(ch) && ch >= ' ') @@ -1032,7 +1091,7 @@ trait Scanners extends ScannersCommon { /** generate an error at the current token offset */ def syntaxError(msg: String): Unit = syntaxError(offset, msg) - def deprecationWarning(msg: String): Unit = deprecationWarning(offset, msg) + def deprecationWarning(msg: String, since: String): Unit = deprecationWarning(offset, msg, since) /** signal an error where the input ended in the middle of a token */ def incompleteInputError(msg: String): Unit = { @@ -1195,15 +1254,15 @@ trait Scanners extends ScannersCommon { class MalformedInput(val offset: Offset, val msg: String) extends Exception /** A scanner for a given source file not necessarily attached to a compilation unit. - * Useful for looking inside source files that aren not currently compiled to see what's there + * Useful for looking inside source files that are not currently compiled to see what's there */ class SourceFileScanner(val source: SourceFile) extends Scanner { val buf = source.content override val decodeUni: Boolean = !settings.nouescape // suppress warnings, throw exception on errors - def deprecationWarning(off: Offset, msg: String): Unit = () - def error (off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) + def deprecationWarning(off: Offset, msg: String, since: String): Unit = () + def error(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) def incompleteInputError(off: Offset, msg: String): Unit = throw new MalformedInput(off, msg) } @@ -1212,9 +1271,9 @@ trait Scanners extends ScannersCommon { class UnitScanner(val unit: CompilationUnit, patches: List[BracePatch]) extends SourceFileScanner(unit.source) { def this(unit: CompilationUnit) = this(unit, List()) - override def deprecationWarning(off: Offset, msg: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg) - override def error (off: Offset, msg: String) = reporter.error(unit.position(off), msg) - override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg) + override def deprecationWarning(off: Offset, msg: String, since: String) = currentRun.reporting.deprecationWarning(unit.position(off), msg, since) + override def error(off: Offset, msg: String) = reporter.error(unit.position(off), msg) + override def incompleteInputError(off: Offset, msg: String) = currentRun.parsing.incompleteInputError(unit.position(off), msg) private var bracePatches: List[BracePatch] = patches |