From 4aad6890777ce05dd8040f1b58f93d55daec4e7a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 19 Nov 2018 17:54:49 +0100 Subject: [PATCH 1/6] Fix #5145: Improve treatment of soft modifiers - Generalize treatment of `inline` to a set of soft modifiers - Make `opaque` a soft modifier - Fix parsing of soft modifiers so that they are treated as modifiers only when preceding a hard modifier or a definition keyword --- .../src/dotty/tools/dotc/core/StdNames.scala | 3 +- .../dotty/tools/dotc/parsing/Parsers.scala | 36 +++++++++---------- .../dotty/tools/dotc/parsing/Scanners.scala | 32 ++++++++++++++--- .../src/dotty/tools/dotc/parsing/Tokens.scala | 11 +++--- .../dotc/printing/SyntaxHighlighting.scala | 3 +- tests/neg/inlinevals.scala | 2 +- tests/pos/i5145.scala | 8 +++++ 7 files changed, 64 insertions(+), 31 deletions(-) create mode 100644 tests/pos/i5145.scala diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index b6e3604516e0..65b73fc545c6 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -74,7 +74,6 @@ object StdNames { final val IFkw: N = kw("if") final val IMPLICITkw: N = kw("implicit") final val IMPORTkw: N = kw("import") - final val INLINEkw: N = kw("inline") final val LAZYkw: N = kw("lazy") final val MACROkw: N = kw("macro") final val MATCHkw: N = kw("match") @@ -436,6 +435,7 @@ object StdNames { val implicitConversions: N = "implicitConversions" val implicitly: N = "implicitly" val in: N = "in" + val `inline`: N = "inline" val info: N = "info" val inlinedEquals: N = "inlinedEquals" val internal: N = "internal" @@ -478,6 +478,7 @@ object StdNames { val notify_ : N = "notify" val null_ : N = "null" val ofDim: N = "ofDim" + val `opaque`: N = "opaque" val origin: N = "origin" val prefix : N = "prefix" val productArity: N = "productArity" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 24d3192beebf..d4d3f0f9621f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -166,22 +166,24 @@ object Parsers { def isSimpleLiteral: Boolean = simpleLiteralTokens contains in.token def isLiteral: Boolean = literalTokens contains in.token def isNumericLit: Boolean = numericLitTokens contains in.token - def isModifier: Boolean = modifierTokens.contains(in.token) || isIdent(nme.INLINEkw) def isBindingIntro: Boolean = canStartBindingTokens contains in.token def isTemplateIntro: Boolean = templateIntroTokens contains in.token def isDclIntro: Boolean = dclIntroTokens contains in.token def isStatSeqEnd: Boolean = in.token == RBRACE || in.token == EOF def mustStartStat: Boolean = mustStartStatTokens contains in.token + /** Is current token a hard or soft modifier (in modifier position or not)? */ + def isModifier: Boolean = modifierTokens.contains(in.token) || in.isSoftModifier + def isExprIntro: Boolean = - (canStartExpressionTokens `contains` in.token) && - (!isIdent(nme.INLINEkw) || lookaheadIn(canStartExpressionTokens)) + canStartExpressionTokens.contains(in.token) && + !in.isSoftModifierInModifierPosition def isDefIntro(allowedMods: BitSet): Boolean = in.token == AT || (defIntroTokens `contains` in.token) || (allowedMods `contains` in.token) || - isIdent(nme.INLINEkw) && lookaheadIn(BitSet(AT) | defIntroTokens | allowedMods) + in.isSoftModifierInModifierPosition def isStatSep: Boolean = in.token == NEWLINE || in.token == NEWLINES || in.token == SEMI @@ -455,14 +457,6 @@ object Parsers { def commaSeparated[T](part: () => T): List[T] = tokenSeparated(COMMA, part) - /** Is the token following the current one in `tokens`? */ - def lookaheadIn(tokens: BitSet): Boolean = { - val lookahead = in.lookaheadScanner - do lookahead.nextToken() - while (lookahead.token == NEWLINE || lookahead.token == NEWLINES) - tokens.contains(lookahead.token) - } - /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ var opStack: List[OpInfo] = Nil @@ -841,7 +835,7 @@ object Parsers { /** Is current ident a `*`, and is it followed by a `)` or `,`? */ def isPostfixStar: Boolean = - in.name == nme.raw.STAR && lookaheadIn(BitSet(RPAREN, COMMA)) + in.name == nme.raw.STAR && in.lookaheadIn(BitSet(RPAREN, COMMA)) def infixTypeRest(t: Tree): Tree = infixOps(t, canStartTypeTokens, refinedType, isType = true, isOperator = !isPostfixStar) @@ -899,7 +893,7 @@ object Parsers { val start = in.skipToken() typeBounds().withPos(Position(start, in.lastOffset, start)) } - else if (isIdent(nme.raw.TILDE) && lookaheadIn(BitSet(IDENTIFIER, BACKQUOTED_IDENT))) + else if (isIdent(nme.raw.TILDE) && in.lookaheadIn(BitSet(IDENTIFIER, BACKQUOTED_IDENT))) atPos(in.offset) { PrefixOp(typeIdent(), path(thisOK = true)) } else path(thisOK = false, handleSingletonType) match { case r @ SingletonTypeTree(_) => r @@ -1744,8 +1738,11 @@ object Parsers { case PRIVATE => Mod.Private() case PROTECTED => Mod.Protected() case SEALED => Mod.Sealed() - case OPAQUE => Mod.Opaque() - case IDENTIFIER if name == nme.INLINEkw => Mod.Inline() + case IDENTIFIER => + name match { + case nme.`inline` => Mod.Inline() + case nme.`opaque` => Mod.Opaque() + } } /** Drop `private' modifier when followed by a qualifier. @@ -1816,7 +1813,8 @@ object Parsers { @tailrec def loop(mods: Modifiers): Modifiers = { if (allowed.contains(in.token) || - isIdent(nme.INLINEkw) && localModifierTokens.subsetOf(allowed)) { + in.isSoftModifier && + localModifierTokens.subsetOf(allowed)) { // soft modifiers are admissible everywhere local modifiers are val isAccessMod = accessModifierTokens contains in.token val mods1 = addModifier(mods) loop(if (isAccessMod) accessQualifierOpt(mods1) else mods1) @@ -1957,7 +1955,7 @@ object Parsers { } } else { - if (isIdent(nme.INLINEkw)) mods = addModifier(mods) + if (isIdent(nme.`inline`)) mods = addModifier(mods) mods = atPos(start) { mods | Param } } atPos(start, nameStart) { @@ -2616,7 +2614,7 @@ object Parsers { if (in.token == IMPLICIT || in.token == ERASED) { val start = in.offset var imods = modifiers(funArgMods) - if (isBindingIntro && !isIdent(nme.INLINEkw)) + if (isBindingIntro && !isIdent(nme.`inline`)) stats += implicitClosure(start, Location.InBlock, imods) else stats +++= localDef(start, imods) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 63f907af6b5c..a3adbfbfb88f 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -11,7 +11,7 @@ import util.NameTransformer.avoidIllegalChars import Tokens._ import scala.annotation.{ switch, tailrec } import scala.collection.mutable -import scala.collection.immutable.SortedMap +import scala.collection.immutable.{SortedMap, BitSet} import rewrites.Rewrites.patch object Scanners { @@ -301,9 +301,6 @@ object Scanners { case _ => } - /** A new Scanner that starts at the current token offset */ - def lookaheadScanner: Scanner = new Scanner(source, offset) - /** Produce next token, filling TokenData fields of Scanner. */ def nextToken(): Unit = { @@ -637,6 +634,28 @@ object Scanners { else false } +// Lookahead --------------------------------------------------------------- + + /** A new Scanner that starts at the current token offset */ + def lookaheadScanner: Scanner = new Scanner(source, offset) + + /** Is the token following the current one in `tokens`? */ + def lookaheadIn(tokens: BitSet): Boolean = { + val lookahead = lookaheadScanner + do lookahead.nextToken() + while (lookahead.token == NEWLINE || lookahead.token == NEWLINES) + tokens.contains(lookahead.token) + } + + /** Is the current token in a position where a modifier is allowed? */ + def inModifierPosition(): Boolean = { + val lookahead = lookaheadScanner + do lookahead.nextToken() + while (lookahead.token == NEWLINE || lookahead.token == NEWLINES || + lookahead.isSoftModifier) + modifierFollowers.contains(lookahead.token) + } + // Identifiers --------------------------------------------------------------- private def getBackquotedIdent(): Unit = { @@ -717,6 +736,11 @@ object Scanners { } } + def isSoftModifier: Boolean = + token == IDENTIFIER && softModifierNames.contains(name) + + def isSoftModifierInModifierPosition: Boolean = + isSoftModifier && inModifierPosition() // Literals ----------------------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 6f425e53dd3e..1d1ded5e13b9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -4,6 +4,7 @@ package parsing import collection.immutable.BitSet import core.Decorators._ +import core.StdNames.nme abstract class TokensCommon { def maxToken: Int @@ -93,7 +94,6 @@ abstract class TokensCommon { //final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate //final val ENUM = 62; enter(ENUM, "enum") //final val ERASED = 63; enter(ERASED, "erased") - //final val OPAQUE = 64; enter(OPAQUE, "opaque") /** special symbols */ final val COMMA = 70; enter(COMMA, "','") @@ -178,7 +178,6 @@ object Tokens extends TokensCommon { final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate final val ENUM = 62; enter(ENUM, "enum") final val ERASED = 63; enter(ERASED, "erased") - final val OPAQUE = 64; enter(OPAQUE, "opaque") /** special symbols */ final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") @@ -199,7 +198,7 @@ object Tokens extends TokensCommon { /** XML mode */ final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate - final val alphaKeywords: TokenSet = tokenRange(IF, OPAQUE) + final val alphaKeywords: TokenSet = tokenRange(IF, ERASED) final val symbolicKeywords: TokenSet = tokenRange(USCORE, VIEWBOUND) final val symbolicTokens: TokenSet = tokenRange(COMMA, VIEWBOUND) final val keywords: TokenSet = alphaKeywords | symbolicKeywords @@ -227,7 +226,7 @@ object Tokens extends TokensCommon { final val defIntroTokens: TokenSet = templateIntroTokens | dclIntroTokens final val localModifierTokens: TokenSet = BitSet( - ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY, ERASED, OPAQUE) + ABSTRACT, FINAL, SEALED, IMPLICIT, LAZY, ERASED) final val accessModifierTokens: TokenSet = BitSet( PRIVATE, PROTECTED) @@ -237,6 +236,8 @@ object Tokens extends TokensCommon { final val modifierTokensOrCase: TokenSet = modifierTokens | BitSet(CASE) + final val modifierFollowers = modifierTokens | defIntroTokens + /** Is token only legal as start of statement (eof also included)? */ final val mustStartStatTokens: TokenSet = defIntroTokens | modifierTokens | BitSet(IMPORT, PACKAGE) @@ -247,4 +248,6 @@ object Tokens extends TokensCommon { TYPE, RPAREN, RBRACE, RBRACKET) final val numericLitTokens: TokenSet = BitSet(INTLIT, LONGLIT, FLOATLIT, DOUBLELIT) + + final val softModifierNames = Set(nme.`inline`, nme.`opaque`) } diff --git a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala index 58d1cc3bbcec..a3b9485c7715 100644 --- a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala @@ -70,8 +70,7 @@ object SyntaxHighlighting { case _ if alphaKeywords.contains(token) => highlightRange(start, end, KeywordColor) - case IDENTIFIER if name == nme.INLINEkw => - // `inline` is a "soft" keyword + case IDENTIFIER if scanner.isSoftModifierInModifierPosition => highlightRange(start, end, KeywordColor) case IDENTIFIER if name == nme.??? => diff --git a/tests/neg/inlinevals.scala b/tests/neg/inlinevals.scala index 513283e63581..45d073850eaa 100644 --- a/tests/neg/inlinevals.scala +++ b/tests/neg/inlinevals.scala @@ -7,7 +7,7 @@ object Test { inline val N = 10 def X = 20 - inline inline val twice = 30 // error: repeated modifier // error: not found: inline + inline inline val twice = 30 // error: repeated modifier class C(inline x: Int, private inline val y: Int) { // error // error inline val foo: Int // error: abstract member may not be inline diff --git a/tests/pos/i5145.scala b/tests/pos/i5145.scala new file mode 100644 index 000000000000..d6dcb0e4f43f --- /dev/null +++ b/tests/pos/i5145.scala @@ -0,0 +1,8 @@ +class Test { + def foo(x: Int): Int = { + val inline = 3 + def opaque(x: Int): Unit = () + opaque(3) + inline + } +} From 1762b0e6c0590cc878a501196b7c3db0b5505b05 Mon Sep 17 00:00:00 2001 From: Allan Renucci Date: Mon, 19 Nov 2018 19:08:05 +0100 Subject: [PATCH 2/6] Fix syntax highlighting of soft keywords --- .../src/dotty/tools/dotc/printing/SyntaxHighlighting.scala | 6 ++---- .../dotty/tools/dotc/printing/SyntaxHighlightingTests.scala | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala index a3b9485c7715..989a3804e23a 100644 --- a/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala +++ b/compiler/src/dotty/tools/dotc/printing/SyntaxHighlighting.scala @@ -53,6 +53,7 @@ object SyntaxHighlighting { val start = scanner.offset val token = scanner.token val name = scanner.name + val isSoftModifier = scanner.isSoftModifierInModifierPosition scanner.nextToken() val end = scanner.lastOffset @@ -67,10 +68,7 @@ object SyntaxHighlighting { // we don't highlight it, hence the `-1` highlightRange(start, end - 1, LiteralColor) - case _ if alphaKeywords.contains(token) => - highlightRange(start, end, KeywordColor) - - case IDENTIFIER if scanner.isSoftModifierInModifierPosition => + case _ if alphaKeywords.contains(token) || isSoftModifier => highlightRange(start, end, KeywordColor) case IDENTIFIER if name == nme.??? => diff --git a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala index c53a2cd773e9..8bff254e663d 100644 --- a/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala +++ b/compiler/test/dotty/tools/dotc/printing/SyntaxHighlightingTests.scala @@ -125,5 +125,8 @@ class SyntaxHighlightingTests extends DottyTest { test("inline def foo = 1", " = ") test("@inline def foo = 1", " = ") test("class inline", " ") + test("val inline = 2", " = ") + test("def inline = 2", " = ") + test("def foo(inline: Int) = 2", " (: ) = ") } } From 3498ec5b2c6a355c58cb751996b85265c712ba74 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 20 Nov 2018 09:25:17 +0100 Subject: [PATCH 3/6] Refinements to soft modifiers - also handle parameter modifiers - add a spec --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 5 +++-- compiler/src/dotty/tools/dotc/parsing/Scanners.scala | 3 +++ docs/docs/reference/inline.md | 3 +-- docs/docs/reference/opaques.md | 2 ++ tests/pos/i5145.scala | 8 ++++++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index d4d3f0f9621f..6b95291b24fb 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1955,7 +1955,8 @@ object Parsers { } } else { - if (isIdent(nme.`inline`)) mods = addModifier(mods) + if (isIdent(nme.`inline`) && in.isSoftModifierInParamModifierPosition) + mods = addModifier(mods) mods = atPos(start) { mods | Param } } atPos(start, nameStart) { @@ -2614,7 +2615,7 @@ object Parsers { if (in.token == IMPLICIT || in.token == ERASED) { val start = in.offset var imods = modifiers(funArgMods) - if (isBindingIntro && !isIdent(nme.`inline`)) + if (isBindingIntro) stats += implicitClosure(start, Location.InBlock, imods) else stats +++= localDef(start, imods) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index a3adbfbfb88f..7dc3f19bfb32 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -742,6 +742,9 @@ object Scanners { def isSoftModifierInModifierPosition: Boolean = isSoftModifier && inModifierPosition() + def isSoftModifierInParamModifierPosition: Boolean = + isSoftModifier && !lookaheadIn(BitSet(COLON)) + // Literals ----------------------------------------------------------------- private def getStringLit() = { diff --git a/docs/docs/reference/inline.md b/docs/docs/reference/inline.md index aac7c914ca25..8f5a9928b3c0 100644 --- a/docs/docs/reference/inline.md +++ b/docs/docs/reference/inline.md @@ -3,8 +3,7 @@ layout: doc-page title: Inline --- -`inline` is a new modifier that guarantees that a definition will be -inline at the point of use. Example: +`inline` is a new [soft modifier](./soft-modifier.html) that guarantees that a definition will be inline at the point of use. Example: object Config { inline val logging = false diff --git a/docs/docs/reference/opaques.md b/docs/docs/reference/opaques.md index e4a2867049ef..b347250cc2ab 100644 --- a/docs/docs/reference/opaques.md +++ b/docs/docs/reference/opaques.md @@ -53,4 +53,6 @@ But the following operations would lead to type errors: l / l2 // error: `/` is not a member fo Logarithm ``` +`opaque` is a [soft modifier](./soft-modifier.html). + For more details, see [Scala SIP 35](https://docs.scala-lang.org/sips/opaque-types.html). \ No newline at end of file diff --git a/tests/pos/i5145.scala b/tests/pos/i5145.scala index d6dcb0e4f43f..f978ac7b71c4 100644 --- a/tests/pos/i5145.scala +++ b/tests/pos/i5145.scala @@ -5,4 +5,12 @@ class Test { opaque(3) inline } + def bar(inline: Int => Int) = 3 + inline def baz(inline x: Int => Int) = 3 + + locally { + bar(inline = identity) + bar(inline => inline) + bar(implicit inline => inline) + } } From 22cd8c1b115ed7d4629dd18bbd17283cdb09547e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 20 Nov 2018 09:31:44 +0100 Subject: [PATCH 4/6] Remove quotes around soft modifier names --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 4 ++-- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 65b73fc545c6..9e0e48374e2b 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -435,7 +435,7 @@ object StdNames { val implicitConversions: N = "implicitConversions" val implicitly: N = "implicitly" val in: N = "in" - val `inline`: N = "inline" + val inline: N = "inline" val info: N = "info" val inlinedEquals: N = "inlinedEquals" val internal: N = "internal" @@ -478,7 +478,7 @@ object StdNames { val notify_ : N = "notify" val null_ : N = "null" val ofDim: N = "ofDim" - val `opaque`: N = "opaque" + val opaque: N = "opaque" val origin: N = "origin" val prefix : N = "prefix" val productArity: N = "productArity" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6b95291b24fb..00a18ca48e05 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1740,8 +1740,8 @@ object Parsers { case SEALED => Mod.Sealed() case IDENTIFIER => name match { - case nme.`inline` => Mod.Inline() - case nme.`opaque` => Mod.Opaque() + case nme.inline => Mod.Inline() + case nme.opaque => Mod.Opaque() } } @@ -1955,7 +1955,7 @@ object Parsers { } } else { - if (isIdent(nme.`inline`) && in.isSoftModifierInParamModifierPosition) + if (isIdent(nme.inline) && in.isSoftModifierInParamModifierPosition) mods = addModifier(mods) mods = atPos(start) { mods | Param } } From 90765e38765b9a16fbc363fa031d113836b9d3f8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 20 Nov 2018 09:41:25 +0100 Subject: [PATCH 5/6] Add spec --- docs/docs/reference/soft-modifier.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docs/docs/reference/soft-modifier.md diff --git a/docs/docs/reference/soft-modifier.md b/docs/docs/reference/soft-modifier.md new file mode 100644 index 000000000000..524ef1a40536 --- /dev/null +++ b/docs/docs/reference/soft-modifier.md @@ -0,0 +1,11 @@ +--- +layout: doc-page +title: Soft Modifiers +--- + +A soft modifier is one of the identifiers `opaque` and `inline`. + +It is treated as a potential modifier of a definition, if it is followed by a hard modifier or a keyword combination starting a definition (`def`, `val`, `var`, `type`, `class`, `case class`, `trait`, `object`, `case object`, `enum`). Between the two words there may be a sequence of newline tokens and soft modifiers. + +It is treated as a potential modifier of a parameter binding unless it is followed by `:`. + From 36e8fe16278af0fcbafdbd4f4b403175079e04e0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 20 Nov 2018 10:06:56 +0100 Subject: [PATCH 6/6] Fix `isBindingIntro` Soft modifiers in modifier position are not considered as binder names. --- compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 00a18ca48e05..79bc1d46b5dd 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -166,7 +166,6 @@ object Parsers { def isSimpleLiteral: Boolean = simpleLiteralTokens contains in.token def isLiteral: Boolean = literalTokens contains in.token def isNumericLit: Boolean = numericLitTokens contains in.token - def isBindingIntro: Boolean = canStartBindingTokens contains in.token def isTemplateIntro: Boolean = templateIntroTokens contains in.token def isDclIntro: Boolean = dclIntroTokens contains in.token def isStatSeqEnd: Boolean = in.token == RBRACE || in.token == EOF @@ -175,6 +174,10 @@ object Parsers { /** Is current token a hard or soft modifier (in modifier position or not)? */ def isModifier: Boolean = modifierTokens.contains(in.token) || in.isSoftModifier + def isBindingIntro: Boolean = + canStartBindingTokens.contains(in.token) && + !in.isSoftModifierInModifierPosition + def isExprIntro: Boolean = canStartExpressionTokens.contains(in.token) && !in.isSoftModifierInModifierPosition