Description
Compiler version
Scala 3.3.0
Introduction
The following are both (somewhat surprisingly) allowed:
def wrapPlaceholders(t: Int) = if true then
println("might fail")
else println("done")
if true then
()
else
()
These can be combined to create the following visually ambiguous piece of code:
Minimized code
if true then
def wrapPlaceholders(t: Int) = if true then
println("might fail")
else
println("done")
else
()
Above there is no space between the end of the first else
and the start of the second, this is not necessary:
if true then
def wrapPlaceholders(t: Int) = if true then
println("might fail")
// many lines of code
else
println("done")
// many lines of code
else
()
https://scastie.scala-lang.org/vgZDbHiSTby8OVyQNyOPsA
The same works with try finally
:
try
def wrapPlaceholders(t: Int) = try
println("might fail")
// many lines of code
finally
println("done")
// many lines of code
finally
()
https://scastie.scala-lang.org/hTAZVYOOSwqgLClEZ8ZtVw
Expectation
The above should not be possible, I believe this can be done by doing at least one of the following:
- Matching keyword pairs should be on the same indentation level
- There should not be any part of the body of a method at the same indentation level as that of its
def
(In the above, I use def
s, but this should also apply to val
s and potentially other things)
More details
I know technically, in the following, both the try
and the catch
are at the same indentation level for the compiler:
def wrapPlaceholders(t: Int) = try
println("might fail")
finally println("done")
I believe this should be changed:
If the starting keyword follows a =
(or similar), its level is "one more" than the level of the def
enclosing it
But there are no notions of "levels" as such in the parser, instead the Indent
after the try
should be able to be split into two non-empty sections, the first of which is exactly the indent difference between the def
and the finally
Example:
def wrapPlaceholders(t: Int) = try
<tab><tab>println("might fail")
<tab>finally println("done")
def wrapPlaceholders(t: Int) = try
<4spaces>println("might fail")
<2spaces>finally println("done")
def wrapPlaceholders(t: Int) = try
<tab><2spaces>println("might fail")
<tab>finally println("done")
Should all work
Disclaimer
I am not trying to find reasons to "go back to braces", on the contrary,
I think fixing these kinds of ambiguities will make braceless more popular (and they might have been one of the causes of the backlash)