@@ -22,7 +22,7 @@ import Constants._
2222import Symbols .defn
2323import ScriptParsers ._
2424import Decorators ._
25- import scala .internal .Chars . isIdentifierStart
25+ import scala .internal .Chars
2626import scala .annotation .{tailrec , switch }
2727import rewrites .Rewrites .patch
2828
@@ -351,6 +351,31 @@ object Parsers {
351351 accept(SEMI )
352352 }
353353
354+ def rewriteNotice (additionalOption : String = " " ) = {
355+ val optionStr = if (additionalOption.isEmpty) " " else " " ++ additionalOption
356+ i " \n This construct can be rewritten automatically under $optionStr -rewrite. "
357+ }
358+
359+ def syntaxVersionError (option : String , span : Span ) = {
360+ syntaxError(em """ This construct is not allowed under $option. ${rewriteNotice(option)}""" , span)
361+ }
362+
363+ def rewriteToNewSyntax (span : Span = Span (in.offset)): Boolean = {
364+ if (in.newSyntax) {
365+ if (in.rewrite) return true
366+ syntaxVersionError(" -new-syntax" , span)
367+ }
368+ false
369+ }
370+
371+ def rewriteToOldSyntax (span : Span = Span (in.offset)): Boolean = {
372+ if (in.oldSyntax) {
373+ if (in.rewrite) return true
374+ syntaxVersionError(" -old-syntax" , span)
375+ }
376+ false
377+ }
378+
354379 def errorTermTree : Literal = atSpan(in.offset) { Literal (Constant (null )) }
355380
356381 private [this ] var inFunReturnType = false
@@ -525,6 +550,131 @@ object Parsers {
525550
526551 def commaSeparated [T ](part : () => T ): List [T ] = tokenSeparated(COMMA , part)
527552
553+ def inSepRegion [T ](opening : Token , closing : Token )(op : => T ): T = {
554+ in.adjustSepRegions(opening)
555+ try op finally in.adjustSepRegions(closing)
556+ }
557+
558+ /* -------- REWRITES ----------------------------------------------------------- */
559+
560+ /** A list of pending patches, to be issued if we can rewrite all enclosing braces to
561+ * indentation regions.
562+ */
563+ var pendingPatches : List [() => Unit ] = Nil
564+
565+ def testChar (idx : Int , p : Char => Boolean ): Boolean = {
566+ val txt = source.content
567+ idx < txt.length && p(txt(idx))
568+ }
569+
570+ def testChar (idx : Int , c : Char ): Boolean = {
571+ val txt = source.content
572+ idx < txt.length && txt(idx) == c
573+ }
574+
575+ def testChars (from : Int , str : String ): Boolean =
576+ str.isEmpty ||
577+ testChar(from, str.head) && testChars(from + 1 , str.tail)
578+
579+ def skipBlanks (idx : Int , step : Int = 1 ): Int =
580+ if (testChar(idx, c => c == ' ' || c == '\t ' || c == Chars .CR )) skipBlanks(idx + step, step)
581+ else idx
582+
583+ def skipLineCommentsRightOf (idx : Int , column : Int ): Int = {
584+ val j = skipBlanks(idx)
585+ if (testChar(j, '/' ) && testChar(j + 1 , '/' ) && source.column(j) > column)
586+ skipLineCommentsRightOf(source.nextLine(j), column)
587+ else idx
588+ }
589+
590+ /** The region to eliminate when replacing a closing `)` or `}` that starts
591+ * a new line
592+ */
593+ def closingElimRegion (): (Offset , Offset ) = {
594+ val skipped = skipBlanks(in.lastOffset)
595+ if (testChar(skipped, Chars .LF )) // if `}` is on a line by itself
596+ (source.startOfLine(in.lastOffset), skipped + 1 ) // skip the whole line
597+ else // else
598+ (in.lastOffset - 1 , skipped) // move the following text up to where the `}` was
599+ }
600+
601+ /** Drop (...) or { ... }, replacing the closing element with `endStr` */
602+ def dropParensOrBraces (start : Offset , endStr : String ): Unit = {
603+ patch(source, Span (start, start + 1 ),
604+ if (testChar(start - 1 , Chars .isIdentifierPart)) " " else " " )
605+ val closingStartsLine = testChar(skipBlanks(in.lastOffset - 2 , - 1 ), Chars .LF )
606+ val preFill = if (closingStartsLine || endStr.isEmpty) " " else " "
607+ val postFill = if (in.lastOffset == in.offset) " " else " "
608+ val (startClosing, endClosing) =
609+ if (closingStartsLine && endStr.isEmpty) closingElimRegion()
610+ else (in.lastOffset - 1 , in.lastOffset)
611+ patch(source, Span (startClosing, endClosing), s " $preFill$endStr$postFill" )
612+ }
613+
614+ /** Drop current token, which is assumed to be `then` or `do`. */
615+ def dropTerminator (): Unit = {
616+ var startOffset = in.offset
617+ var endOffset = in.lastCharOffset
618+ if (in.isAfterLineEnd()) {
619+ if (testChar(endOffset, ' ' )) endOffset += 1
620+ }
621+ else {
622+ if (testChar(startOffset - 1 , ' ' )) startOffset -= 1
623+ }
624+ patch(source, Span (startOffset, endOffset), " " )
625+ }
626+
627+ /** rewrite code with (...) around the source code of `t` */
628+ def revertToParens (t : Tree ): Unit =
629+ if (t.span.exists) {
630+ patch(source, t.span.startPos, " (" )
631+ patch(source, t.span.endPos, " )" )
632+ dropTerminator()
633+ }
634+
635+ /** In the tokens following the current one, does `query` precede any of the tokens that
636+ * - must start a statement, or
637+ * - separate two statements, or
638+ * - continue a statement (e.g. `else`, catch`)?
639+ */
640+ def followedByToken (query : Token ): Boolean = {
641+ val lookahead = in.lookaheadScanner
642+ var braces = 0
643+ while (true ) {
644+ val token = lookahead.token
645+ if (braces == 0 ) {
646+ if (token == query) return true
647+ if (stopScanTokens.contains(token) || lookahead.token == RBRACE ) return false
648+ }
649+ else if (token == EOF )
650+ return false
651+ else if (lookahead.token == RBRACE )
652+ braces -= 1
653+ if (lookahead.token == LBRACE ) braces += 1
654+ lookahead.nextToken()
655+ }
656+ false
657+ }
658+
659+ /** A the generators of a for-expression enclosed in (...)? */
660+ def parensEncloseGenerators : Boolean = {
661+ val lookahead = in.lookaheadScanner
662+ var parens = 1
663+ lookahead.nextToken()
664+ while (parens != 0 && lookahead.token != EOF ) {
665+ val token = lookahead.token
666+ if (token == LPAREN ) parens += 1
667+ else if (token == RPAREN ) parens -= 1
668+ lookahead.nextToken()
669+ }
670+ if (lookahead.token == LARROW )
671+ false // it's a pattern
672+ else if (lookahead.token != IDENTIFIER && lookahead.token != BACKQUOTED_IDENT )
673+ true // it's not a pattern since token cannot be an infix operator
674+ else
675+ followedByToken(LARROW ) // `<-` comes before possible statement starts
676+ }
677+
528678/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
529679
530680 var opStack : List [OpInfo ] = Nil
@@ -758,7 +908,7 @@ object Parsers {
758908 }
759909 else atSpan(negOffset) {
760910 if (in.token == QUOTEID ) {
761- if ((staged & StageKind .Spliced ) != 0 && isIdentifierStart(in.name(0 ))) {
911+ if ((staged & StageKind .Spliced ) != 0 && Chars . isIdentifierStart(in.name(0 ))) {
762912 val t = atSpan(in.offset + 1 ) {
763913 val tok = in.toToken(in.name)
764914 tok match {
@@ -844,7 +994,7 @@ object Parsers {
844994
845995 def newLineOptWhenFollowedBy (token : Int ): Unit = {
846996 // note: next is defined here because current == NEWLINE
847- if (in.token == NEWLINE && in.next.token == token) newLineOpt ()
997+ if (in.token == NEWLINE && in.next.token == token) in.nextToken ()
848998 }
849999
8501000 def newLineOptWhenFollowing (p : Int => Boolean ): Unit = {
@@ -1235,11 +1385,22 @@ object Parsers {
12351385
12361386 def condExpr (altToken : Token ): Tree = {
12371387 if (in.token == LPAREN ) {
1238- val t = atSpan(in.offset) { Parens (inParens(exprInParens())) }
1239- if (in.token == altToken) in.nextToken()
1388+ var t : Tree = atSpan(in.offset) { Parens (inParens(exprInParens())) }
1389+ if (in.token != altToken && followedByToken(altToken))
1390+ t = inSepRegion(LPAREN , RPAREN ) {
1391+ newLineOpt()
1392+ expr1Rest(postfixExprRest(simpleExprRest(t)), Location .ElseWhere )
1393+ }
1394+ if (in.token == altToken) {
1395+ if (rewriteToOldSyntax()) revertToParens(t)
1396+ in.nextToken()
1397+ }
1398+ else if (rewriteToNewSyntax(t.span))
1399+ dropParensOrBraces(t.span.start, s " ${tokenString(altToken)}" )
12401400 t
12411401 } else {
1242- val t = expr()
1402+ val t = inSepRegion(LPAREN , RPAREN )(expr())
1403+ if (rewriteToOldSyntax(t.span.startPos)) revertToParens(t)
12431404 accept(altToken)
12441405 t
12451406 }
@@ -1333,7 +1494,7 @@ object Parsers {
13331494 in.errorOrMigrationWarning(
13341495 i """ `do <body> while <cond>' is no longer supported,
13351496 |use `while ({<body> ; <cond>}) ()' instead.
1336- |The statement can be rewritten automatically under -language:Scala2 -migration -rewrite.
1497+ | ${rewriteNotice( " -language:Scala2" )}
13371498 """ )
13381499 val start = in.skipToken()
13391500 atSpan(start) {
@@ -1342,7 +1503,7 @@ object Parsers {
13421503 val whileStart = in.offset
13431504 accept(WHILE )
13441505 val cond = expr()
1345- if (ctx.settings.migration.value ) {
1506+ if (in.isScala2Mode ) {
13461507 patch(source, Span (start, start + 2 ), " while ({" )
13471508 patch(source, Span (whileStart, whileStart + 5 ), " ;" )
13481509 cond match {
@@ -1576,8 +1737,10 @@ object Parsers {
15761737 * | InfixExpr id [nl] InfixExpr
15771738 * | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
15781739 */
1579- def postfixExpr (): Tree =
1580- infixOps(prefixExpr(), canStartExpressionTokens, prefixExpr, maybePostfix = true )
1740+ def postfixExpr (): Tree = postfixExprRest(prefixExpr())
1741+
1742+ def postfixExprRest (t : Tree ): Tree =
1743+ infixOps(t, canStartExpressionTokens, prefixExpr, maybePostfix = true )
15811744
15821745 /** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr
15831746 */
@@ -1799,8 +1962,13 @@ object Parsers {
17991962 def enumerators (): List [Tree ] = generator() :: enumeratorsRest()
18001963
18011964 def enumeratorsRest (): List [Tree ] =
1802- if (isStatSep) { in.nextToken(); enumerator() :: enumeratorsRest() }
1803- else if (in.token == IF ) guard() :: enumeratorsRest()
1965+ if (isStatSep) {
1966+ in.nextToken()
1967+ if (in.token == DO || in.token == YIELD || in.token == RBRACE ) Nil
1968+ else enumerator() :: enumeratorsRest()
1969+ }
1970+ else if (in.token == IF )
1971+ guard() :: enumeratorsRest()
18041972 else Nil
18051973
18061974 /** Enumerator ::= Generator
@@ -1838,37 +2006,72 @@ object Parsers {
18382006 */
18392007 def forExpr (): Tree = atSpan(in.skipToken()) {
18402008 var wrappedEnums = true
2009+ val start = in.offset
2010+ val forEnd = in.lastOffset
2011+ val leading = in.token
18412012 val enums =
1842- if (in.token == LBRACE ) inBraces(enumerators())
1843- else if (in.token == LPAREN ) {
1844- val lparenOffset = in.skipToken()
1845- openParens.change(LPAREN , 1 )
2013+ if (leading == LBRACE || leading == LPAREN && parensEncloseGenerators) {
2014+ in.nextToken()
2015+ openParens.change(leading, 1 )
18462016 val res =
1847- if (in.token == CASE ) enumerators()
2017+ if (leading == LBRACE || in.token == CASE )
2018+ enumerators()
18482019 else {
18492020 val pats = patternsOpt()
18502021 val pat =
18512022 if (in.token == RPAREN || pats.length > 1 ) {
18522023 wrappedEnums = false
18532024 accept(RPAREN )
18542025 openParens.change(LPAREN , - 1 )
1855- atSpan(lparenOffset ) { makeTupleOrParens(pats) } // note: alternatives `|' need to be weeded out by typer.
2026+ atSpan(start ) { makeTupleOrParens(pats) } // note: alternatives `|' need to be weeded out by typer.
18562027 }
18572028 else pats.head
18582029 generatorRest(pat, casePat = false ) :: enumeratorsRest()
18592030 }
18602031 if (wrappedEnums) {
1861- accept(RPAREN )
1862- openParens.change(LPAREN , - 1 )
2032+ val closingOnNewLine = in.isAfterLineEnd()
2033+ accept(leading + 1 )
2034+ openParens.change(leading, - 1 )
2035+ def hasMultiLineEnum =
2036+ res.exists { t =>
2037+ val pos = t.sourcePos
2038+ pos.startLine < pos.endLine
2039+ }
2040+ if (rewriteToNewSyntax(Span (start)) && (leading == LBRACE || ! hasMultiLineEnum)) {
2041+ // Don't rewrite if that could change meaning of newlines
2042+ newLinesOpt()
2043+ dropParensOrBraces(start, if (in.token == YIELD || in.token == DO ) " " else " do" )
2044+ }
18632045 }
18642046 res
1865- } else {
2047+ }
2048+ else {
18662049 wrappedEnums = false
1867- enumerators()
2050+
2051+ /* if (in.token == INDENT) inBracesOrIndented(enumerators()) else*/
2052+ val ts = inSepRegion(LBRACE , RBRACE )(enumerators())
2053+ if (rewriteToOldSyntax(Span (start)) && ts.nonEmpty) {
2054+ if (ts.length > 1 && ts.head.sourcePos.startLine != ts.last.sourcePos.startLine) {
2055+ patch(source, Span (forEnd), " {" )
2056+ patch(source, Span (in.offset), " } " )
2057+ }
2058+ else {
2059+ patch(source, ts.head.span.startPos, " (" )
2060+ patch(source, ts.last.span.endPos, " )" )
2061+ }
2062+ }
2063+ ts
18682064 }
18692065 newLinesOpt()
1870- if (in.token == YIELD ) { in.nextToken(); ForYield (enums, expr()) }
1871- else if (in.token == DO ) { in.nextToken(); ForDo (enums, expr()) }
2066+ if (in.token == YIELD ) {
2067+ in.nextToken()
2068+ ForYield (enums, expr())
2069+ }
2070+ else if (in.token == DO ) {
2071+ if (rewriteToOldSyntax()) dropTerminator()
2072+ in.nextToken()
2073+ ForDo (enums, expr())
2074+ }
18722075 else {
18732076 if (! wrappedEnums) syntaxErrorOrIncomplete(YieldOrDoExpectedInForComprehension ())
18742077 ForDo (enums, expr())
@@ -2675,7 +2878,7 @@ object Parsers {
26752878 }
26762879
26772880 /** ConstrExpr ::= SelfInvocation
2678- * | ConstrBlock
2881+ * | `{' SelfInvocation {semi BlockStat} `}'
26792882 */
26802883 def constrExpr (): Tree =
26812884 if (in.token == LBRACE ) constrBlock()
0 commit comments