From efbb347f2779d8907c03e8021fb51091fe75ff06 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 May 2021 12:12:41 +0200 Subject: [PATCH] Don't assume leading infix if next line is indented less Don't assume that an operator `op` is a leading infix operator if `op` appears on its own line, and next line is indented less. Fixes #12395 --- compiler/src/dotty/tools/dotc/parsing/Scanners.scala | 6 +++++- docs/docs/reference/changed-features/operators.md | 10 ++++++---- tests/pos/i12395.scala | 9 +++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) create mode 100644 tests/pos/i12395.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 10905f93c80a..d977b084a14d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -369,6 +369,8 @@ object Scanners { * - it does not follow a blank line, and * - it is followed by at least one whitespace character and a * token that can start an expression. + * - if the operator appears on its own line, the next line must have at least + * the same indentation width as the operator. See pos/i12395 for a test where this matters. * If a leading infix operator is found and the source version is `3.0-migration`, emit a change warning. */ def isLeadingInfixOperator(nextWidth: IndentWidth = indentWidth(offset), inConditional: Boolean = true) = @@ -397,7 +399,9 @@ object Scanners { // force a NEWLINE a after current token if it is on its own line lookahead.nextToken() assumeStartsExpr(lookahead) - || lookahead.token == NEWLINE && assumeStartsExpr(lookahead.next) + || lookahead.token == NEWLINE + && assumeStartsExpr(lookahead.next) + && indentWidth(offset) <= indentWidth(lookahead.next.offset) } && { currentRegion match diff --git a/docs/docs/reference/changed-features/operators.md b/docs/docs/reference/changed-features/operators.md index ff30c6c74bd9..a9511ab7cc5e 100644 --- a/docs/docs/reference/changed-features/operators.md +++ b/docs/docs/reference/changed-features/operators.md @@ -128,10 +128,12 @@ would have treated the continuations `++ " world"` or `|| xs.isEmpty` as separat To make this syntax work, the rules are modified to not infer semicolons in front of leading infix operators. A _leading infix operator_ is - - a symbolic identifier such as `+`, or `approx_==`, or an identifier in backticks, - - that starts a new line, - - that precedes a token on the same or the next line that can start an expression, - - and that is immediately followed by at least one whitespace character. + - a symbolic identifier such as `+`, or `approx_==`, or an identifier in backticks that + - starts a new line, and + - is not following a blank line, and + - is followed by at least one whitespace character and a token that can start an expression. + - Furthermore, if the operator appears on its own line, the next line must have at least + the same indentation width as the operator. Example: diff --git a/tests/pos/i12395.scala b/tests/pos/i12395.scala new file mode 100644 index 000000000000..6915ba5655b9 --- /dev/null +++ b/tests/pos/i12395.scala @@ -0,0 +1,9 @@ +@main def main : Unit = + val x = 1 + + val y = x match + case 1 => 1 + case _ => + println("bad") + ??? + println(x) \ No newline at end of file