Skip to content

Commit a668041

Browse files
committed
Change fewerBraces to always require colon
1 parent f35df41 commit a668041

File tree

9 files changed

+153
-109
lines changed

9 files changed

+153
-109
lines changed

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+76-73
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ object Parsers {
4343
enum Location(val inParens: Boolean, val inPattern: Boolean, val inArgs: Boolean):
4444
case InParens extends Location(true, false, false)
4545
case InArgs extends Location(true, false, true)
46+
case InColonArg extends Location(false, false, true)
4647
case InPattern extends Location(false, true, false)
4748
case InGuard extends Location(false, false, false)
4849
case InPatternArgs extends Location(false, true, true) // InParens not true, since it might be an alternative
@@ -431,7 +432,7 @@ object Parsers {
431432
convertToParam(t, mods) :: Nil
432433
case Tuple(ts) =>
433434
ts.map(convertToParam(_, mods))
434-
case t: Typed =>
435+
case t @ Typed(Ident(_), _) =>
435436
report.errorOrMigrationWarning(
436437
em"parentheses are required around the parameter of a lambda${rewriteNotice()}",
437438
in.sourcePos(), from = `3.0`)
@@ -444,22 +445,24 @@ object Parsers {
444445

445446
/** Convert tree to formal parameter
446447
*/
447-
def convertToParam(tree: Tree, mods: Modifiers, expected: String = "formal parameter"): ValDef = tree match
448-
case param: ValDef =>
449-
param.withMods(param.mods | mods.flags)
450-
case id @ Ident(name) =>
451-
makeParameter(name.asTermName, TypeTree(), mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
452-
// the following three cases are needed only for 2.x parameters without enclosing parentheses
453-
case Typed(_, tpt: TypeBoundsTree) =>
454-
syntaxError(s"not a legal $expected", tree.span)
455-
makeParameter(nme.ERROR, tree, mods)
456-
case Typed(id @ Ident(name), tpt) =>
457-
makeParameter(name.asTermName, tpt, mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
458-
case Typed(Splice(Ident(name)), tpt) =>
459-
makeParameter(("$" + name).toTermName, tpt, mods).withSpan(tree.span)
460-
case _ =>
461-
syntaxError(s"not a legal $expected", tree.span)
448+
def convertToParam(tree: Tree, mods: Modifiers): ValDef =
449+
def fail() =
450+
syntaxError(s"not a legal formal parameter for a function literal", tree.span)
462451
makeParameter(nme.ERROR, tree, mods)
452+
tree match
453+
case param: ValDef =>
454+
param.withMods(param.mods | mods.flags)
455+
case id @ Ident(name) =>
456+
makeParameter(name.asTermName, TypeTree(), mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
457+
// the following three cases are needed only for 2.x parameters without enclosing parentheses
458+
case Typed(_, tpt: TypeBoundsTree) =>
459+
fail()
460+
case Typed(id @ Ident(name), tpt) =>
461+
makeParameter(name.asTermName, tpt, mods, isBackquoted = isBackquoted(id)).withSpan(tree.span)
462+
case Typed(Splice(Ident(name)), tpt) =>
463+
makeParameter(("$" + name).toTermName, tpt, mods).withSpan(tree.span)
464+
case _ =>
465+
fail()
463466

464467
/** Convert (qual)ident to type identifier
465468
*/
@@ -893,9 +896,8 @@ object Parsers {
893896
val next = in.lookahead.token
894897
next == LBRACKET || next == LPAREN
895898

896-
897899
def followingIsSelfType() =
898-
val lookahead = in.LookaheadScanner()
900+
val lookahead = in.LookaheadScanner(allowIndent = true)
899901
lookahead.nextToken()
900902
lookahead.token == COLON
901903
&& {
@@ -917,10 +919,10 @@ object Parsers {
917919
}
918920
}
919921

920-
/** When encountering a `:`, is that in the first binding of a lambda?
921-
* @pre location of the enclosing expression is `InParens`, so there is am open `(`.
922+
/** When encountering a `:`, is that in the binding of a lambda?
923+
* @pre location of the enclosing expression is `InParens`, so there is an open `(`.
922924
*/
923-
def followingisLambdaParams() =
925+
def followingIsLambdaParams() =
924926
val lookahead = in.LookaheadScanner()
925927
lookahead.nextToken()
926928
while lookahead.token != RPAREN && lookahead.token != EOF do
@@ -932,6 +934,28 @@ object Parsers {
932934
lookahead.isArrow
933935
}
934936

937+
/** Is the token sequence following the current `:` token classified as a lambda?
938+
* This is the case if the input starts with an identifier, a wildcard, or
939+
* something enclosed in (...) or [...], and this is followed by a `=>` or `?=>`
940+
* and an INDENT.
941+
*/
942+
def followingIsLambdaAfterColon(): Boolean =
943+
val lookahead = in.LookaheadScanner(allowIndent = true)
944+
def isArrowIndent() =
945+
lookahead.isArrow
946+
&& {
947+
lookahead.nextToken()
948+
lookahead.token == INDENT
949+
}
950+
lookahead.nextToken()
951+
if lookahead.isIdent || lookahead.token == USCORE then
952+
lookahead.nextToken()
953+
isArrowIndent()
954+
else if lookahead.token == LPAREN || lookahead.token == LBRACKET then
955+
lookahead.skipParens()
956+
isArrowIndent()
957+
else false
958+
935959
/* --------- OPERAND/OPERATOR STACK --------------------------------------- */
936960

937961
var opStack: List[OpInfo] = Nil
@@ -2226,7 +2250,11 @@ object Parsers {
22262250
in.nextToken()
22272251
else
22282252
accept(ARROW)
2229-
Function(params, if (location == Location.InBlock) block() else expr())
2253+
val body =
2254+
if location == Location.InBlock then block()
2255+
else if location == Location.InColonArg && in.token == INDENT then blockExpr()
2256+
else expr()
2257+
Function(params, body)
22302258
}
22312259

22322260
/** PostfixExpr ::= InfixExpr [id [nl]]
@@ -2276,9 +2304,11 @@ object Parsers {
22762304
* | SimpleExpr `.` MatchClause
22772305
* | SimpleExpr (TypeArgs | NamedTypeArgs)
22782306
* | SimpleExpr1 ArgumentExprs
2279-
* | SimpleExpr1 `:` IndentedExpr -- under language.experimental.fewerBraces
2280-
* | SimpleExpr1 FunParams (‘=>’ | ‘?=>’) IndentedExpr -- under language.experimental.fewerBraces
2281-
* IndentedExpr ::= indent (CaseClauses | Block) outdent
2307+
* | SimpleExpr1 `:` ColonArgument -- under language.experimental.fewerBraces
2308+
* ColonArgument ::= indent (CaseClauses | Block) outdent
2309+
* | FunParams (‘=>’ | ‘?=>’) ColonArgBody
2310+
* | HkTypeParamClause ‘=>’ ColonArgBody
2311+
* ColonArgBody ::= indent (CaseClauses | Block) outdent
22822312
* Quoted ::= ‘'’ ‘{’ Block ‘}’
22832313
* | ‘'’ ‘[’ Type ‘]’
22842314
*/
@@ -2334,65 +2364,38 @@ object Parsers {
23342364
simpleExprRest(t, location, canApply)
23352365
}
23362366

2337-
def simpleExprRest(t: Tree, location: Location, canApply: Boolean = true): Tree = {
2367+
def simpleExprRest(t: Tree, location: Location, canApply: Boolean = true): Tree =
23382368
if (canApply) argumentStart()
2339-
in.token match {
2369+
in.token match
23402370
case DOT =>
23412371
in.nextToken()
23422372
simpleExprRest(selectorOrMatch(t), location, canApply = true)
23432373
case LBRACKET =>
23442374
val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) }
23452375
simpleExprRest(tapp, location, canApply = true)
2346-
case LPAREN if canApply =>
2347-
val app = atSpan(startOffset(t), in.offset) {
2348-
val argExprs @ (args, isUsing) = argumentExprs()
2349-
if !isUsing && in.isArrow && location != Location.InGuard && in.fewerBracesEnabled then
2350-
val params = convertToParams(Tuple(args))
2351-
if params.forall(_.name != nme.ERROR) then
2352-
applyToClosure(t, in.offset, params)
2353-
else
2354-
mkApply(t, argExprs)
2355-
else
2356-
mkApply(t, argExprs)
2357-
}
2358-
simpleExprRest(app, location, canApply = true)
2359-
case LBRACE | INDENT if canApply =>
2376+
case LPAREN | LBRACE | INDENT if canApply =>
23602377
val app = atSpan(startOffset(t), in.offset) { mkApply(t, argumentExprs()) }
23612378
simpleExprRest(app, location, canApply = true)
23622379
case USCORE =>
2363-
if in.lookahead.isArrow && location != Location.InGuard && in.fewerBracesEnabled then
2364-
val app = applyToClosure(t, in.offset, convertToParams(wildcardIdent()))
2365-
simpleExprRest(app, location, canApply = true)
2366-
else
2367-
atSpan(startOffset(t), in.skipToken()) { PostfixOp(t, Ident(nme.WILDCARD)) }
2368-
case IDENTIFIER
2369-
if !in.isOperator && in.lookahead.isArrow && location != Location.InGuard && in.fewerBracesEnabled =>
2370-
val app = applyToClosure(t, in.offset, convertToParams(termIdent()))
2371-
simpleExprRest(app, location, canApply = true)
2380+
atSpan(startOffset(t), in.skipToken()) { PostfixOp(t, Ident(nme.WILDCARD)) }
23722381
case _ =>
2373-
t match
2374-
case id @ Ident(name)
2375-
if in.isColon() && location == Location.InParens && followingisLambdaParams() =>
2376-
if name.is(WildcardParamName) then
2377-
assert(name == placeholderParams.head.name)
2378-
placeholderParams = placeholderParams.tail
2379-
atSpan(startOffset(id)) {
2380-
makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id))
2381-
}
2382-
case _ =>
2383-
t
2384-
}
2385-
}
2386-
2387-
def applyToClosure(t: Tree, start: Offset, params: List[ValDef]): Tree =
2388-
atSpan(startOffset(t), in.offset) {
2389-
val arg = atSpan(start, in.skipToken()) {
2390-
if in.token != INDENT then
2391-
syntaxErrorOrIncomplete(i"indented expression expected, ${in} found")
2392-
Function(params, blockExpr())
2393-
}
2394-
Apply(t, arg)
2395-
}
2382+
if in.isColon() && location == Location.InParens && followingIsLambdaParams() then
2383+
t match
2384+
case id @ Ident(name) =>
2385+
if name.is(WildcardParamName) then
2386+
assert(name == placeholderParams.head.name)
2387+
placeholderParams = placeholderParams.tail
2388+
atSpan(startOffset(id)) {
2389+
makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id))
2390+
}
2391+
case _ => t
2392+
else if in.fewerBracesEnabled && in.token == COLON && followingIsLambdaAfterColon() then
2393+
val app = atSpan(startOffset(t), in.skipToken()) {
2394+
Apply(t, expr(Location.InColonArg) :: Nil)
2395+
}
2396+
simpleExprRest(app, location, canApply = true)
2397+
else t
2398+
end simpleExprRest
23962399

23972400
/** SimpleExpr ::= ‘new’ ConstrApp {`with` ConstrApp} [TemplateBody]
23982401
* | ‘new’ TemplateBody

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

+11-3
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,10 @@ object Scanners {
189189
val indentSyntax =
190190
((if (Config.defaultIndent) !noindentSyntax else ctx.settings.indent.value)
191191
|| rewriteNoIndent)
192-
&& !isInstanceOf[LookaheadScanner]
192+
&& { this match
193+
case self: LookaheadScanner => self.allowIndent
194+
case _ => true
195+
}
193196

194197
if (rewrite) {
195198
val s = ctx.settings
@@ -206,12 +209,17 @@ object Scanners {
206209
def featureEnabled(name: TermName) = Feature.enabled(name)(using languageImportContext)
207210
def erasedEnabled = featureEnabled(Feature.erasedDefinitions)
208211

212+
private inline val fewerBracesByDefault = false
213+
// turn on to study impact on codebase if `fewerBraces` was the default
214+
209215
private var fewerBracesEnabledCache = false
210216
private var fewerBracesEnabledCtx: Context = NoContext
211217

212218
def fewerBracesEnabled =
213219
if fewerBracesEnabledCtx ne myLanguageImportContext then
214-
fewerBracesEnabledCache = featureEnabled(Feature.fewerBraces)
220+
fewerBracesEnabledCache =
221+
featureEnabled(Feature.fewerBraces)
222+
|| fewerBracesByDefault && indentSyntax
215223
fewerBracesEnabledCtx = myLanguageImportContext
216224
fewerBracesEnabledCache
217225

@@ -1067,7 +1075,7 @@ object Scanners {
10671075
reset()
10681076
next
10691077

1070-
class LookaheadScanner() extends Scanner(source, offset) {
1078+
class LookaheadScanner(val allowIndent: Boolean = false) extends Scanner(source, offset) {
10711079
override def languageImportContext = Scanner.this.languageImportContext
10721080
}
10731081

docs/_docs/internals/syntax.md

+6-6
Original file line numberDiff line numberDiff line change
@@ -254,13 +254,13 @@ SimpleExpr ::= SimpleRef
254254
| SimpleExpr ‘.’ MatchClause
255255
| SimpleExpr TypeArgs TypeApply(expr, args)
256256
| SimpleExpr ArgumentExprs Apply(expr, args)
257-
| SimpleExpr ‘:’ IndentedExpr -- under language.experimental.fewerBraces
258-
| SimpleExpr FunParams (‘=>’ | ‘?=>’) IndentedExpr -- under language.experimental.fewerBraces
257+
| SimpleExpr ‘:’ ColonArgument -- under language.experimental.fewerBraces
259258
| SimpleExpr ‘_’ PostfixOp(expr, _) (to be dropped)
260-
| XmlExpr -- to be dropped
261-
IndentedExpr ::= indent CaseClauses | Block outdent
262-
Quoted ::= ‘'’ ‘{’ Block ‘}’
263-
| ‘'’ ‘[’ Type ‘]’
259+
| XmlExpr -- to be dropped
260+
ColonArgument ::= indent CaseClauses | Block outdent
261+
| FunParams (‘=>’ | ‘?=>’) ColonArgBody
262+
| HkTypeParamClause ‘=>’ ColonArgBody
263+
ColonArgBody ::= indent (CaseClauses | Block) outdent
264264
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
265265
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
266266
| Expr

tests/neg-custom-args/nowarn/nowarn.check

+5-5
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Matching filters for @nowarn or -Wconf:
6363
| ^
6464
| method f is deprecated
6565
-- Deprecation Warning: tests/neg-custom-args/nowarn/nowarn.scala:47:10 ------------------------------------------------
66-
47 |def t7c = f: // warning (deprecation)
66+
47 |def t7c = f // warning (deprecation)
6767
| ^
6868
| method f is deprecated
6969
-- Unchecked Warning: tests/neg-custom-args/nowarn/nowarn.scala:53:7 ---------------------------------------------------
@@ -78,10 +78,10 @@ Matching filters for @nowarn or -Wconf:
7878
40 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation)
7979
|^^^^^^^^^^^^^^^^^^^
8080
|@nowarn annotation does not suppress any warnings
81-
-- Error: tests/neg-custom-args/nowarn/nowarn.scala:48:3 ---------------------------------------------------------------
82-
48 | @nowarn("msg=fish") // error (unused nowarn)
83-
| ^^^^^^^^^^^^^^^^^^^
84-
| @nowarn annotation does not suppress any warnings
81+
-- Error: tests/neg-custom-args/nowarn/nowarn.scala:48:5 ---------------------------------------------------------------
82+
48 | : @nowarn("msg=fish") // error (unused nowarn)
83+
| ^^^^^^^^^^^^^^^^^^^
84+
| @nowarn annotation does not suppress any warnings
8585
-- Error: tests/neg-custom-args/nowarn/nowarn.scala:60:0 ---------------------------------------------------------------
8686
60 |@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused)
8787
|^^^^^^^

tests/neg-custom-args/nowarn/nowarn.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,10 @@ def t6a = f // warning (refchecks, deprecation)
4242
@nowarn def t6f = f
4343

4444
def t7a = f: @nowarn("cat=deprecation")
45-
def t7b = f:
46-
@nowarn("msg=deprecated")
47-
def t7c = f: // warning (deprecation)
48-
@nowarn("msg=fish") // error (unused nowarn)
45+
def t7b = f
46+
: @nowarn("msg=deprecated")
47+
def t7c = f // warning (deprecation)
48+
: @nowarn("msg=fish") // error (unused nowarn)
4949
def t7d = f: @nowarn("")
5050
def t7e = f: @nowarn
5151

tests/neg/closure-args.scala

+19-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,25 @@
11
import language.experimental.fewerBraces
22

3-
val x = List().map (x: => Int) => // error
3+
val x = List().map: (x: => Int) => // error
44
???
5-
val y = List() map x => // error
5+
val y = List() map: x => // error
66
x + 1 // error
7-
val z = List() map + => // error
7+
val z = List().map: + => // ok
88
???
99

10+
val xs = List(1)
11+
val b: Int = xs // error
12+
.map: x => x * x // error
13+
.filter: y => y > 0 // error
14+
(0)
15+
val d = xs // error
16+
.map: x => x.toString + xs.dropWhile:
17+
y => y > 0
18+
19+
val c = List(xs.map: y => y + y) // error // error
20+
val d2: String = xs // error
21+
.map: x => x.toString + xs.dropWhile: y => y > 0 // error // error
22+
.filter: z => !z.isEmpty // error
23+
(0)
24+
25+
val fs: List[List[Int] => Int] = xs.map: x => case y :: ys => y case Nil => -1 // error // error

tests/neg/i7751.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
import language.experimental.fewerBraces
2-
val a = Some(a=a,)=> // error // error // error
2+
val a = Some(a=a,)=> // error // error
33
val a = Some(x=y,)=>

0 commit comments

Comments
 (0)