diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index c78e4cd8fb2f..d2e523587404 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -444,9 +444,12 @@ object Parsers { /** Convert tree to formal parameter */ - def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match { + def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match + case param: ValDef => + param.withMods(param.mods | mods.flags) case id @ Ident(name) => makeParameter(name.asTermName, TypeTree(), mods, isBackquoted = isBackquoted(id)).withSpan(tree.span) + // the following three cases are needed only for 2.x parameters without enclosing parentheses case Typed(_, tpt: TypeBoundsTree) => syntaxError(s"not a legal $expected", tree.span) makeParameter(nme.ERROR, tree, mods) @@ -457,7 +460,6 @@ object Parsers { case _ => syntaxError(s"not a legal $expected", tree.span) makeParameter(nme.ERROR, tree, mods) - } /** Convert (qual)ident to type identifier */ @@ -891,6 +893,16 @@ object Parsers { val next = in.lookahead.token next == LBRACKET || next == LPAREN + + def followingIsSelfType() = + val lookahead = in.LookaheadScanner() + lookahead.nextToken() + lookahead.token == COLON + && { + lookahead.nextToken() + canStartTypeTokens(lookahead.token) + } + /** Is current ident a `*`, and is it followed by a `)`, `, )`, `,EOF`? The latter two are not syntactically valid, but we need to include them here for error recovery. */ def followingIsVararg(): Boolean = @@ -905,6 +917,21 @@ object Parsers { } } + /** When encountering a `:`, is that in the first binding of a lambda? + * @pre location of the enclosing expression is `InParens`, so there is am open `(`. + */ + def followingisLambdaParams() = + val lookahead = in.LookaheadScanner() + lookahead.nextToken() + while lookahead.token != RPAREN && lookahead.token != EOF do + if lookahead.token == LPAREN then lookahead.skipParens() + else lookahead.nextToken() + lookahead.token == RPAREN + && { + lookahead.nextToken() + lookahead.isArrow + } + /* --------- OPERAND/OPERATOR STACK --------------------------------------- */ var opStack: List[OpInfo] = Nil @@ -2273,7 +2300,7 @@ object Parsers { placeholderParams = param :: placeholderParams atSpan(start) { Ident(pname) } case LPAREN => - atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOpt())) } + atSpan(in.offset) { makeTupleOrParens(inParens(exprsInParensOrBindings())) } case LBRACE | INDENT => canApply = false blockExpr() @@ -2343,7 +2370,17 @@ object Parsers { val app = applyToClosure(t, in.offset, convertToParams(termIdent())) simpleExprRest(app, location, canApply = true) case _ => - t + t match + case id @ Ident(name) + if in.isColon() && location == Location.InParens && followingisLambdaParams() => + if name.is(WildcardParamName) then + assert(name == placeholderParams.head.name) + placeholderParams = placeholderParams.tail + atSpan(startOffset(id)) { + makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id)) + } + case _ => + t } } @@ -2377,9 +2414,20 @@ object Parsers { } /** ExprsInParens ::= ExprInParens {`,' ExprInParens} + * Bindings ::= Binding {`,' Binding} */ - def exprsInParensOpt(): List[Tree] = - if (in.token == RPAREN) Nil else commaSeparated(exprInParens) + def exprsInParensOrBindings(): List[Tree] = + if in.token == RPAREN then Nil + else in.currentRegion.withCommasExpected { + var isFormalParams = false + def exprOrBinding() = + if isFormalParams then binding(Modifiers()) + else + val t = exprInParens() + if t.isInstanceOf[ValDef] then isFormalParams = true + t + commaSeparatedRest(exprOrBinding(), exprOrBinding) + } /** ParArgumentExprs ::= `(' [‘using’] [ExprsInParens] `)' * | `(' [ExprsInParens `,'] PostfixExpr `*' ')' @@ -3892,7 +3940,37 @@ object Parsers { stats.toList } - /** TemplateStatSeq ::= [id [`:' Type] `=>'] TemplateStat {semi TemplateStat} + /** SelfType ::= id [‘:’ InfixType] ‘=>’ + * | ‘this’ ‘:’ InfixType ‘=>’ + */ + def selfType(): ValDef = + if (in.isIdent || in.token == THIS) + && (in.lookahead.token == COLON && followingIsSelfType() + || in.lookahead.token == ARROW) + then + atSpan(in.offset) { + val selfName = + if in.token == THIS then + in.nextToken() + nme.WILDCARD + else ident() + val selfTpt = + if in.token == COLON then + in.nextToken() + infixType() + else + if selfName == nme.WILDCARD then accept(COLON) + TypeTree() + if in.token == ARROW then + in.token = SELFARROW // suppresses INDENT insertion after `=>` + in.nextToken() + else + syntaxError("`=>` expected after self type") + makeSelfDef(selfName, selfTpt) + } + else EmptyValDef + + /** TemplateStatSeq ::= [SelfType] TemplateStat {semi TemplateStat} * TemplateStat ::= Import * | Export * | Annotations Modifiers Def @@ -3904,25 +3982,8 @@ object Parsers { * | Annotations Modifiers EnumCase */ def templateStatSeq(): (ValDef, List[Tree]) = checkNoEscapingPlaceholders { - var self: ValDef = EmptyValDef val stats = new ListBuffer[Tree] - if isExprIntro && !isDefIntro(modifierTokens) then - val first = expr1() - if in.token == ARROW then - first match { - case Typed(tree @ This(EmptyTypeIdent), tpt) => - self = makeSelfDef(nme.WILDCARD, tpt).withSpan(first.span) - case _ => - val ValDef(name, tpt, _) = convertToParam(first, EmptyModifiers, "self type clause") - if (name != nme.ERROR) - self = makeSelfDef(name, tpt).withSpan(first.span) - } - in.token = SELFARROW // suppresses INDENT insertion after `=>` - in.nextToken() - else - stats += first - statSepOrEnd(stats) - end if + val self = selfType() while var empty = false if (in.token == IMPORT) diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index 1f414e7e912c..594312f9aaaa 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -233,7 +233,7 @@ object Tokens extends TokensCommon { final val canStartExprTokens2: TokenSet = canStartExprTokens3 | BitSet(DO) final val canStartTypeTokens: TokenSet = literalTokens | identifierTokens | BitSet( - THIS, SUPER, USCORE, LPAREN, AT) + THIS, SUPER, USCORE, LPAREN, LBRACE, AT) final val templateIntroTokens: TokenSet = BitSet(CLASS, TRAIT, OBJECT, ENUM, CASECLASS, CASEOBJECT) diff --git a/tests/neg/cycles.scala b/tests/neg/cycles.scala index b77d253bd527..b24a367c8377 100644 --- a/tests/neg/cycles.scala +++ b/tests/neg/cycles.scala @@ -38,6 +38,6 @@ class T2 { type U = X | Int } object T12 { - ??? : (T1 {})#U // old-error: conflicting bounds + val _ : (T1 {})#U = ??? // old-error: conflicting bounds ??? : (T2 {})#U // old-error: conflicting bounds } diff --git a/tests/neg/i13769.check b/tests/neg/i13769.check index 3d7af1bd06a1..8291a84fc899 100644 --- a/tests/neg/i13769.check +++ b/tests/neg/i13769.check @@ -1,10 +1,6 @@ --- Error: tests/neg/i13769.scala:2:18 ---------------------------------------------------------------------------------- -2 |val te = tup.map((x: _ <: Int) => List(x)) // error // error - | ^^^^^^^^^^^ - | not a legal formal parameter --- [E006] Not Found Error: tests/neg/i13769.scala:2:39 ----------------------------------------------------------------- -2 |val te = tup.map((x: _ <: Int) => List(x)) // error // error - | ^ - | Not found: x +-- [E035] Syntax Error: tests/neg/i13769.scala:2:21 -------------------------------------------------------------------- +2 |val te = tup.map((x: _ <: Int) => List(x)) // error + | ^^^^^^^^ + | Unbound wildcard type | | longer explanation available when compiling with `-explain` diff --git a/tests/neg/i13769.scala b/tests/neg/i13769.scala index 67575e821334..e4e66fe2cef2 100644 --- a/tests/neg/i13769.scala +++ b/tests/neg/i13769.scala @@ -1,2 +1,2 @@ val tup = (1, "s") -val te = tup.map((x: _ <: Int) => List(x)) // error // error +val te = tup.map((x: _ <: Int) => List(x)) // error diff --git a/tests/neg/i1424.scala b/tests/neg/i1424.scala index 8eba3284211b..deffd671761e 100644 --- a/tests/neg/i1424.scala +++ b/tests/neg/i1424.scala @@ -1,3 +1,3 @@ class Test { - (x: Int) => x // error: not a legal self type clause // error: not found x + (x: Int) => x // error: not a legal self type clause } diff --git a/tests/neg/i4373b.scala b/tests/neg/i4373b.scala index 297fcd76ff08..45b60a46c721 100644 --- a/tests/neg/i4373b.scala +++ b/tests/neg/i4373b.scala @@ -1,5 +1,5 @@ // ==> 05bef7805687ba94da37177f7568e3ba7da1f91c.scala <== class x0 { - x1: // error + x1: x0 | _ // error // error \ No newline at end of file diff --git a/tests/neg/i7818.scala b/tests/neg/i7818.scala index 1dc243cfeedc..78cbee506784 100644 --- a/tests/neg/i7818.scala +++ b/tests/neg/i7818.scala @@ -1 +1 @@ -def foo = (x: @) => () // error // error \ No newline at end of file +def foo = (x: @) => () // error \ No newline at end of file diff --git a/tests/neg/parser-stability-16.scala b/tests/neg/parser-stability-16.scala index 9ba58d0219f2..25fb38374c45 100644 --- a/tests/neg/parser-stability-16.scala +++ b/tests/neg/parser-stability-16.scala @@ -3,4 +3,3 @@ class x0[x0] { } trait x3 extends x0 { // error x1 = 0 object // error // error -// error \ No newline at end of file diff --git a/tests/neg/parser-stability-5.scala b/tests/neg/parser-stability-5.scala index 69f4568aab73..5de49927ee58 100644 --- a/tests/neg/parser-stability-5.scala +++ b/tests/neg/parser-stability-5.scala @@ -1,4 +1,4 @@ trait x0 { -x1 : { // error +x1 : { var x2 // error \ No newline at end of file