Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow soft keywords open and infix under -Xsource:3 #9580

Merged
merged 1 commit into from Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}
}