Skip to content

Commit 421bdd6

Browse files
committed
Change fewerBraces to always require colon
1 parent d64c6dc commit 421bdd6

File tree

9 files changed

+151
-107
lines changed

9 files changed

+151
-107
lines changed

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

Lines changed: 74 additions & 71 deletions
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,20 +445,22 @@ 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 _ =>
459-
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)
460451
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 _ =>
463+
fail()
461464

462465
/** Convert (qual)ident to type identifier
463466
*/
@@ -891,9 +894,8 @@ object Parsers {
891894
val next = in.lookahead.token
892895
next == LBRACKET || next == LPAREN
893896

894-
895897
def followingIsSelfType() =
896-
val lookahead = in.LookaheadScanner()
898+
val lookahead = in.LookaheadScanner(allowIndent = true)
897899
lookahead.nextToken()
898900
lookahead.token == COLON
899901
&& {
@@ -915,10 +917,10 @@ object Parsers {
915917
}
916918
}
917919

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

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

935959
var opStack: List[OpInfo] = Nil
@@ -2235,7 +2259,11 @@ object Parsers {
22352259
in.nextToken()
22362260
else
22372261
accept(ARROW)
2238-
Function(params, if (location == Location.InBlock) block() else expr())
2262+
val body =
2263+
if location == Location.InBlock then block()
2264+
else if location == Location.InColonArg && in.token == INDENT then blockExpr()
2265+
else expr()
2266+
Function(params, body)
22392267
}
22402268

22412269
/** PostfixExpr ::= InfixExpr [id [nl]]
@@ -2285,9 +2313,11 @@ object Parsers {
22852313
* | SimpleExpr `.` MatchClause
22862314
* | SimpleExpr (TypeArgs | NamedTypeArgs)
22872315
* | SimpleExpr1 ArgumentExprs
2288-
* | SimpleExpr1 `:` IndentedExpr -- under language.experimental.fewerBraces
2289-
* | SimpleExpr1 FunParams (‘=>’ | ‘?=>’) IndentedExpr -- under language.experimental.fewerBraces
2290-
* IndentedExpr ::= indent (CaseClauses | Block) outdent
2316+
* | SimpleExpr1 `:` ColonArgument -- under language.experimental.fewerBraces
2317+
* ColonArgument ::= indent (CaseClauses | Block) outdent
2318+
* | FunParams (‘=>’ | ‘?=>’) ColonArgBody
2319+
* | HkTypeParamClause ‘=>’ ColonArgBody
2320+
* ColonArgBody ::= indent (CaseClauses | Block) outdent
22912321
* Quoted ::= ‘'’ ‘{’ Block ‘}’
22922322
* | ‘'’ ‘[’ Type ‘]’
22932323
*/
@@ -2343,65 +2373,38 @@ object Parsers {
23432373
simpleExprRest(t, location, canApply)
23442374
}
23452375

2346-
def simpleExprRest(t: Tree, location: Location, canApply: Boolean = true): Tree = {
2376+
def simpleExprRest(t: Tree, location: Location, canApply: Boolean = true): Tree =
23472377
if (canApply) argumentStart()
2348-
in.token match {
2378+
in.token match
23492379
case DOT =>
23502380
in.nextToken()
23512381
simpleExprRest(selectorOrMatch(t), location, canApply = true)
23522382
case LBRACKET =>
23532383
val tapp = atSpan(startOffset(t), in.offset) { TypeApply(t, typeArgs(namedOK = true, wildOK = false)) }
23542384
simpleExprRest(tapp, location, canApply = true)
2355-
case LPAREN if canApply =>
2356-
val app = atSpan(startOffset(t), in.offset) {
2357-
val argExprs @ (args, isUsing) = argumentExprs()
2358-
if !isUsing && in.isArrow && location != Location.InGuard && in.fewerBracesEnabled then
2359-
val params = convertToParams(Tuple(args))
2360-
if params.forall(_.name != nme.ERROR) then
2361-
applyToClosure(t, in.offset, params)
2362-
else
2363-
mkApply(t, argExprs)
2364-
else
2365-
mkApply(t, argExprs)
2366-
}
2367-
simpleExprRest(app, location, canApply = true)
2368-
case LBRACE | INDENT if canApply =>
2385+
case LPAREN | LBRACE | INDENT if canApply =>
23692386
val app = atSpan(startOffset(t), in.offset) { mkApply(t, argumentExprs()) }
23702387
simpleExprRest(app, location, canApply = true)
23712388
case USCORE =>
2372-
if in.lookahead.isArrow && location != Location.InGuard && in.fewerBracesEnabled then
2373-
val app = applyToClosure(t, in.offset, convertToParams(wildcardIdent()))
2374-
simpleExprRest(app, location, canApply = true)
2375-
else
2376-
atSpan(startOffset(t), in.skipToken()) { PostfixOp(t, Ident(nme.WILDCARD)) }
2377-
case IDENTIFIER
2378-
if !in.isOperator && in.lookahead.isArrow && location != Location.InGuard && in.fewerBracesEnabled =>
2379-
val app = applyToClosure(t, in.offset, convertToParams(termIdent()))
2380-
simpleExprRest(app, location, canApply = true)
2389+
atSpan(startOffset(t), in.skipToken()) { PostfixOp(t, Ident(nme.WILDCARD)) }
23812390
case _ =>
2382-
t match
2383-
case id @ Ident(name)
2384-
if in.isColon() && location == Location.InParens && followingisLambdaParams() =>
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 _ =>
2392-
t
2393-
}
2394-
}
2395-
2396-
def applyToClosure(t: Tree, start: Offset, params: List[ValDef]): Tree =
2397-
atSpan(startOffset(t), in.offset) {
2398-
val arg = atSpan(start, in.skipToken()) {
2399-
if in.token != INDENT then
2400-
syntaxErrorOrIncomplete(i"indented expression expected, ${in} found")
2401-
Function(params, blockExpr())
2402-
}
2403-
Apply(t, arg)
2404-
}
2391+
if in.isColon() && location == Location.InParens && followingIsLambdaParams() then
2392+
t match
2393+
case id @ Ident(name) =>
2394+
if name.is(WildcardParamName) then
2395+
assert(name == placeholderParams.head.name)
2396+
placeholderParams = placeholderParams.tail
2397+
atSpan(startOffset(id)) {
2398+
makeParameter(name.asTermName, typedOpt(), Modifiers(), isBackquoted = isBackquoted(id))
2399+
}
2400+
case _ => t
2401+
else if in.fewerBracesEnabled && in.token == COLON && followingIsLambdaAfterColon() then
2402+
val app = atSpan(startOffset(t), in.skipToken()) {
2403+
Apply(t, expr(Location.InColonArg) :: Nil)
2404+
}
2405+
simpleExprRest(app, location, canApply = true)
2406+
else t
2407+
end simpleExprRest
24052408

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

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

Lines changed: 11 additions & 3 deletions
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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,13 @@ SimpleExpr ::= SimpleRef
253253
| SimpleExpr ‘.’ MatchClause
254254
| SimpleExpr TypeArgs TypeApply(expr, args)
255255
| SimpleExpr ArgumentExprs Apply(expr, args)
256-
| SimpleExpr ‘:’ IndentedExpr -- under language.experimental.fewerBraces
257-
| SimpleExpr FunParams (‘=>’ | ‘?=>’) IndentedExpr -- under language.experimental.fewerBraces
256+
| SimpleExpr ‘:’ ColonArgument -- under language.experimental.fewerBraces
258257
| SimpleExpr ‘_’ PostfixOp(expr, _) (to be dropped)
259-
| XmlExpr -- to be dropped
260-
IndentedExpr ::= indent CaseClauses | Block outdent
261-
Quoted ::= ‘'’ ‘{’ Block ‘}’
262-
| ‘'’ ‘[’ Type ‘]’
258+
| XmlExpr -- to be dropped
259+
ColonArgument ::= indent CaseClauses | Block outdent
260+
| FunParams (‘=>’ | ‘?=>’) ColonArgBody
261+
| HkTypeParamClause ‘=>’ ColonArgBody
262+
ColonArgBody ::= indent (CaseClauses | Block) outdent
263263
ExprSplice ::= spliceId -- if inside quoted block
264264
| ‘$’ ‘{’ Block ‘}’ -- unless inside quoted pattern
265265
| ‘$’ ‘{’ Pattern ‘}’ -- when inside quoted pattern

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

Lines changed: 5 additions & 5 deletions
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

Lines changed: 4 additions & 4 deletions
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

Lines changed: 19 additions & 3 deletions
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

Lines changed: 1 addition & 1 deletion
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)