@@ -1090,27 +1090,56 @@ object Parsers {
10901090 }
10911091
10921092 /** Is the token sequence following the current `:` token classified as a lambda?
1093- * This is the case if the input starts with an identifier, a wildcard, or
1094- * something enclosed in (...) or [...], and this is followed by a `=>` or `?=>`
1095- * and an INDENT.
1096- */
1097- def followingIsLambdaAfterColon (): Boolean =
1093+ * If yes return a defined parsing function to parse the lambda body, if not
1094+ * return None. The case is triggered in two situations:
1095+ * 1. If the input starts with an identifier, a wildcard, or something
1096+ * enclosed in (...) or [...], this is followed by a `=>` or `?=>`,
1097+ * and one of the following two subcases applies:
1098+ * 1a. The next token is an indent. In this case the return parsing function parses
1099+ * an Expr in location Location.InColonArg.
1100+ * 1b. Under relaxedLambdaSyntax: the next token is on the same line and the enclosing region is not `(...)`.
1101+ * In this case the parsing function parses an Expr in location Location.InColonArg
1102+ * enclosed in a SingleLineLambda region, and then eats the ENDlambda token
1103+ * generated by the Scanner at the end of that region.
1104+ * The reason for excluding (1b) in regions enclosed in parentheses is to avoid
1105+ * an ambiguity with type ascription `(x: A => B)`, where function types are only
1106+ * allowed inside parentheses.
1107+ * 2. Under relaxedLambdaSyntax: the input starts with a `case`.
1108+ */
1109+ def followingIsLambdaAfterColon (): Option [() => Tree ] =
10981110 val lookahead = in.LookaheadScanner (allowIndent = true )
10991111 .tap(_.currentRegion.knownWidth = in.currentRegion.indentWidth)
1100- def isArrowIndent () =
1101- lookahead.isArrow
1102- && {
1112+ def isArrowIndent (): Option [() => Tree ] =
1113+ if lookahead.isArrow then
11031114 lookahead.observeArrowIndented()
1104- lookahead.token == INDENT || lookahead.token == EOF
1105- }
1106- lookahead.nextToken()
1107- if lookahead.isIdent || lookahead.token == USCORE then
1115+ if lookahead.token == INDENT || lookahead.token == EOF then
1116+ Some (() => expr(Location .InColonArg ))
1117+ else if in.featureEnabled(Feature .relaxedLambdaSyntax) then
1118+ isParamsAndArrow() match
1119+ case success @ Some (_) => success
1120+ case _ if ! in.currentRegion.isInstanceOf [InParens ] =>
1121+ Some : () =>
1122+ val t = inSepRegion(SingleLineLambda (_)):
1123+ expr(Location .InColonArg )
1124+ accept(ENDlambda )
1125+ t
1126+ case _ => None
1127+ else None
1128+ else None
1129+ def isParamsAndArrow (): Option [() => Tree ] =
11081130 lookahead.nextToken()
1109- isArrowIndent()
1110- else if lookahead.token == LPAREN || lookahead.token == LBRACKET then
1111- lookahead.skipParens()
1112- isArrowIndent()
1113- else false
1131+ if lookahead.isIdent || lookahead.token == USCORE then
1132+ lookahead.nextToken()
1133+ isArrowIndent()
1134+ else if lookahead.token == LPAREN || lookahead.token == LBRACKET then
1135+ lookahead.skipParens()
1136+ isArrowIndent()
1137+ else if lookahead.token == CASE && in.featureEnabled(Feature .relaxedLambdaSyntax) then
1138+ Some (() => singleCaseMatch())
1139+ else
1140+ None
1141+ isParamsAndArrow()
1142+ end followingIsLambdaAfterColon
11141143
11151144 /** Can the next lookahead token start an operand as defined by
11161145 * leadingOperandTokens, or is postfix ops enabled?
@@ -1175,12 +1204,19 @@ object Parsers {
11751204 case _ => infixOp
11761205 }
11771206
1178- /** True if we are seeing a lambda argument after a colon of the form:
1207+ /** Optionally, if we are seeing a lambda argument after a colon of the form
11791208 * : (params) =>
11801209 * body
1210+ * or a single-line lambda (under relaxedLambdaSyntax)
1211+ * : (params) => body
1212+ * or a case clause (under relaxedLambdaSyntax)
1213+ * : case pat guard => rhs
1214+ * then return the function used to parse `body` or the case clause.
11811215 */
1182- def isColonLambda =
1183- sourceVersion.enablesFewerBraces && in.token == COLONfollow && followingIsLambdaAfterColon()
1216+ def detectColonLambda : Option [() => Tree ] =
1217+ if sourceVersion.enablesFewerBraces && in.token == COLONfollow
1218+ then followingIsLambdaAfterColon()
1219+ else None
11841220
11851221 /** operand { infixop operand | MatchClause } [postfixop],
11861222 *
@@ -1204,17 +1240,19 @@ object Parsers {
12041240 opStack = OpInfo (top1, op, in.offset) :: opStack
12051241 colonAtEOLOpt()
12061242 newLineOptWhenFollowing(canStartOperand)
1207- if isColonLambda then
1208- in.nextToken()
1209- recur(expr(Location .InColonArg ))
1210- else if maybePostfix && ! canStartOperand(in.token) then
1211- val topInfo = opStack.head
1212- opStack = opStack.tail
1213- val od = reduceStack(base, topInfo.operand, 0 , true , in.name, isType)
1214- atSpan(startOffset(od), topInfo.offset) {
1215- PostfixOp (od, topInfo.operator)
1216- }
1217- else recur(operand(location))
1243+ detectColonLambda match
1244+ case Some (parseExpr) =>
1245+ in.nextToken()
1246+ recur(parseExpr())
1247+ case _ =>
1248+ if maybePostfix && ! canStartOperand(in.token) then
1249+ val topInfo = opStack.head
1250+ opStack = opStack.tail
1251+ val od = reduceStack(base, topInfo.operand, 0 , true , in.name, isType)
1252+ atSpan(startOffset(od), topInfo.offset) {
1253+ PostfixOp (od, topInfo.operator)
1254+ }
1255+ else recur(operand(location))
12181256 else
12191257 val t = reduceStack(base, top, minPrec, leftAssoc = true , in.name, isType)
12201258 if ! isType && in.token == MATCH then recurAtMinPrec(matchClause(t))
@@ -2358,6 +2396,7 @@ object Parsers {
23582396
23592397 /** Expr ::= [`implicit'] FunParams (‘=>’ | ‘?=>’) Expr
23602398 * | TypTypeParamClause ‘=>’ Expr
2399+ * | ExprCaseClause -- under experimental.relaxedLambdaSyntax
23612400 * | Expr1
23622401 * FunParams ::= Bindings
23632402 * | id
@@ -2409,6 +2448,8 @@ object Parsers {
24092448 val arrowOffset = accept(ARROW )
24102449 val body = expr(location)
24112450 makePolyFunction(tparams, body, " literal" , errorTermTree(arrowOffset), start, arrowOffset)
2451+ case CASE if in.featureEnabled(Feature .relaxedLambdaSyntax) =>
2452+ singleCaseMatch()
24122453 case _ =>
24132454 val saved = placeholderParams
24142455 placeholderParams = Nil
@@ -2472,9 +2513,8 @@ object Parsers {
24722513 if in.token == CATCH then
24732514 val span = in.offset
24742515 in.nextToken()
2475- (if in.token == CASE then Match (EmptyTree , caseClause(exprOnly = true ) :: Nil )
2476- else subExpr(),
2477- span)
2516+ (if in.token == CASE then singleCaseMatch() else subExpr(),
2517+ span)
24782518 else (EmptyTree , - 1 )
24792519
24802520 handler match {
@@ -2769,8 +2809,10 @@ object Parsers {
27692809 * | SimpleExpr (TypeArgs | NamedTypeArgs)
27702810 * | SimpleExpr1 ArgumentExprs
27712811 * | SimpleExpr1 ColonArgument
2772- * ColonArgument ::= colon [ LambdaStart]
2812+ * ColonArgument ::= colon { LambdaStart}
27732813 * indent (CaseClauses | Block) outdent
2814+ * | colon LambdaStart {LambdaStart} expr ENDlambda -- under experimental.relaxedLambdaSyntax
2815+ * | colon ExprCaseClause -- under experimental.relaxedLambdaSyntax
27742816 * LambdaStart ::= FunParams (‘=>’ | ‘?=>’)
27752817 * | TypTypeParamClause ‘=>’
27762818 * ColonArgBody ::= indent (CaseClauses | Block) outdent
@@ -2853,12 +2895,14 @@ object Parsers {
28532895 makeParameter(name.asTermName, typedOpt(), Modifiers (), isBackquoted = isBackquoted(id))
28542896 }
28552897 case _ => t
2856- else if isColonLambda then
2857- val app = atSpan(startOffset(t), in.skipToken()) {
2858- Apply (t, expr(Location .InColonArg ) :: Nil )
2859- }
2860- simpleExprRest(app, location, canApply = true )
2861- else t
2898+ else detectColonLambda match
2899+ case Some (parseExpr) =>
2900+ val app =
2901+ atSpan(startOffset(t), in.skipToken()):
2902+ Apply (t, parseExpr() :: Nil )
2903+ simpleExprRest(app, location, canApply = true )
2904+ case None =>
2905+ t
28622906 end simpleExprRest
28632907
28642908 /** SimpleExpr ::= ‘new’ ConstrApp {`with` ConstrApp} [TemplateBody]
@@ -3165,9 +3209,9 @@ object Parsers {
31653209 case ARROW => atSpan(in.skipToken()):
31663210 if exprOnly then
31673211 if in.indentSyntax && in.isAfterLineEnd && in.token != INDENT then
3168- warning(em """ Misleading indentation: this expression forms part of the preceding catch case.
3212+ warning(em """ Misleading indentation: this expression forms part of the preceding case.
31693213 |If this is intended, it should be indented for clarity.
3170- |Otherwise, if the handler is intended to be empty, use a multi-line catch with
3214+ |Otherwise, if the handler is intended to be empty, use a multi-line match or catch with
31713215 |an indented case. """ )
31723216 expr()
31733217 else block()
@@ -3184,6 +3228,9 @@ object Parsers {
31843228 CaseDef (pat, grd1, body)
31853229 }
31863230
3231+ def singleCaseMatch () =
3232+ Match (EmptyTree , caseClause(exprOnly = true ) :: Nil )
3233+ 31873234 /** TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi]
31883235 */
31893236 def typeCaseClause (): CaseDef = atSpan(in.offset) {
0 commit comments