scala3 icon indicating copy to clipboard operation
scala3 copied to clipboard

Surprising parsing behaviour for objects ending with `_`

Open keynmol opened this issue 2 years ago • 4 comments

Objects ending with _ that have a body are parsed in a surprising way. I only narrowed it down accidentally, but the symptoms were lots of compiler errors because of the defs I expected to be in the companion actually being at top level.

Compiler version

3.2.0

Minimized code

You can see how Hello_: is defined instead, and x is treated as a top-level definition

object Hello_:
  def x = 1

object `Byte_`:
  def x = 1

@main def hello = 
  println(Byte_)
  println(Hello_:)
  println(x)

Output

This parsing warning is the only indication that helped me figure out that something was off:

[warn] ./test.scala:2:3: Line is indented too far to the right, or a `{` or `:` is missing
[warn]   def x = 1
[warn]   ^

Expectation

The code either parses correctly, or fails to parse, or gives a meaningful warning

keynmol avatar Sep 19 '22 15:09 keynmol

That's a pretty good warning. Luckily everyone always compiles with -Werror. But this is a longstanding issue. It leads to folks always adding a space before their punctuation.

scala> def _greeting_: String = "hello"
-- [E040] Syntax Error: ------------------------------------------------------------------------------------------------
1 |def _greeting_: String = "hello"
  |                ^^^^^^
  |                '=' expected, but identifier found

Here, we want to write _greeting_ for emphasis! greeting, Earthling!

som-snytt avatar Sep 19 '22 15:09 som-snytt

I think the only thing we could do here is to improve the warning/error message. According to the language specification a sequence of alphanumeric characters directly followed by an underscore and a sequence of operator-like characters is treated as one identifier, so

object Hello_:

is equivalent to

object `Hello_:`

and not

object `Hello_`:

prolativ avatar Sep 20 '22 08:09 prolativ

It feels like the warning is a warning because we thought that indenting too much is harmless, but here the indentation is evidently misleading. One of the basic reasons we went for indentation-based syntax is to prevent misleading indentation, so I think we should emit an error here. @odersky what do you think about this?

abgruszecki avatar Sep 20 '22 12:09 abgruszecki

There are other situations where you might want this. Example:

  val mainDef = ...
     val mainDef_subSef1 = ...
     ...
     val mainDef_subDefN = ...

(this pattern is used a lot in Definitions.scala).

Or:

def foo = 
  val x = 2
    x
  + y
  + z

odersky avatar Sep 20 '22 13:09 odersky

Without a clear idea how this should be improved there's nothing to do. So far I have not seen anything.

odersky avatar Dec 14 '22 13:12 odersky

Relatedly, https://github.com/lampepfl/dotty/issues/10671

I tried just checking ahead from objectDef or classDef when name.endsWith(":"), as the suspicious case is easy to test for. I'll see if the carets line up.

Maybe it's worth the effort because "colon fusion" f_: is a language characteristic.

som-snytt avatar Dec 20 '22 00:12 som-snytt

I see on my local branch the carets did not line up. (Trailing comments, etc.)

On discord, the problem of "accidental fusion of colon" came up again, in the context that it is not only accidental but surprising, the way a banana peel is not surprising when someone slips on it, unless one has no experience with them.

Has no one ever proposed allowing (some subset of) trailing colon opchars only for operators, which are encouraged for infix? That sounds draconian. Perhaps a deprecation under -source:future if lacking infix.

som-snytt avatar Feb 11 '23 23:02 som-snytt

Ok. So this issue happens because the users end the object's name with an underscore and they accidentally "fuse" the colon. How about we just refuse to compile objects with such names, unless they are quoted? We could emit an error early during the parsing stage.

Example of the problematic scenario:

object Hello_:
  def x = 1

The user wanted to name the object Hello_ and accidentally created an object named Hello_: instead. So: we refuse to compile this code (b/c the object's name ends in _: without being quoted) and emit an error together with an explanation.

Example of fixed code:

object `Hello_:`
  def x = 1

Now it should be clear to the user what the object's name actually is.

Thoughts?

abgruszecki avatar Feb 14 '23 11:02 abgruszecki

it's not more restrictive than

➜  ~ scala -deprecation
Welcome to Scala 3.2.2 (19, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.

scala> {
     | object :: :
     |   def f = 42
     | }
1 warning found
-- Deprecation Warning: ------------------------------------------------------------------------------------------------
2 |object :: :
  |          ^
  |          `:` after symbolic operator is deprecated; use backticks around operator instead
// defined object ::

som-snytt avatar Feb 14 '23 13:02 som-snytt