@@ -22,7 +22,7 @@ import Constants._
22
22
import Symbols .defn
23
23
import ScriptParsers ._
24
24
import Decorators ._
25
- import scala .internal .Chars . isIdentifierStart
25
+ import scala .internal .Chars
26
26
import scala .annotation .{tailrec , switch }
27
27
import rewrites .Rewrites .patch
28
28
@@ -351,6 +351,31 @@ object Parsers {
351
351
accept(SEMI )
352
352
}
353
353
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
+
354
379
def errorTermTree : Literal = atSpan(in.offset) { Literal (Constant (null )) }
355
380
356
381
private [this ] var inFunReturnType = false
@@ -525,6 +550,131 @@ object Parsers {
525
550
526
551
def commaSeparated [T ](part : () => T ): List [T ] = tokenSeparated(COMMA , part)
527
552
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
+
528
678
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
529
679
530
680
var opStack : List [OpInfo ] = Nil
@@ -758,7 +908,7 @@ object Parsers {
758
908
}
759
909
else atSpan(negOffset) {
760
910
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 ))) {
762
912
val t = atSpan(in.offset + 1 ) {
763
913
val tok = in.toToken(in.name)
764
914
tok match {
@@ -844,7 +994,7 @@ object Parsers {
844
994
845
995
def newLineOptWhenFollowedBy (token : Int ): Unit = {
846
996
// 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 ()
848
998
}
849
999
850
1000
def newLineOptWhenFollowing (p : Int => Boolean ): Unit = {
@@ -1235,11 +1385,22 @@ object Parsers {
1235
1385
1236
1386
def condExpr (altToken : Token ): Tree = {
1237
1387
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)}" )
1240
1400
t
1241
1401
} else {
1242
- val t = expr()
1402
+ val t = inSepRegion(LPAREN , RPAREN )(expr())
1403
+ if (rewriteToOldSyntax(t.span.startPos)) revertToParens(t)
1243
1404
accept(altToken)
1244
1405
t
1245
1406
}
@@ -1333,7 +1494,7 @@ object Parsers {
1333
1494
in.errorOrMigrationWarning(
1334
1495
i """ `do <body> while <cond>' is no longer supported,
1335
1496
|use `while ({<body> ; <cond>}) ()' instead.
1336
- |The statement can be rewritten automatically under -language:Scala2 -migration -rewrite.
1497
+ | ${rewriteNotice( " -language:Scala2" )}
1337
1498
""" )
1338
1499
val start = in.skipToken()
1339
1500
atSpan(start) {
@@ -1342,7 +1503,7 @@ object Parsers {
1342
1503
val whileStart = in.offset
1343
1504
accept(WHILE )
1344
1505
val cond = expr()
1345
- if (ctx.settings.migration.value ) {
1506
+ if (in.isScala2Mode ) {
1346
1507
patch(source, Span (start, start + 2 ), " while ({" )
1347
1508
patch(source, Span (whileStart, whileStart + 5 ), " ;" )
1348
1509
cond match {
@@ -1576,8 +1737,10 @@ object Parsers {
1576
1737
* | InfixExpr id [nl] InfixExpr
1577
1738
* | InfixExpr ‘given’ (InfixExpr | ParArgumentExprs)
1578
1739
*/
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 )
1581
1744
1582
1745
/** PrefixExpr ::= [`-' | `+' | `~' | `!'] SimpleExpr
1583
1746
*/
@@ -1799,8 +1962,13 @@ object Parsers {
1799
1962
def enumerators (): List [Tree ] = generator() :: enumeratorsRest()
1800
1963
1801
1964
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()
1804
1972
else Nil
1805
1973
1806
1974
/** Enumerator ::= Generator
@@ -1838,37 +2006,72 @@ object Parsers {
1838
2006
*/
1839
2007
def forExpr (): Tree = atSpan(in.skipToken()) {
1840
2008
var wrappedEnums = true
2009
+ val start = in.offset
2010
+ val forEnd = in.lastOffset
2011
+ val leading = in.token
1841
2012
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 )
1846
2016
val res =
1847
- if (in.token == CASE ) enumerators()
2017
+ if (leading == LBRACE || in.token == CASE )
2018
+ enumerators()
1848
2019
else {
1849
2020
val pats = patternsOpt()
1850
2021
val pat =
1851
2022
if (in.token == RPAREN || pats.length > 1 ) {
1852
2023
wrappedEnums = false
1853
2024
accept(RPAREN )
1854
2025
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.
1856
2027
}
1857
2028
else pats.head
1858
2029
generatorRest(pat, casePat = false ) :: enumeratorsRest()
1859
2030
}
1860
2031
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
+ }
1863
2045
}
1864
2046
res
1865
- } else {
2047
+ }
2048
+ else {
1866
2049
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
1868
2064
}
1869
2065
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
+ }
1872
2075
else {
1873
2076
if (! wrappedEnums) syntaxErrorOrIncomplete(YieldOrDoExpectedInForComprehension ())
1874
2077
ForDo (enums, expr())
@@ -2675,7 +2878,7 @@ object Parsers {
2675
2878
}
2676
2879
2677
2880
/** ConstrExpr ::= SelfInvocation
2678
- * | ConstrBlock
2881
+ * | `{' SelfInvocation {semi BlockStat} `}'
2679
2882
*/
2680
2883
def constrExpr (): Tree =
2681
2884
if (in.token == LBRACE ) constrBlock()
0 commit comments