Skip to content
Open
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: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/config/Feature.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ object Feature:
val multiSpreads = experimental("multiSpreads")
val subCases = experimental("subCases")
val relaxedLambdaSyntax = experimental("relaxedLambdaSyntax")
val relaxedColonSyntax = experimental("relaxedColonSyntax")

def experimentalAutoEnableFeatures(using Context): List[TermName] =
defn.languageExperimentalFeatures
Expand Down Expand Up @@ -73,6 +74,7 @@ object Feature:
(multiSpreads, "Enable experimental varargs with multi-spreads"),
(subCases, "Enable experimental match expressions with sub-cases"),
(relaxedLambdaSyntax, "Enable experimental relaxed lambda syntax"),
(relaxedColonSyntax, "Enable experimental relaxed colon syntax"),
)

// legacy language features from Scala 2 that are no longer supported.
Expand Down
18 changes: 14 additions & 4 deletions compiler/src/dotty/tools/dotc/parsing/Scanners.scala
Original file line number Diff line number Diff line change
Expand Up @@ -628,7 +628,13 @@ object Scanners {
insert(if (pastBlankLine) NEWLINES else NEWLINE, lineOffset)
else if indentIsSignificant then
if nextWidth < lastWidth
|| nextWidth == lastWidth && (indentPrefix == MATCH || indentPrefix == CATCH) && token != CASE then
|| nextWidth == lastWidth
&& token != CASE
&& indentPrefix.match
case MATCH | CATCH => true
case CASE => featureEnabled(Feature.relaxedColonSyntax)
case _ => false
then
if currentRegion.isOutermost then
if nextWidth < lastWidth then currentRegion = topLevelRegion(nextWidth)
else if canDedent then
Expand All @@ -654,9 +660,13 @@ object Scanners {
else if r.isInstanceOf[InBraces] && !closingRegionTokens.contains(token) then
report.warning("Line is indented too far to the left, or a `}` is missing", sourcePos())
else if lastWidth < nextWidth
|| lastWidth == nextWidth && (lastToken == MATCH || lastToken == CATCH) && token == CASE then
|| lastWidth == nextWidth
&& token == CASE
&& (lastToken == MATCH || lastToken == CATCH || featureEnabled(Feature.relaxedColonSyntax))
then
if canStartIndentTokens.contains(lastToken) then
currentRegion = Indented(nextWidth, lastToken, currentRegion)
val prefix = if token == CASE && featureEnabled(Feature.relaxedColonSyntax) then CASE else lastToken
currentRegion = Indented(nextWidth, prefix, currentRegion)
insert(INDENT, offset)
else if lastToken == SELFARROW then
currentRegion.knownWidth = nextWidth
Expand Down Expand Up @@ -1694,7 +1704,7 @@ object Scanners {
case class Indented(width: IndentWidth, prefix: Token, outer: Region | Null) extends Region(OUTDENT):
knownWidth = width

/** Other indendation widths > width of lines in the same region */
/** Other indentation widths > width of lines in the same region */
var otherIndentWidths: Set[IndentWidth] = Set()

override def coversIndent(w: IndentWidth) = width == w || otherIndentWidths.contains(w)
Expand Down
11 changes: 7 additions & 4 deletions library/src/scala/language.scala
Original file line number Diff line number Diff line change
Expand Up @@ -368,13 +368,16 @@ object language {
@compileTimeOnly("`subCases` can only be used at compile time in import statements")
object subCases

/** Experimental support for single-line lambdas and case clause expressions after `:`
*/
/** Experimental support for single-line lambdas and case clause expressions after `:`. */
@compileTimeOnly("`relaxedLambdaSyntax` can only be used at compile time in import statements")
object relaxedLambdaSyntax

/** Experimental support for unindented case block after `:`. */
@compileTimeOnly("`relaxedColonSyntax` can only be used at compile time in import statements")
object relaxedColonSyntax
}

/** The deprecated object contains features that are no longer officially suypported in Scala.
/** The deprecated object contains features that are no longer officially suypported in Scala.
* Features in this object are slated for removal. New code should not use them and
* old code should migrate away from them.
*/
Expand Down Expand Up @@ -448,7 +451,7 @@ object language {
object `future-migration`

/** Sets source version to 2.13. Effectively, this doesn't change the source language,
* but rather adapts the generated code as if it was compiled with Scala 2.13
* but rather adapts the generated code as if it were compiled with Scala 2.13.
*/
@compileTimeOnly("`2.13` can only be used at compile time in import statements")
private[scala] object `2.13`
Expand Down
3 changes: 3 additions & 0 deletions project/MiMaFilters.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ object MiMaFilters {
ProblemFilters.exclude[DirectMissingMethodProblem]("scala.caps.package#package.freeze"),
// scala/scala3#24545 / scala/scala3#24788
ProblemFilters.exclude[MissingClassProblem]("scala.annotation.unchecked.uncheckedOverride"),
ProblemFilters.exclude[MissingFieldProblem]("scala.language#experimental.relaxedColonSyntax"),
ProblemFilters.exclude[MissingClassProblem]("scala.language$experimental$relaxedColonSyntax$"),
// scala/scala3#24841
),

)
Expand Down
49 changes: 49 additions & 0 deletions tests/pos/case-indent.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import language.experimental.relaxedColonSyntax
import language.experimental.relaxedLambdaSyntax

def f[A](xs: List[A]): List[String] =
xs.map:
case s: String => s
case x => x.toString
.map:
case s: String if s.length > 4 => s
case s => f"$s%.04s"

def g(xs: List[Int]): List[String] =
xs.map: case i: Int => i.toString // "relaxed lambda"
.map:
case s: String if s.length > 4 => s
case s => f"$s%.04s"

class Extra:
val pf: PartialFunction[String, Int] =
case "foo" => 1
case "bar" => 2

def tryit(xs: List[String]) = xs.collect(pf)

class Possibly(b: Boolean):
val pf: PartialFunction[String, Int] =
if b then
case "foo" => 1
case "bar" => 2
else
case "foo" => 42
case "bar" => 27

def tryit(xs: List[String]) = xs.collect(pf)

class Functional:
val f: Int => PartialFunction[String, Int] =
i =>
case _ => i

@main def main =
println:
f(List(42))
println:
g(List(42))
println:
Extra().tryit("baz" :: "bar" :: "foo" :: Nil)
println:
Possibly(false).tryit("baz" :: "bar" :: "foo" :: Nil)
Loading