From 4ba89e9e8eb027b2bd11503e3621fa5e62e7bad0 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sat, 15 Feb 2025 14:32:39 -0800 Subject: [PATCH 1/3] Allow observing an indent after conditional Normally do not infer NEWLINE within parens, but special case old syntax for conditionals, so that it can observe indented syntax. The mechanism is to inject an Indented region when parsing a parenthesized condition which is within an InParens region, such as an arg list. The effect is not to advance past EOL after `(true)`. --- .../dotty/tools/dotc/parsing/Parsers.scala | 9 +++- tests/pos/i22608.scala | 48 +++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/pos/i22608.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index ef07d477c303..50034fa6f5d6 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -105,6 +105,9 @@ object Parsers { private val InCase: Region => Region = Scanners.InCase(_) private val InCond: Region => Region = Scanners.InParens(LPAREN, _) private val InFor : Region => Region = Scanners.InBraces(_) + private val InBrk : Region => Region = _.match + case p: Scanners.InParens => Scanners.Indented(p.indentWidth, p.prefix, p) + case r => r def unimplementedExpr(using Context): Select = Select(scalaDot(nme.Predef), nme.???) @@ -2325,8 +2328,10 @@ object Parsers { def condExpr(altToken: Token): Tree = val t: Tree = if in.token == LPAREN then - var t: Tree = atSpan(in.offset): - makeTupleOrParens(inParensWithCommas(commaSeparated(exprInParens))) + var t: Tree = + inSepRegion(InBrk): // allow inferred NEWLINE for observeIndented below + atSpan(in.offset): + makeTupleOrParens(inParensWithCommas(commaSeparated(exprInParens))) if in.token != altToken then if toBeContinued(altToken) then t = inSepRegion(InCond) { diff --git a/tests/pos/i22608.scala b/tests/pos/i22608.scala new file mode 100644 index 000000000000..e4b49e87769f --- /dev/null +++ b/tests/pos/i22608.scala @@ -0,0 +1,48 @@ + +def f(i: Int) = i +def g(i: Int, j: Int) = i+j + +def t = + val y = f( + if (true)// then + val x = 1 + 5 + else 7 + ) + y + +def u(j: Int) = + val y = g( + if (true)// then + val x = 1 + 5 + else 7, + j + ) + y + +def b(k: Boolean): Int = + f( + if ( + k + && b(!k) > 0 + ) then 27 + else 42 + ) + +def p(b: Boolean) = + import collection.mutable.ListBuffer + val xs, ys = ListBuffer.empty[String] + (if (b) + xs + else + ys) += "hello, world" + (xs.toString, ys.toString) + +def q(b: Boolean) = + import collection.mutable.ListBuffer + val xs, ys = ListBuffer.empty[String] + (if (b) + then xs + else ys) += "hello, world" + (xs.toString, ys.toString) From c8131c031a644c2f9d0dcd9f5fb9f07889880657 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Sun, 16 Feb 2025 08:49:08 -0800 Subject: [PATCH 2/3] Prefer chaining, avoid duplication in finally --- .../dotty/tools/dotc/parsing/Parsers.scala | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 50034fa6f5d6..89e267764458 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -105,7 +105,7 @@ object Parsers { private val InCase: Region => Region = Scanners.InCase(_) private val InCond: Region => Region = Scanners.InParens(LPAREN, _) private val InFor : Region => Region = Scanners.InBraces(_) - private val InBrk : Region => Region = _.match + private val InBrk : Region => Region = case p: Scanners.InParens => Scanners.Indented(p.indentWidth, p.prefix, p) case r => r @@ -2328,27 +2328,25 @@ object Parsers { def condExpr(altToken: Token): Tree = val t: Tree = if in.token == LPAREN then - var t: Tree = - inSepRegion(InBrk): // allow inferred NEWLINE for observeIndented below - atSpan(in.offset): - makeTupleOrParens(inParensWithCommas(commaSeparated(exprInParens))) - if in.token != altToken then - if toBeContinued(altToken) then - t = inSepRegion(InCond) { + inSepRegion(InBrk): // allow inferred NEWLINE for observeIndented below + atSpan(in.offset): + makeTupleOrParens(inParensWithCommas(commaSeparated(exprInParens))) + .pipe: t => + if in.token == altToken then t + else if toBeContinued(altToken) then + inSepRegion(InCond): expr1Rest( postfixExprRest( simpleExprRest(t, Location.ElseWhere), Location.ElseWhere), Location.ElseWhere) - } else if rewriteToNewSyntax(t.span) then - dropParensOrBraces(t.span.start, s"${tokenString(altToken)}") + dropParensOrBraces(t.span.start, tokenString(altToken)) in.observeIndented() return t - t else if in.isNestedStart then - try expr() finally newLinesOpt() + expr().tap(_ => newLinesOpt()) else inSepRegion(InCond)(expr()) if rewriteToOldSyntax(t.span.startPos) then revertToParens(t) From a19c14ad704c4d1d2e2e794c888fecf8672b0797 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Tue, 18 Feb 2025 09:18:01 -0800 Subject: [PATCH 3/3] Clarify region workaround --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 89e267764458..b70ea413b0c6 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -105,7 +105,7 @@ object Parsers { private val InCase: Region => Region = Scanners.InCase(_) private val InCond: Region => Region = Scanners.InParens(LPAREN, _) private val InFor : Region => Region = Scanners.InBraces(_) - private val InBrk : Region => Region = + private val InOldCond: Region => Region = // old-style Cond to allow indent when InParens, see #22608 case p: Scanners.InParens => Scanners.Indented(p.indentWidth, p.prefix, p) case r => r @@ -2328,7 +2328,7 @@ object Parsers { def condExpr(altToken: Token): Tree = val t: Tree = if in.token == LPAREN then - inSepRegion(InBrk): // allow inferred NEWLINE for observeIndented below + inSepRegion(InOldCond): // allow inferred NEWLINE for observeIndented below atSpan(in.offset): makeTupleOrParens(inParensWithCommas(commaSeparated(exprInParens))) .pipe: t =>