Skip to content

Commit c19ab3b

Browse files
committed
Allow infix operators at start of line
1 parent 5370bc1 commit c19ab3b

File tree

1 file changed

+36
-4
lines changed

1 file changed

+36
-4
lines changed

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ object Scanners {
223223
val oldSyntax = ctx.settings.oldSyntax.value
224224
val newSyntax = ctx.settings.newSyntax.value
225225

226+
/** A switch whether operators at the start of lines can be infix operators */
227+
private var allowLeadingInfixOperators = true
228+
226229
/** All doc comments kept by their end position in a `Map` */
227230
private[this] var docstringMap: SortedMap[Int, Comment] = SortedMap.empty
228231

@@ -382,6 +385,30 @@ object Scanners {
382385
next.token = EMPTY
383386
}
384387

388+
def insertNL(nl: Token): Unit = {
389+
next.copyFrom(this)
390+
// todo: make offset line-end of previous line?
391+
offset = if (lineStartOffset <= offset) lineStartOffset else lastLineStartOffset
392+
token = nl
393+
}
394+
395+
396+
/** A leading symbolic or backquoted identifier is treated as an infix operator
397+
* if it is followed by at least one ' ' and a token on the same line
398+
* that can start an expression.
399+
*/
400+
def isLeadingInfixOperator =
401+
allowLeadingInfixOperators &&
402+
(token == BACKQUOTED_IDENT ||
403+
token == IDENTIFIER && isOperatorPart(name(name.length - 1))) &&
404+
(ch == ' ') && {
405+
val lookahead = lookaheadScanner
406+
lookahead.allowLeadingInfixOperators = false
407+
// force a NEWLINE a after current token if it is on its own line
408+
lookahead.nextToken()
409+
canStartExpressionTokens.contains(lookahead.token)
410+
}
411+
385412
/** Insert NEWLINE or NEWLINES if
386413
* - we are after a newline
387414
* - we are within a { ... } or on toplevel (wrt sepRegions)
@@ -393,10 +420,15 @@ object Scanners {
393420
(canStartStatTokens contains token) &&
394421
(sepRegions.isEmpty || sepRegions.head == RBRACE ||
395422
sepRegions.head == ARROW && token == CASE)) {
396-
next copyFrom this
397-
// todo: make offset line-end of previous line?
398-
offset = if (lineStartOffset <= offset) lineStartOffset else lastLineStartOffset
399-
token = if (pastBlankLine()) NEWLINES else NEWLINE
423+
if (pastBlankLine())
424+
insertNL(NEWLINES)
425+
else if (!isLeadingInfixOperator)
426+
insertNL(NEWLINE)
427+
else if (isScala2Mode || oldSyntax)
428+
ctx.warning(em"""Line starts with an operator;
429+
|it is now treated as a continuation of the expression on the previous line,
430+
|not as a separate statement.""",
431+
source.atSpan(Span(offset)))
400432
}
401433

402434
postProcessToken()

0 commit comments

Comments
 (0)