-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Improve diagnostic for unclosed strings #13185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -774,7 +774,16 @@ handle_heredocs(T, Line, Column, H, Scope, Tokens) -> | |
| handle_strings(T, Line, Column, H, Scope, Tokens) -> | ||
| case elixir_interpolation:extract(Line, Column, Scope, true, T, H) of | ||
| {error, Reason} -> | ||
| interpolation_error(Reason, [H | T], Scope, Tokens, " (for string starting at line ~B)", [Line]); | ||
| {error, {Meta, Message, Token}, NewRest, NewScope, NewTokens} = interpolation_error(Reason, [H | T], Scope, Tokens, " (for string starting at line ~B)", [Line]), | ||
| NewMeta = case lists:keyfind(error_type, 1, Meta) of | ||
| % Don't override other errors that may have happened downstream already | ||
| {error_type, mismatched_delimiter} -> Meta; | ||
| {error_type, invalid_bidi} -> lists:keydelete(error_type, 1, Meta); | ||
| _ -> | ||
| {line, EndLine} = lists:keyfind(line, 1, Meta), | ||
| [{line, Line}, {column, Column - 1}, {error_type, unclosed_delimiter}, {end_line, EndLine}, {opening_delimiter, '"'}] | ||
josevalim marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| end, | ||
| {error, {NewMeta, Message, Token}, NewRest, NewScope, NewTokens}; | ||
|
|
||
| {NewLine, NewColumn, Parts, [$: | Rest], InterScope} when ?is_space(hd(Rest)) -> | ||
| NewScope = case is_unnecessary_quote(Parts, InterScope) of | ||
|
|
@@ -1493,6 +1502,7 @@ terminator('(') -> ')'; | |
| terminator('[') -> ']'; | ||
| terminator('{') -> '}'; | ||
| terminator('"""') -> '"""'; | ||
| terminator('"') -> '"'; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have been thinking for a while that we should include the expected_delimiter directly in the exception struct. The fact we are adding new entries here make me thing we should go ahead with adding these fields. WDYT?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess that's fine, I see no problem in doing it this way as well, but perhaps by also adding it to the exception makes it a bit more complete In the end I think that the least work we have to do in the exception code and can do it before is better, because the overall diagnostics get closer to the diagnostics we print to the terminal as well (although in some cases it will probably better to just keep them different) |
||
| terminator('<<') -> '>>'. | ||
|
|
||
| %% Keywords checking | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -496,7 +496,13 @@ defmodule CodeTest do | |
| test "string_to_quoted returns error on incomplete escaped string" do | ||
| assert Code.string_to_quoted("\"\\") == | ||
| {:error, | ||
| {[line: 1, column: 3], "missing terminator: \" (for string starting at line 1)", ""}} | ||
| {[ | ||
| line: 1, | ||
| column: 1, | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have my doubts about the correctness of this case here though
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is correct according to our definition of MissingTerminator. But you need to add
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually, maybe it should be swapped around. line and column always points to where the error happens and we have opening_line and opening_column to point to where it began.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @lukaszsamson do you have any opinion on what the editor should show here? The line where the identifier starts or is it the line where it is not closed (usually the last line of the file)? What about a mismatched delimiter? Do we show point to the opening one (incorrectly closed) or the closing one? It is hard to say because either side may be wrong. The issue though with showing the end location is that we would need a negative span for the diagnostic.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was also thinking about this, currently on the diagnostics that are printed, the location is shown as the file bottom, but it's not a problem there because the new snippet shows both the start and end For a language server or editor extension that needs to show one point, I think it makes sense for us to use the starting one in the unclosed delimiter case, as the problem is always there and the end is always empty. For the mismatched delimiter I'm not sure
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Currently our TokenMissingError does not support the end_column, we actually just calculate them when building the snippet Should we update it so that we calculate the end column based on the snippet before building the exception? And if so, should we do it on this PR or another one?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have created this issue to help with the discussion: #13187 |
||
| error_type: :unclosed_delimiter, | ||
| end_line: 1, | ||
| opening_delimiter: :"\"" | ||
| ], "missing terminator: \" (for string starting at line 1)", ""}} | ||
| end | ||
|
|
||
| test "compile source" do | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this matching be handled inside
interpolation_errorinstead? 🤔 This way we don't need to make changes to bidi (i.e. all other errors pass through).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also think this code is called for
'foo'. So you need to use the variableHto detect which kind of delimiter we are expecting.Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my tests and understanding, we get the bidi error from elixir_interpolation:extract, and then interpolation_error just passes it through
I've updated bidi to add its error type so we can differentiate and not override it, because the way it currently is we receive the same error structure whether it's a bidi error or an unclosed string one
Great point, I'll do some tests with single-quote strings, I believe they should have this same behavior if unclosed, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you only receive the same structure because you are intercepting the error. Instead of intercepting it, change where the error is raised to already include all metadata.