Skip to content

Commit 56b62b6

Browse files
committed
Consider start line in MismatchedDelimiterError
1 parent abbf906 commit 56b62b6

File tree

3 files changed

+156
-41
lines changed

3 files changed

+156
-41
lines changed

lib/elixir/lib/exception.ex

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,7 @@ defmodule MismatchedDelimiterError do
954954
:file,
955955
:line,
956956
:column,
957+
:line_offset,
957958
:end_line,
958959
:end_column,
959960
:opening_delimiter,
@@ -968,6 +969,7 @@ defmodule MismatchedDelimiterError do
968969
column: start_column,
969970
end_line: end_line,
970971
end_column: end_column,
972+
line_offset: line_offset,
971973
description: description,
972974
opening_delimiter: opening_delimiter,
973975
closing_delimiter: _closing_delimiter,
@@ -979,13 +981,24 @@ defmodule MismatchedDelimiterError do
979981
lines = String.split(snippet, "\n")
980982
expected_delimiter = :elixir_tokenizer.terminator(opening_delimiter)
981983

982-
snippet = format_snippet(start_pos, end_pos, description, file, lines, expected_delimiter)
984+
snippet =
985+
format_snippet(
986+
start_pos,
987+
end_pos,
988+
line_offset,
989+
description,
990+
file,
991+
lines,
992+
expected_delimiter
993+
)
994+
983995
format_message(file, end_line, end_column, snippet)
984996
end
985997

986998
defp format_snippet(
987999
{start_line, _start_column} = start_pos,
9881000
{end_line, end_column} = end_pos,
1001+
line_offset,
9891002
description,
9901003
file,
9911004
lines,
@@ -998,12 +1011,21 @@ defmodule MismatchedDelimiterError do
9981011

9991012
relevant_lines =
10001013
if end_line - start_line < @max_lines_shown do
1001-
line_range(lines, start_pos, end_pos, padding, max_digits, expected_delimiter)
1014+
line_range(
1015+
lines,
1016+
start_pos,
1017+
end_pos,
1018+
line_offset,
1019+
padding,
1020+
max_digits,
1021+
expected_delimiter
1022+
)
10021023
else
10031024
trimmed_inbetween_lines(
10041025
lines,
10051026
start_pos,
10061027
end_pos,
1028+
line_offset,
10071029
padding,
10081030
max_digits,
10091031
expected_delimiter
@@ -1022,6 +1044,7 @@ defmodule MismatchedDelimiterError do
10221044
defp format_snippet(
10231045
{start_line, start_column},
10241046
{end_line, end_column},
1047+
line_offset,
10251048
description,
10261049
file,
10271050
lines,
@@ -1032,7 +1055,7 @@ defmodule MismatchedDelimiterError do
10321055
general_padding = max(2, max_digits) + 1
10331056
padding = n_spaces(general_padding)
10341057

1035-
line = Enum.fetch!(lines, end_line - 1)
1058+
line = Enum.fetch!(lines, end_line - 1 - line_offset)
10361059
formatted_line = [line_padding(end_line, max_digits), to_string(end_line), " │ ", line]
10371060

10381061
mismatched_closing_line =
@@ -1080,14 +1103,15 @@ defmodule MismatchedDelimiterError do
10801103
lines,
10811104
{start_line, start_column},
10821105
{end_line, end_column},
1106+
line_offset,
10831107
padding,
10841108
max_digits,
10851109
expected_delimiter
10861110
) do
10871111
start_padding = line_padding(start_line, max_digits)
10881112
end_padding = line_padding(end_line, max_digits)
1089-
first_line = Enum.fetch!(lines, start_line - 1)
1090-
last_line = Enum.fetch!(lines, end_line - 1)
1113+
first_line = Enum.fetch!(lines, start_line - 1 - line_offset)
1114+
last_line = Enum.fetch!(lines, end_line - 1 - line_offset)
10911115

10921116
"""
10931117
#{start_padding}#{start_line}#{first_line}
@@ -1102,6 +1126,7 @@ defmodule MismatchedDelimiterError do
11021126
lines,
11031127
{start_line, start_column},
11041128
{end_line, end_column},
1129+
line_offset,
11051130
padding,
11061131
max_digits,
11071132
expected_delimiter
@@ -1110,7 +1135,7 @@ defmodule MismatchedDelimiterError do
11101135
end_line = end_line - 1
11111136

11121137
lines
1113-
|> Enum.slice(start_line..end_line)
1138+
|> Enum.slice((start_line - line_offset)..(end_line - line_offset))
11141139
|> Enum.zip_with(start_line..end_line, fn line, line_number ->
11151140
line_number = line_number + 1
11161141
start_line = start_line + 1

lib/elixir/src/elixir_errors.erl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -404,9 +404,10 @@ parse_erl_term(Term) ->
404404
Parsed.
405405

406406
raise_mismatched_delimiter(Location, File, Input, Message) ->
407-
{InputString, _, _} = Input,
407+
{InputString, StartLine, _} = Input,
408408
InputBinary = elixir_utils:characters_to_binary(InputString),
409-
raise('Elixir.MismatchedDelimiterError', Message, [{file, File}, {snippet, InputBinary} | Location]).
409+
KV = [{file, File}, {line_offset, StartLine - 1}, {snippet, InputBinary} | Location],
410+
raise('Elixir.MismatchedDelimiterError', Message, KV).
410411

411412
raise_reserved(Location, File, Input, Keyword) ->
412413
raise_snippet(Location, File, Input, 'Elixir.SyntaxError',

lib/elixir/test/elixir/kernel/diagnostics_test.exs

Lines changed: 122 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,28 @@ defmodule Kernel.DiagnosticsTest do
5353
"""
5454
end
5555

56+
test "same line with offset" do
57+
output =
58+
capture_raise(
59+
"""
60+
[1, 2, 3, 4, 5, 6)
61+
""",
62+
MismatchedDelimiterError,
63+
line: 3
64+
)
65+
66+
assert output == """
67+
** (MismatchedDelimiterError) mismatched delimiter found on nofile:3:18:
68+
error: unexpected token: )
69+
70+
3 │ [1, 2, 3, 4, 5, 6)
71+
│ │ └ mismatched closing delimiter (expected "]")
72+
│ └ unclosed delimiter
73+
74+
└─ nofile:3:18\
75+
"""
76+
end
77+
5678
test "two-line span" do
5779
output =
5880
capture_raise(
@@ -76,6 +98,30 @@ defmodule Kernel.DiagnosticsTest do
7698
"""
7799
end
78100

101+
test "two-line span with offset" do
102+
output =
103+
capture_raise(
104+
"""
105+
[a, b, c
106+
d, f, g}
107+
""",
108+
MismatchedDelimiterError,
109+
line: 3
110+
)
111+
112+
assert output == """
113+
** (MismatchedDelimiterError) mismatched delimiter found on nofile:4:9:
114+
error: unexpected token: }
115+
116+
3 │ [a, b, c
117+
│ └ unclosed delimiter
118+
4 │ d, f, g}
119+
│ └ mismatched closing delimiter (expected "]")
120+
121+
└─ nofile:4:9\
122+
"""
123+
end
124+
79125
test "many-line span" do
80126
output =
81127
capture_raise(
@@ -103,28 +149,31 @@ defmodule Kernel.DiagnosticsTest do
103149
104150
└─ nofile:5:5\
105151
"""
152+
end
106153

154+
test "many-line span with offset" do
107155
output =
108156
capture_raise(
109157
"""
110158
fn always_forget_end ->
111159
IO.inspect(2 + 2) + 2
112160
)
113161
""",
114-
MismatchedDelimiterError
162+
MismatchedDelimiterError,
163+
line: 3
115164
)
116165

117166
assert output == """
118-
** (MismatchedDelimiterError) mismatched delimiter found on nofile:3:1:
167+
** (MismatchedDelimiterError) mismatched delimiter found on nofile:5:1:
119168
error: unexpected token: )
120169
121-
1 │ fn always_forget_end ->
170+
3 │ fn always_forget_end ->
122171
│ └ unclosed delimiter
123-
2 │ IO.inspect(2 + 2) + 2
124-
3 │ )
172+
4 │ IO.inspect(2 + 2) + 2
173+
5 │ )
125174
│ └ mismatched closing delimiter (expected "end")
126175
127-
└─ nofile:3:1\
176+
└─ nofile:5:1\
128177
"""
129178
end
130179

@@ -290,16 +339,6 @@ defmodule Kernel.DiagnosticsTest do
290339

291340
describe "compile-time exceptions" do
292341
test "SyntaxError (snippet)" do
293-
expected = """
294-
** (SyntaxError) invalid syntax found on nofile:1:17:
295-
error: syntax error before: '*'
296-
297-
1 │ [1, 2, 3, 4, 5, *]
298-
│ ^
299-
300-
└─ nofile:1:17\
301-
"""
302-
303342
output =
304343
capture_raise(
305344
"""
@@ -308,20 +347,39 @@ defmodule Kernel.DiagnosticsTest do
308347
SyntaxError
309348
)
310349

311-
assert output == expected
350+
assert output == """
351+
** (SyntaxError) invalid syntax found on nofile:1:17:
352+
error: syntax error before: '*'
353+
354+
1 │ [1, 2, 3, 4, 5, *]
355+
│ ^
356+
357+
└─ nofile:1:17\
358+
"""
312359
end
313360

314-
test "TokenMissingError (snippet)" do
315-
expected = """
316-
** (TokenMissingError) token missing on nofile:1:4:
317-
error: syntax error: expression is incomplete
318-
319-
1 │ 1 +
320-
│ ^
321-
322-
└─ nofile:1:4\
323-
"""
361+
test "SyntaxError (snippet) with offset" do
362+
output =
363+
capture_raise(
364+
"""
365+
[1, 2, 3, 4, 5, *]
366+
""",
367+
SyntaxError,
368+
line: 3
369+
)
370+
371+
assert output == """
372+
** (SyntaxError) invalid syntax found on nofile:3:17:
373+
error: syntax error before: '*'
374+
375+
3 │ [1, 2, 3, 4, 5, *]
376+
│ ^
377+
378+
└─ nofile:3:17\
379+
"""
380+
end
324381

382+
test "TokenMissingError (snippet)" do
325383
output =
326384
capture_raise(
327385
"""
@@ -330,7 +388,36 @@ defmodule Kernel.DiagnosticsTest do
330388
TokenMissingError
331389
)
332390

333-
assert output == expected
391+
assert output == """
392+
** (TokenMissingError) token missing on nofile:1:4:
393+
error: syntax error: expression is incomplete
394+
395+
1 │ 1 +
396+
│ ^
397+
398+
└─ nofile:1:4\
399+
"""
400+
end
401+
402+
test "TokenMissingError (snippet) with offset" do
403+
output =
404+
capture_raise(
405+
"""
406+
1 +
407+
""",
408+
TokenMissingError,
409+
line: 3
410+
)
411+
412+
assert output == """
413+
** (TokenMissingError) token missing on nofile:3:4:
414+
error: syntax error: expression is incomplete
415+
416+
3 │ 1 +
417+
│ ^
418+
419+
└─ nofile:3:4\
420+
"""
334421
end
335422

336423
test "TokenMissingError (no snippet)" do
@@ -419,7 +506,7 @@ defmodule Kernel.DiagnosticsTest do
419506
1 -
420507
""",
421508
TokenMissingError,
422-
fake_stacktrace
509+
stacktrace: fake_stacktrace
423510
)
424511

425512
assert output == expected
@@ -448,7 +535,7 @@ defmodule Kernel.DiagnosticsTest do
448535
1 -
449536
""",
450537
TokenMissingError,
451-
fake_stacktrace
538+
stacktrace: fake_stacktrace
452539
)
453540

454541
assert output == expected
@@ -1104,14 +1191,16 @@ defmodule Kernel.DiagnosticsTest do
11041191
end)
11051192
end
11061193

1107-
defp capture_raise(source, exception, mock_stacktrace \\ []) do
1194+
defp capture_raise(source, exception, opts \\ []) do
1195+
{stacktrace, opts} = Keyword.pop(opts, :stacktrace, [])
1196+
11081197
e =
11091198
assert_raise exception, fn ->
1110-
ast = Code.string_to_quoted!(source, columns: true)
1199+
ast = Code.string_to_quoted!(source, [columns: true] ++ opts)
11111200
Code.eval_quoted(ast)
11121201
end
11131202

1114-
Exception.format(:error, e, mock_stacktrace)
1203+
Exception.format(:error, e, stacktrace)
11151204
end
11161205

11171206
defp purge(module) when is_atom(module) do

0 commit comments

Comments
 (0)