Skip to content

Commit

Permalink
Backport Generalize isOperator and test for assignment op
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Apr 17, 2021
1 parent 1f44b27 commit 6e0e4f1
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 19 deletions.
28 changes: 16 additions & 12 deletions src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
Expand Up @@ -432,20 +432,23 @@ trait Scanners extends ScannersCommon {
token = nl
}

def isOperator: Boolean = token == BACKQUOTED_IDENT || token == IDENTIFIER && isOperatorPart(name.charAt(name.length - 1))

/* A leading infix operator must be followed by a lexically suitable expression.
* Usually any simple expr will do. However, if the op is backtick style, make
* sure it is not followed by a binary op, which suggests the backticked identifier
* is a reference.
* Usually any simple expr will do. However, a backquoted identifier may serve as
* either an op or a reference. So the additional constraint is that the following
* token can't be an assignment operator. (Dotty disallows binary ops, hence the
* test for unary.) See run/multiLineOps.scala for 42 + `x` on 3 lines, where +
* is not leading infix because backquoted x is non-unary op.
*/
def followedByInfixRHS: Boolean = {
val current = token
def isOp: Boolean = token == IDENTIFIER && isOperatorPart(name.charAt(name.length - 1))
def isCandidateInfixRHS: Boolean =
isSimpleExprIntroToken(token) &&
(current != BACKQUOTED_IDENT || !isOp || nme.raw.isUnary(name))
//def isCandidateInfixRHS: Boolean = isSimpleExprIntroToken(token) && (!isOperator || nme.raw.isUnary(name) || token == BACKQUOTED_IDENT)
def isAssignmentOperator: Boolean =
name.endsWith('=') && !name.startsWith('=') && isOperatorPart(name.startChar) &&
(name.length != 2 || (name.startChar match { case '!' | '<' | '>' => false case _ => true }))
def isCandidateInfixRHS: Boolean = isSimpleExprIntroToken(token) && (!isOperator || token == BACKQUOTED_IDENT || !isAssignmentOperator)
lookingAhead {
isCandidateInfixRHS ||
token == NEWLINE && { nextToken() ; isCandidateInfixRHS }
isCandidateInfixRHS || token == NEWLINE && { nextToken() ; isCandidateInfixRHS }
}
}

Expand All @@ -455,8 +458,9 @@ trait Scanners extends ScannersCommon {
*/
def isLeadingInfixOperator =
allowLeadingInfixOperators &&
(token == BACKQUOTED_IDENT || token == IDENTIFIER && isOperatorPart(name.charAt(name.length - 1))) &&
ch <= ' ' && followedByInfixRHS
isOperator &&
(isWhitespace(ch) || ch == LF) &&
followedByInfixRHS

/* Insert NEWLINE or NEWLINES if
* - we are after a newline
Expand Down
17 changes: 13 additions & 4 deletions test/files/neg/t12071.scala
Expand Up @@ -34,9 +34,18 @@ object C {
def `test-2`: Int = 42
def compareTo(x: Int) = println("lol")

var `test-3`: List[Int] = Nil
def yy = 1
/* fails in scala 3
+
`test-1`
+
`test-2`
*/
}

// since ++ is not unary, test-3 is not taken as an operator; this test doesn't fix y above.
def yy = List.empty[Int] ++
`test-3` ++ `test-3`
object Test extends App {
println(C.x)
println(C.y)
println(C.z)
println(C.yy)
}
8 changes: 5 additions & 3 deletions test/files/run/multiLineOps.scala
Expand Up @@ -6,8 +6,10 @@
object Test extends App {
val a = 7
val x = 1
+ //
`a` * 6
+
`a`
*
6

assert(x == 1 + 42, x) // was: 1
assert(x == 1 + 7 * 6, x) // was: 1, now: successor(42)
}

0 comments on commit 6e0e4f1

Please sign in to comment.