Skip to content

Commit

Permalink
Merge pull request #9580 from smarter/open-infix
Browse files Browse the repository at this point in the history
Allow soft keywords `open` and `infix` under -Xsource:3
  • Loading branch information
lrytz committed Apr 21, 2021
2 parents 996a89c + 8da523c commit 81c02a0
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 11 deletions.
47 changes: 36 additions & 11 deletions src/compiler/scala/tools/nsc/ast/parser/Parsers.scala
Expand Up @@ -674,6 +674,24 @@ self =>
case _ => false
}

def isSoftModifier: Boolean =
currentRun.isScala3 && in.token == IDENTIFIER && softModifierNames.contains(in.name)

/** Is the current token a soft modifier in a position where such a modifier is allowed? */
def isValidSoftModifier: Boolean =
isSoftModifier && {
val mod = in.name
lookingAhead {
while (in.token == NEWLINE || isModifier || isSoftModifier) in.nextToken()

in.token match {
case CLASS | CASECLASS => true
case DEF | TRAIT | TYPE => mod == nme.infix
case _ => false
}
}
}

def isAnnotation: Boolean = in.token == AT

def isLocalModifier: Boolean = in.token match {
Expand Down Expand Up @@ -719,12 +737,13 @@ self =>
}
def isLiteral = isLiteralToken(in.token)

def isExprIntroToken(token: Token): Boolean = isLiteralToken(token) || (token match {
case IDENTIFIER | BACKQUOTED_IDENT |
THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE |
DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true
case _ => false
})
def isExprIntroToken(token: Token): Boolean =
!isValidSoftModifier && (isLiteralToken(token) || (token match {
case IDENTIFIER | BACKQUOTED_IDENT |
THIS | SUPER | IF | FOR | NEW | USCORE | TRY | WHILE |
DO | RETURN | THROW | LPAREN | LBRACE | XMLSTART => true
case _ => false
}))

def isExprIntro: Boolean = isExprIntroToken(in.token)

Expand Down Expand Up @@ -2265,8 +2284,11 @@ self =>
*/
def accessModifierOpt(): Modifiers = normalizeModifiers {
in.token match {
case m @ (PRIVATE | PROTECTED) => in.nextToken() ; accessQualifierOpt(Modifiers(flagTokens(m)))
case _ => NoMods
case m @ (PRIVATE | PROTECTED) =>
in.nextToken()
accessQualifierOpt(Modifiers(flagTokens(m)))
case _ =>
NoMods
}
}

Expand All @@ -2288,7 +2310,10 @@ self =>
in.nextToken()
loop(mods)
case _ =>
mods
if (isValidSoftModifier) {
in.nextToken()
loop(mods)
} else mods
}
loop(NoMods)
}
Expand Down Expand Up @@ -3221,7 +3246,7 @@ self =>
case IMPORT =>
in.flushDoc()
importClause()
case _ if isAnnotation || isTemplateIntro || isModifier =>
case _ if isAnnotation || isTemplateIntro || isModifier || isValidSoftModifier =>
joinComment(topLevelTmplDef :: Nil)
}

Expand Down Expand Up @@ -3271,7 +3296,7 @@ self =>
case IMPORT =>
in.flushDoc()
importClause()
case _ if isDefIntro || isModifier || isAnnotation =>
case _ if isDefIntro || isModifier || isAnnotation || isValidSoftModifier =>
joinComment(nonLocalDefOrDcl)
case _ if isExprIntro =>
in.flushDoc()
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/scala/tools/nsc/ast/parser/Scanners.scala
Expand Up @@ -1438,6 +1438,8 @@ trait Scanners extends ScannersCommon {

final val token2name = (allKeywords map (_.swap)).toMap

final val softModifierNames = Set(nme.open, nme.infix)

// Token representation ----------------------------------------------------

/** Returns the string representation of given token. */
Expand Down
4 changes: 4 additions & 0 deletions src/reflect/scala/reflect/internal/StdNames.scala
Expand Up @@ -661,6 +661,10 @@ trait StdNames {
// Scala 3 import syntax
val as: NameType = nameType("as")

// Scala 3 soft keywords
val infix: NameType = nameType("infix")
val open: NameType = nameType("open")

// Compiler utilized names

val AnnotatedType: NameType = nameType("AnnotatedType")
Expand Down
22 changes: 22 additions & 0 deletions test/files/neg/open-infix-future.check
@@ -0,0 +1,22 @@
open-infix-future.scala:4: error: expected class or object definition
open trait A // error
^
open-infix-future.scala:5: error: expected class or object definition
open object B // error
^
open-infix-future.scala:8: error: ';' expected but 'val' found.
infix val a: Int = 1 // error
^
open-infix-future.scala:9: error: ';' expected but 'var' found.
infix var b: Int = 1 // error
^
open-infix-future.scala:11: error: ';' expected but 'type' found.
open type D // error
^
open-infix-future.scala:14: error: illegal start of statement
open class E // error
^
open-infix-future.scala:15: error: ';' expected but 'def' found.
open def bla(y: Int) = y // error
^
7 errors
17 changes: 17 additions & 0 deletions test/files/neg/open-infix-future.scala
@@ -0,0 +1,17 @@
// scalac: -Xsource:3
//

open trait A // error
open object B // error

class C {
infix val a: Int = 1 // error
infix var b: Int = 1 // error

open type D // error

def foo: Unit = {
open class E // error
open def bla(y: Int) = y // error
}
}
36 changes: 36 additions & 0 deletions test/files/pos/open-infix-future.scala
@@ -0,0 +1,36 @@
// scalac: -Xsource:3
//

open class A
infix class B[T, S]

open infix class C[T, S]
open infix case class CC[T, S](x: Int)
infix open class D[T, S]
infix trait DT[T, S]

open
infix
private
class E

class F {
open infix class C1[T, S]
infix type X

infix def foo(x: Int): Int = x
}

object G {
open infix class C2[T, S]
}

object Test {
val infix: Int = 1
infix + 1
val open: Int => Int = x => x
open(1)
open {
2
}
}

0 comments on commit 81c02a0

Please sign in to comment.