Skip to content

Commit

Permalink
Make fewerBraces a standard feature
Browse files Browse the repository at this point in the history
Enables fewerBraces as a standard feature from 3.3 on. No language
import is needed.
  • Loading branch information
odersky committed Nov 8, 2022
1 parent 275cfa8 commit 43e3b7c
Show file tree
Hide file tree
Showing 23 changed files with 87 additions and 116 deletions.
6 changes: 5 additions & 1 deletion compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,11 @@ object Feature:
case Some(v) => v
case none => sourceVersionSetting

def migrateTo3(using Context): Boolean = sourceVersion == `3.0-migration`
def migrateTo3(using Context): Boolean =
sourceVersion == `3.0-migration`

def fewerBracesEnabled(using Context) =
sourceVersion.isAtLeast(`3.3`) || enabled(fewerBraces)

/** If current source migrates to `version`, issue given warning message
* and return `true`, otherwise return `false`.
Expand Down
1 change: 0 additions & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ trait AllScalaSettings extends CommonScalaSettings, PluginSettings, VerboseSetti
val oldSyntax: Setting[Boolean] = BooleanSetting("-old-syntax", "Require `(...)` around conditions.")
val indent: Setting[Boolean] = BooleanSetting("-indent", "Together with -rewrite, remove {...} syntax when possible due to significant indentation.")
val noindent: Setting[Boolean] = BooleanSetting("-no-indent", "Require classical {...} syntax, indentation is not significant.", aliases = List("-noindent"))
val YindentColons: Setting[Boolean] = BooleanSetting("-Yindent-colons", "(disabled: use -language:experimental.fewerBraces instead)")

/* Decompiler settings */
val printTasty: Setting[Boolean] = BooleanSetting("-print-tasty", "Prints the raw tasty.", aliases = List("--print-tasty"))
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/SourceVersion.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import util.Property
enum SourceVersion:
case `3.0-migration`, `3.0`, `3.1` // Note: do not add `3.1-migration` here, 3.1 is the same language as 3.0.
case `3.2-migration`, `3.2`
case `3.3-migration`, `3.3`
case `future-migration`, `future`

val isMigrating: Boolean = toString.endsWith("-migration")
Expand Down
16 changes: 8 additions & 8 deletions compiler/src/dotty/tools/dotc/parsing/Parsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -778,15 +778,14 @@ object Parsers {
}
})
canRewrite &= (in.isAfterLineEnd || statCtdTokens.contains(in.token)) // test (5)
if (canRewrite && (!underColonSyntax || in.fewerBracesEnabled)) {
if canRewrite && (!underColonSyntax || Feature.fewerBracesEnabled) then
val openingPatchStr =
if !colonRequired then ""
else if testChar(startOpening - 1, Chars.isOperatorPart(_)) then " :"
else ":"
val (startClosing, endClosing) = closingElimRegion()
patch(source, Span(startOpening, endOpening), openingPatchStr)
patch(source, Span(startClosing, endClosing), "")
}
t
}

Expand Down Expand Up @@ -1025,7 +1024,7 @@ object Parsers {
* body
*/
def isColonLambda =
in.fewerBracesEnabled && in.token == COLONfollow && followingIsLambdaAfterColon()
Feature.fewerBracesEnabled && in.token == COLONfollow && followingIsLambdaAfterColon()

/** operand { infixop operand | MatchClause } [postfixop],
*
Expand Down Expand Up @@ -2370,7 +2369,7 @@ object Parsers {
/** PostfixExpr ::= InfixExpr [id [nl]]
* InfixExpr ::= PrefixExpr
* | InfixExpr id [nl] InfixExpr
* | InfixExpr id `:` IndentedExpr
* | InfixExpr id ColonArgument
* | InfixExpr MatchClause
*/
def postfixExpr(location: Location = Location.ElseWhere): Tree =
Expand Down Expand Up @@ -2414,10 +2413,11 @@ object Parsers {
* | SimpleExpr `.` MatchClause
* | SimpleExpr (TypeArgs | NamedTypeArgs)
* | SimpleExpr1 ArgumentExprs
* | SimpleExpr1 `:` ColonArgument -- under language.experimental.fewerBraces
* ColonArgument ::= indent (CaseClauses | Block) outdent
* | FunParams (‘=>’ | ‘?=>’) ColonArgBody
* | HkTypeParamClause ‘=>’ ColonArgBody
* | SimpleExpr1 ColonArgument
* ColonArgument ::= colon [LambdaStart]
* indent (CaseClauses | Block) outdent
* LambdaStart ::= FunParams (‘=>’ | ‘?=>’)
* | HkTypeParamClause ‘=>’
* ColonArgBody ::= indent (CaseClauses | Block) outdent
* Quoted ::= ‘'’ ‘{’ Block ‘}’
* | ‘'’ ‘[’ Type ‘]’
Expand Down
21 changes: 1 addition & 20 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import scala.collection.mutable
import scala.collection.immutable.SortedMap
import rewrites.Rewrites.patch
import config.Feature
import config.Feature.migrateTo3
import config.Feature.{migrateTo3, fewerBracesEnabled}
import config.SourceVersion.`3.0`
import reporting.{NoProfile, Profile}

Expand Down Expand Up @@ -202,25 +202,6 @@ object Scanners {
def featureEnabled(name: TermName) = Feature.enabled(name)(using languageImportContext)
def erasedEnabled = featureEnabled(Feature.erasedDefinitions)

private inline val fewerBracesByDefault = false
// 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)
|| fewerBracesByDefault && indentSyntax && !migrateTo3
// ensure that fewer braces is not the default for 3.0-migration since
// { x: T =>
// expr
// }
// would be ambiguous
fewerBracesEnabledCtx = myLanguageImportContext
fewerBracesEnabledCache

private var postfixOpsEnabledCache = false
private var postfixOpsEnabledCtx: Context = NoContext

Expand Down
113 changes: 54 additions & 59 deletions docs/_docs/reference/other-new-features/indentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,60 @@ Refinement ::= :<<< [RefineDcl] {semi [RefineDcl]} >>>
Packaging ::= ‘package’ QualId :<<< TopStats >>>
```
## Optional Braces for Method Arguments
Starting with Scala 3.3, a `<colon>` token is also recognized where a function argument would be expected. Examples:
```scala
times(10):
println("ah")
println("ha")
```
or
```scala
credentials `++`:
val file = Path.userHome / ".credentials"
if file.exists
then Seq(Credentials(file))
else Seq()
```
or
```scala
xs.map:
x =>
val y = x - 1
y * y
```
What's more, a `:` in these settings can also be followed on the same line by the parameter part and arrow of a lambda. So the last example could be compressed to this:
```scala
xs.map: x =>
val y = x - 1
y * y
```
and the following would also be legal:
```scala
xs.foldLeft(0): (x, y) =>
x + y
```
The grammar changes for optional braces around arguments are as follows.
```
SimpleExpr ::= ...
| SimpleExpr ColonArgument
InfixExpr ::= ...
| InfixExpr id ColonArgument
ColonArgument ::= colon [LambdaStart]
indent (CaseClauses | Block) outdent
LambdaStart ::= FunParams (‘=>’ | ‘?=>’)
| HkTypeParamClause ‘=>’
```
## Spaces vs Tabs
Indentation prefixes can consist of spaces and/or tabs. Indentation widths are the indentation prefixes themselves, ordered by the string prefix relation. So, so for instance "2 tabs, followed by 4 spaces" is strictly less than "2 tabs, followed by 5 spaces", but "2 tabs, followed by 4 spaces" is incomparable to "6 tabs" or to "4 spaces, followed by 2 tabs". It is an error if the indentation width of some line is incomparable with the indentation width of the region that's current at that point. To avoid such errors, it is a good idea not to mix spaces and tabs in the same source file.
Expand Down Expand Up @@ -448,62 +502,3 @@ indented regions where possible. When invoked with options `-rewrite -no-indent`
The `-indent` option only works on [new-style syntax](./control-syntax.md). So to go from old-style syntax to new-style indented code one has to invoke the compiler twice, first with options `-rewrite -new-syntax`, then again with options
`-rewrite -indent`. To go in the opposite direction, from indented code to old-style syntax, it's `-rewrite -no-indent`, followed by `-rewrite -old-syntax`.

## Variant: Indentation Marker `:` for Arguments

Generally, the possible indentation regions coincide with those regions where braces `{...}` are also legal, no matter whether the braces enclose an expression or a set of definitions. There is one exception, though: Arguments to functions can be enclosed in braces but they cannot be simply indented instead. Making indentation always significant for function arguments would be too restrictive and fragile.

To allow such arguments to be written without braces, a variant of the indentation scheme is implemented under language import
```scala
import language.experimental.fewerBraces
```
In this variant, a `<colon>` token is also recognized where function argument would be expected. Examples:

```scala
times(10):
println("ah")
println("ha")
```

or

```scala
credentials `++`:
val file = Path.userHome / ".credentials"
if file.exists
then Seq(Credentials(file))
else Seq()
```

or

```scala
xs.map:
x =>
val y = x - 1
y * y
```
What's more, a `:` in these settings can also be followed on the same line by the parameter part and arrow of a lambda. So the last example could be compressed to this:

```scala
xs.map: x =>
val y = x - 1
y * y
```
and the following would also be legal:
```scala
xs.foldLeft(0): (x, y) =>
x + y
```

The grammar changes for this variant are as follows.

```
SimpleExpr ::= ...
| SimpleExpr ColonArgument
InfixExpr ::= ...
| InfixExpr id ColonArgument
ColonArgument ::= colon [LambdaStart]
indent (CaseClauses | Block) outdent
LambdaStart ::= FunParams (‘=>|?=>’)
| HkTypeParamClause=>
```
6 changes: 6 additions & 0 deletions docs/_docs/reference/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ Catches ::= ‘catch’ (Expr | ExprCaseClause)
PostfixExpr ::= InfixExpr [id] -- only if language.postfixOperators is enabled
InfixExpr ::= PrefixExpr
| InfixExpr id [nl] InfixExpr
| InfixExpr id ColonArgument
| InfixExpr MatchClause
MatchClause ::= ‘match’ <<< CaseClauses >>>
PrefixExpr ::= [PrefixOperator] SimpleExpr
Expand All @@ -267,6 +268,11 @@ SimpleExpr ::= SimpleRef
| SimpleExpr ‘.’ MatchClause
| SimpleExpr TypeArgs
| SimpleExpr ArgumentExprs
| SimpleExpr ColonArgument
ColonArgument ::= colon [LambdaStart]
indent (CaseClauses | Block) outdent
LambdaStart ::= FunParams (‘=>’ | ‘?=>’)
| HkTypeParamClause ‘=>’
Quoted ::= ‘'’ ‘{’ Block ‘}’
| ‘'’ ‘[’ Type ‘]’
ExprSplice ::= spliceId -- if inside quoted block
Expand Down
4 changes: 2 additions & 2 deletions library/src/scala/runtime/stdLibPatches/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ object language:
/** Experimental support for using indentation for arguments
*/
@compileTimeOnly("`fewerBraces` can only be used at compile time in import statements")
@deprecated("`fewerBraces` is now standard, no language import is needed", since = "3.3")
object fewerBraces

/** Experimental support for typechecked exception capabilities
Expand Down Expand Up @@ -192,7 +193,6 @@ object language:
@compileTimeOnly("`3.2` can only be used at compile time in import statements")
object `3.2`

/* This can be added when we go to 3.3
/** Set source version to 3.3-migration.
*
* @see [[https://docs.scala-lang.org/scala3/guides/migration/compatibility-intro.html]]
Expand All @@ -206,5 +206,5 @@ object language:
*/
@compileTimeOnly("`3.3` can only be used at compile time in import statements")
object `3.3`
*/

end language
Original file line number Diff line number Diff line change
@@ -1,31 +1,27 @@
import annotation.experimental

class Class1:
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
@experimental def f = 1
def g = 1

object Object1:
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
@experimental def f = 1
def g = 1

def fun1 =
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
@experimental def f = 1
def g = 1

val value1 =
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
import annotation.experimental

class Class1:
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition

object Object1:
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition

def fun1 =
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition

val value1 =
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
Original file line number Diff line number Diff line change
@@ -1,28 +1,24 @@
import annotation.experimental

class Class1:
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
@experimental def f = 1

object Object1:
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
@experimental def f = 1

def fun1 =
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
@experimental def f = 1

val value1 =
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import annotation.experimental

package foo {
import language.experimental.fewerBraces // error
import language.experimental.namedTypeArguments // error
import language.experimental.genericNumberLiterals // error
import language.experimental.erasedDefinitions // ok: only check at erased definition
Expand All @@ -13,7 +12,6 @@ package foo {

package foo2 {
// ok: all definitions are top-level @experimental
import language.experimental.fewerBraces
import language.experimental.namedTypeArguments
import language.experimental.genericNumberLiterals
import language.experimental.erasedDefinitions
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/closure-args.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import language.experimental.fewerBraces
import language.`3.3`

val x = List(1).map: (x: => Int) => // error
???
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i10943.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import language.experimental.fewerBraces
import language.`3.3`

object T:
class A
Expand Down
Loading

0 comments on commit 43e3b7c

Please sign in to comment.