Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always enable fewerBraces #15261

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion community-build/community-projects/zio
314 changes: 190 additions & 124 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Large diffs are not rendered by default.

41 changes: 30 additions & 11 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,10 @@ object Scanners {
val indentSyntax =
((if (Config.defaultIndent) !noindentSyntax else ctx.settings.indent.value)
|| rewriteNoIndent)
&& !isInstanceOf[LookaheadScanner]
&& { this match
case self: LookaheadScanner => self.allowIndent
case _ => true
}

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

private inline val fewerBracesByDefault = true
// turn on to study impact on codebase if `fewerBraces` was the default

private var fewerBracesEnabledCache = false
private var fewerBracesEnabledCtx: Context = NoContext

def fewerBracesEnabled =
if fewerBracesEnabledCtx ne myLanguageImportContext then
fewerBracesEnabledCache = featureEnabled(Feature.fewerBraces)
fewerBracesEnabledCache =
featureEnabled(Feature.fewerBraces)
|| fewerBracesByDefault && indentSyntax
fewerBracesEnabledCtx = myLanguageImportContext
fewerBracesEnabledCache

Expand Down Expand Up @@ -610,9 +618,13 @@ object Scanners {
if next.token != COLON then
handleNewIndentWidth(r.enclosing, ir =>
errorButContinue(
i"""The start of this line does not match any of the previous indentation widths.
|Indentation width of current line : $nextWidth
|This falls between previous widths: ${ir.width} and $lastWidth"""))
if r.indentWidth == IndentWidth.Max then
i"""Enclosing expression is nested in a one line lambda expression following `:`.
|It may not spill over to a new line."""
else
i"""The start of this line does not match any of the previous indentation widths.
|Indentation width of current line : $nextWidth
|This falls between previous widths: ${ir.width} and $lastWidth"""))
case r =>
if skipping then
if r.enclosing.isClosedByUndentAt(nextWidth) then
Expand Down Expand Up @@ -653,7 +665,9 @@ object Scanners {
currentRegion = Indented(nextWidth, COLONEOL, currentRegion)
offset = next.offset
token = INDENT
end observeIndented

def insertMaxIndent() =
currentRegion = Indented(IndentWidth.Max, ARROW, currentRegion)

/** Insert an <outdent> token if next token closes an indentation region.
* Exception: continue if indentation region belongs to a `match` and next token is `case`.
Expand Down Expand Up @@ -1067,7 +1081,7 @@ object Scanners {
reset()
next

class LookaheadScanner() extends Scanner(source, offset) {
class LookaheadScanner(val allowIndent: Boolean = false) extends Scanner(source, offset) {
override def languageImportContext = Scanner.this.languageImportContext
}

Expand Down Expand Up @@ -1643,25 +1657,29 @@ object Scanners {
enum IndentWidth {
case Run(ch: Char, n: Int)
case Conc(l: IndentWidth, r: Run)
case Max // delimits one-line lambdas following a `:`

def <= (that: IndentWidth): Boolean = this match {
def <= (that: IndentWidth): Boolean = this match
case Run(ch1, n1) =>
that match {
that match
case Run(ch2, n2) => n1 <= n2 && (ch1 == ch2 || n1 == 0)
case Conc(l, r) => this <= l
}
case Max => true
case Conc(l1, r1) =>
that match {
case Conc(l2, r2) => l1 == l2 && r1 <= r2
case Max => true
case _ => false
}
}
case Max =>
that == Max

def < (that: IndentWidth): Boolean = this <= that && !(that <= this)

def toPrefix: String = this match {
case Run(ch, n) => ch.toString * n
case Conc(l, r) => l.toPrefix ++ r.toPrefix
case Max => "(max >>)"
}

override def toString: String = {
Expand All @@ -1673,6 +1691,7 @@ object Scanners {
this match {
case Run(ch, n) => s"$n ${kind(ch)}${if (n == 1) "" else "s"}"
case Conc(l, r) => s"$l, $r"
case Max => "(max >>)"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/parsing/Tokens.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
3 changes: 2 additions & 1 deletion compiler/test/dotty/tools/repl/ReplCompilerTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ class ReplCompilerTests extends ReplTest:
|""".stripMargin)
assertEquals("val x: Int = 8", storedOutput().trim)
}
/*
initially {
run("""|import language.experimental.fewerBraces
|import language.experimental.fewerBraces as _
Expand All @@ -366,7 +367,7 @@ class ReplCompilerTests extends ReplTest:
|""".stripMargin)
assert("expected error if fewerBraces is unimported",
lines().exists(_.contains("missing arguments for method apply")))
}
}*/

object ReplCompilerTests:

Expand Down
13 changes: 7 additions & 6 deletions docs/_docs/internals/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,13 +254,14 @@ SimpleExpr ::= SimpleRef
| SimpleExpr ‘.’ MatchClause
| SimpleExpr TypeArgs TypeApply(expr, args)
| SimpleExpr ArgumentExprs Apply(expr, args)
| SimpleExpr ‘:’ IndentedExpr -- under language.experimental.fewerBraces
| SimpleExpr FunParams (‘=>’ | ‘?=>’) IndentedExpr -- under language.experimental.fewerBraces
| SimpleExpr ‘:’ ColonArgument -- under language.experimental.fewerBraces
| SimpleExpr ‘_’ PostfixOp(expr, _) (to be dropped)
| XmlExpr -- to be dropped
IndentedExpr ::= indent CaseClauses | Block outdent
Quoted ::= ‘'’ ‘{’ Block ‘}’
| ‘'’ ‘[’ Type ‘]’
| XmlExpr -- to be dropped
ColonArgument ::= indent CaseClauses | Block outdent
| FunParams (‘=>’ | ‘?=>’) ColonArgBody
| HkTypeParamClause ‘=>’ ColonArgBody
ColonArgBody ::= indent (CaseClauses | Block) outdent
| <silent-indent> (CaseClauses | Block) outdent -- silent-indent is inserted by Lexer if no real indent is found
ExprsInParens ::= ExprInParens {‘,’ ExprInParens}
ExprInParens ::= PostfixExpr ‘:’ Type -- normal Expr allows only RefinedType here
| Expr
Expand Down
44 changes: 25 additions & 19 deletions docs/_docs/reference/experimental/fewer-braces.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import language.experimental.fewerBraces
```
Alternatively, it can be enabled with command line option `-language:experimental.fewerBraces`.

This variant is more contentious and less stable than the rest of the significant indentation scheme. It allows to replace a function argument in braces by a `:` at the end of a line and indented code, similar to the convention for class bodies. It also allows to leave out braces around arguments that are multi-line function values.
This variant is more contentious and less stable than the rest of the significant indentation scheme. It allows to replace a function argument in braces by a `:` at the end of a line and indented code, similar to the convention for class bodies. The `:` can
optionally be followed by a lambda.

## Using `:` At End Of Line

Expand Down Expand Up @@ -50,34 +51,39 @@ val firstLine = files.get(fileName).fold:

## Lambda Arguments Without Braces

Braces can also be omitted around multiple line function value arguments:
The `:` can optionally be followed by the parameter list of a function literal:
```scala
val xs = elems.map x =>
val xs = elems.map: x =>
val y = x - 1
y * y
xs.foldLeft (x, y) =>
x + y
```
Braces can be omitted if the lambda starts with a parameter list and `=>` or `=>?` at the end of one line and it has an indented body on the following lines.
Braces can be omitted if the lambda starts with a parameter list and an arrow symbol `=>` or `?=>`. The arrow is followed by the body of the functional literal, which can be
either on the same line or as an indented block on the following lines. Example:
```scala
val xs = elems
.map: x => x * x
.foldLeft (x, y) => x = y
```

## Syntax Changes

As a lexical change, a `:` at the end of a line is now always treated as a
"colon at end of line" token.

The context free grammar changes as follows:
```
SimpleExpr ::= ...
| SimpleExpr `:` IndentedArgument
| SimpleExpr FunParams (‘=>’ | ‘?=>’) IndentedArgument
InfixExpr ::= ...
| InfixExpr id `:` IndentedArgument
IndentedArgument ::= indent (CaseClauses | Block) outdent
```
| SimpleExpr ‘:’ ColonArgument

Note that a lambda argument must have the `=>` at the end of a line for braces
to be optional. For instance, the following would also be incorrect:

```scala
xs.map x => x + 1 // error: braces or parentheses are required
```
The lambda has to be enclosed in braces or parentheses:
```scala
xs.map(x => x + 1) // ok
| SimpleExpr FunParams (‘=>’ | ‘?=>’) IndentedArgument
ColonArgument ::= indent CaseClauses | Block outdent
| FunParams (‘=>’ | ‘?=>’) ColonArgBody
| HkTypeParamClause ‘=>’ ColonArgBody
ColonArgBody ::= indent (CaseClauses | Block) outdent
| <silent-indent> (CaseClauses | Block) outdent --
```
The last line is understood as follows: If the token following a `=>` or `?=>` in a
`ColonArgument` is not an `indent`, then the parser inserts a silent indent token
and assumes the associated indentation region has maximal indentation width.
10 changes: 5 additions & 5 deletions tests/neg-custom-args/nowarn/nowarn.check
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ Matching filters for @nowarn or -Wconf:
| ^
| method f is deprecated
-- Deprecation Warning: tests/neg-custom-args/nowarn/nowarn.scala:47:10 ------------------------------------------------
47 |def t7c = f: // warning (deprecation)
47 |def t7c = f // warning (deprecation)
| ^
| method f is deprecated
-- Unchecked Warning: tests/neg-custom-args/nowarn/nowarn.scala:53:7 ---------------------------------------------------
Expand All @@ -78,10 +78,10 @@ Matching filters for @nowarn or -Wconf:
40 |@nowarn("msg=fish") def t6d = f // error (unused nowarn), warning (deprecation)
|^^^^^^^^^^^^^^^^^^^
|@nowarn annotation does not suppress any warnings
-- Error: tests/neg-custom-args/nowarn/nowarn.scala:48:3 ---------------------------------------------------------------
48 | @nowarn("msg=fish") // error (unused nowarn)
| ^^^^^^^^^^^^^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Error: tests/neg-custom-args/nowarn/nowarn.scala:48:5 ---------------------------------------------------------------
48 | : @nowarn("msg=fish") // error (unused nowarn)
| ^^^^^^^^^^^^^^^^^^^
| @nowarn annotation does not suppress any warnings
-- Error: tests/neg-custom-args/nowarn/nowarn.scala:60:0 ---------------------------------------------------------------
60 |@nowarn def t9a = { 1: @nowarn; 2 } // error (outer @nowarn is unused)
|^^^^^^^
Expand Down
8 changes: 4 additions & 4 deletions tests/neg-custom-args/nowarn/nowarn.scala
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ def t6a = f // warning (refchecks, deprecation)
@nowarn def t6f = f

def t7a = f: @nowarn("cat=deprecation")
def t7b = f:
@nowarn("msg=deprecated")
def t7c = f: // warning (deprecation)
@nowarn("msg=fish") // error (unused nowarn)
def t7b = f
: @nowarn("msg=deprecated")
def t7c = f // warning (deprecation)
: @nowarn("msg=fish") // error (unused nowarn)
def t7d = f: @nowarn("")
def t7e = f: @nowarn

Expand Down
11 changes: 8 additions & 3 deletions tests/neg/closure-args.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import language.experimental.fewerBraces

val x = List().map (x: => Int) => // error
val x = List().map: (x: => Int) => // error
???
val y = List() map x => // error
val y = List() map: x => // error
x + 1 // error
val z = List() map + => // error
val z = List().map: + => // ok
???

val xs = List(1)
val d = xs
.map: x => x.toString + xs.dropWhile:
y => y > 0 // error

2 changes: 1 addition & 1 deletion tests/neg/cycles.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
12 changes: 12 additions & 0 deletions tests/neg/fun-ascription.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
-- [E081] Type Error: tests/neg/fun-ascription.scala:2:13 --------------------------------------------------------------
2 |val x1 = (f: Int => Int) // error
| ^^^
| Missing parameter type
|
| I could not infer the type of the parameter Int.
-- [E081] Type Error: tests/neg/fun-ascription.scala:3:12 --------------------------------------------------------------
3 |val x2 = f: Int => Int // error
| ^^^
| Missing parameter type
|
| I could not infer the type of the parameter Int.
3 changes: 3 additions & 0 deletions tests/neg/fun-ascription.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def f[T](x: T): T = x
val x1 = (f: Int => Int) // error
val x2 = f: Int => Int // error
12 changes: 4 additions & 8 deletions tests/neg/i13769.check
Original file line number Diff line number Diff line change
@@ -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`
2 changes: 1 addition & 1 deletion tests/neg/i13769.scala
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion tests/neg/i1424.scala
Original file line number Diff line number Diff line change
@@ -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
}
4 changes: 2 additions & 2 deletions tests/neg/i4373b.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ==> 05bef7805687ba94da37177f7568e3ba7da1f91c.scala <==
class x0 {
x1: // error
x0 | _ // error
x1: // error
x0 | _
// error
2 changes: 1 addition & 1 deletion tests/neg/i7751.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import language.experimental.fewerBraces
val a = Some(a=a,)=> // error // error // error
val a = Some(a=a,)=> // error // error
val a = Some(x=y,)=>
2 changes: 1 addition & 1 deletion tests/neg/i7818.scala
Original file line number Diff line number Diff line change
@@ -1 +1 @@
def foo = (x: @) => () // error // error
def foo = (x: @) => () // error
1 change: 0 additions & 1 deletion tests/neg/parser-stability-16.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@ class x0[x0] {
}
trait x3 extends x0 { // error
x1 = 0 object // error // error
// error
2 changes: 1 addition & 1 deletion tests/neg/parser-stability-5.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
trait x0 {
x1 : { // error
x1 : {
var x2
// error
Loading