Skip to content

Conversation

@thomasmarshall
Copy link
Contributor

👋 We're working on integrating Prism into Sorbet and I would like to propose an improvement to how unterminated construct errors are reported to end users.

This PR adds an expect1_opening function that expects a token and attaches the error to the opening token location rather than the current position. This is useful for errors about missing closing tokens, where it could be useful to point to the line with the opening token rather than the end of the file.

For example:

def foo
def bar
def baz
       ^ expected an `end` to close the `def` statement
       ^ expected an `end` to close the `def` statement
       ^ expected an `end` to close the `def` statement

This would previously produce three identical errors at the end of the file. After this commit, they would be reported at the opening token location:

def foo
^~~ expected an `end` to close the `def` statement
def bar
^~~ expected an `end` to close the `def` statement
def baz
^~~ expected an `end` to close the `def` statement

This way, end users can see which particular def node is considered unclosed, rather than a seeing a collection of errors all pointing to the end of the file. However, I understand this might be considered less technically correct and potentially less useful in certain situations. In the single-line test examples it might seem preferable to show the error at end-of-input, since that's where the closing token should be, but I think in most practical cases that won't be the case.

Alternative

An alternative approach might be to add "hints" to errors so the error location remains end-of-input but an additional hint location could point to the opening token. Clients could choose whether to surface the original error location, the hint location, or both. What do you think?

Tests

As part of this, I added a handful of tests capturing the original behavior which should make it easier to see the effect this proposed change has.

This commit adds an `expect1_opening` function that expects a token and
attaches the error to the opening token location rather than the current
position. This is useful for errors about missing closing tokens, where
we want to point to the line with the opening token rather than the end
of the file.

For example:

```ruby
def foo
def bar
def baz
       ^ expected an `end` to close the `def` statement
       ^ expected an `end` to close the `def` statement
       ^ expected an `end` to close the `def` statement
```

This would previously produce three identical errors at the end of the
file. After this commit, they would be reported at the opening token
location:

```ruby
def foo
^~~ expected an `end` to close the `def` statement
def bar
^~~ expected an `end` to close the `def` statement
def baz
^~~ expected an `end` to close the `def` statement
```

I considered using the end of the line where the opening token is
located, but in some cases that would be less useful than the opening
token location itself. For example:

```ruby
def foo def bar def baz
```

Here the end of the line where the opening token is located would be the
same for each of the unclosed `def` nodes.
@thomasmarshall thomasmarshall changed the title Proposal: Report unterminated construct errors at opening token Report unterminated construct errors at opening token Dec 29, 2025
Copy link
Collaborator

@kddnewton kddnewton left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great, I love it.

Yeah in reality this is showing a limitation of our error messages where they only refer to a single location. Other languages have more sophisticated setups where it can refer to an origination point as well as when the error was detected, which is really what we should work toward. But I think this is a really good step in that direction (especially because it finds all of the call sites for us to extend later).

@kddnewton kddnewton merged commit 6f91603 into ruby:main Dec 29, 2025
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants