Skip to content

Commit dfba5db

Browse files
committed
Consider start line in MismatchedDelimiterError
1 parent 51d23cb commit dfba5db

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
@@ -916,6 +916,7 @@ defmodule MismatchedDelimiterError do
916916
:file,
917917
:line,
918918
:column,
919+
:line_offset,
919920
:end_line,
920921
:end_column,
921922
:opening_delimiter,
@@ -930,6 +931,7 @@ defmodule MismatchedDelimiterError do
930931
column: start_column,
931932
end_line: end_line,
932933
end_column: end_column,
934+
line_offset: line_offset,
933935
description: description,
934936
opening_delimiter: opening_delimiter,
935937
closing_delimiter: _closing_delimiter,
@@ -941,13 +943,24 @@ defmodule MismatchedDelimiterError do
941943
lines = String.split(snippet, "\n")
942944
expected_delimiter = :elixir_tokenizer.terminator(opening_delimiter)
943945

944-
snippet = format_snippet(start_pos, end_pos, description, file, lines, expected_delimiter)
946+
snippet =
947+
format_snippet(
948+
start_pos,
949+
end_pos,
950+
line_offset,
951+
description,
952+
file,
953+
lines,
954+
expected_delimiter
955+
)
956+
945957
format_message(file, end_line, end_column, snippet)
946958
end
947959

948960
defp format_snippet(
949961
{start_line, _start_column} = start_pos,
950962
{end_line, end_column} = end_pos,
963+
line_offset,
951964
description,
952965
file,
953966
lines,
@@ -960,12 +973,21 @@ defmodule MismatchedDelimiterError do
960973

961974
relevant_lines =
962975
if end_line - start_line < @max_lines_shown do
963-
line_range(lines, start_pos, end_pos, padding, max_digits, expected_delimiter)
976+
line_range(
977+
lines,
978+
start_pos,
979+
end_pos,
980+
line_offset,
981+
padding,
982+
max_digits,
983+
expected_delimiter
984+
)
964985
else
965986
trimmed_inbetween_lines(
966987
lines,
967988
start_pos,
968989
end_pos,
990+
line_offset,
969991
padding,
970992
max_digits,
971993
expected_delimiter
@@ -984,6 +1006,7 @@ defmodule MismatchedDelimiterError do
9841006
defp format_snippet(
9851007
{start_line, start_column},
9861008
{end_line, end_column},
1009+
line_offset,
9871010
description,
9881011
file,
9891012
lines,
@@ -994,7 +1017,7 @@ defmodule MismatchedDelimiterError do
9941017
general_padding = max(2, max_digits) + 1
9951018
padding = n_spaces(general_padding)
9961019

997-
line = Enum.fetch!(lines, end_line - 1)
1020+
line = Enum.fetch!(lines, end_line - 1 - line_offset)
9981021
formatted_line = [line_padding(end_line, max_digits), to_string(end_line), " │ ", line]
9991022

10001023
mismatched_closing_line =
@@ -1042,14 +1065,15 @@ defmodule MismatchedDelimiterError do
10421065
lines,
10431066
{start_line, start_column},
10441067
{end_line, end_column},
1068+
line_offset,
10451069
padding,
10461070
max_digits,
10471071
expected_delimiter
10481072
) do
10491073
start_padding = line_padding(start_line, max_digits)
10501074
end_padding = line_padding(end_line, max_digits)
1051-
first_line = Enum.fetch!(lines, start_line - 1)
1052-
last_line = Enum.fetch!(lines, end_line - 1)
1075+
first_line = Enum.fetch!(lines, start_line - 1 - line_offset)
1076+
last_line = Enum.fetch!(lines, end_line - 1 - line_offset)
10531077

10541078
"""
10551079
#{start_padding}#{start_line}#{first_line}
@@ -1064,6 +1088,7 @@ defmodule MismatchedDelimiterError do
10641088
lines,
10651089
{start_line, start_column},
10661090
{end_line, end_column},
1091+
line_offset,
10671092
padding,
10681093
max_digits,
10691094
expected_delimiter
@@ -1072,7 +1097,7 @@ defmodule MismatchedDelimiterError do
10721097
end_line = end_line - 1
10731098

10741099
lines
1075-
|> Enum.slice(start_line..end_line)
1100+
|> Enum.slice((start_line - line_offset)..(end_line - line_offset))
10761101
|> Enum.zip_with(start_line..end_line, fn line, line_number ->
10771102
line_number = line_number + 1
10781103
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)