Skip to content

Commit 4870351

Browse files
author
José Valim
committed
Deprecate continuable heredocs, closes #1986
1 parent aba387c commit 4870351

File tree

12 files changed

+61
-89
lines changed

12 files changed

+61
-89
lines changed

lib/elixir/src/elixir_tokenizer.erl

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,7 @@ tokenize([Space, Sign, NotMarker|T], Line, Scope, [{ Identifier, _, _ } = H|Toke
483483
tokenize([T|Rest], Line, Scope, Tokens) when ?is_horizontal_space(T) ->
484484
tokenize(Rest, Line, Scope, Tokens);
485485

486+
%% TODO: This token is deprecated once continuable heredocs are removed
486487
tokenize([{line,Line}|Rest], _Line, Scope, Tokens) ->
487488
tokenize(Rest, Line, Scope, Tokens);
488489

@@ -574,7 +575,7 @@ collect_modifiers(Rest, Buffer) ->
574575
%% Heredocs
575576

576577
extract_heredoc_with_interpolation(Line, Scope, Interpol, T, H) ->
577-
case extract_heredoc(Line, T, H) of
578+
case extract_heredoc(Line, T, H, Scope) of
578579
{ error, _ } = Error ->
579580
Error;
580581
{ Body, Rest } ->
@@ -586,51 +587,61 @@ extract_heredoc_with_interpolation(Line, Scope, Interpol, T, H) ->
586587
end
587588
end.
588589

589-
extract_heredoc(Line0, Rest0, Marker) ->
590-
case extract_heredoc_line(Rest0, []) of
590+
extract_heredoc(Line0, Rest0, Marker, Scope) ->
591+
case extract_heredoc_header(Rest0, [], {Line0,Scope#elixir_tokenizer.file}) of
591592
{ ok, Extra, Rest1 } ->
592593
%% We prepend a new line so we can transparently remove
593594
%% spaces later. This new line is removed by calling `tl`
594595
%% in the final heredoc body three lines below.
595596
case extract_heredoc_body(Line0, Marker, [$\n|Rest1], []) of
596597
{ Line1, Body, Rest2, Spaces } ->
597598
{ tl(remove_heredoc_spaces(Body, Spaces)), merge_heredoc_extra(Line1, Extra, Rest2) };
598-
{ error, Line1 } ->
599-
heredoc_error(Line1, Line0, Marker)
599+
{ error, ErrorLine } ->
600+
Terminator = [Marker, Marker, Marker],
601+
Message = "missing terminator: ~ts (for heredoc starting at line ~B)",
602+
{ error, { ErrorLine, io_lib:format(Message, [Terminator, Line0]), [] } }
600603
end;
601-
{ error, eof } ->
602-
heredoc_error(Line0, Line0, Marker)
604+
error ->
605+
%% TODO: Test me once the deprecated continuable heredocs are removed
606+
Terminator = [Marker, Marker, Marker],
607+
Message = "heredoc start ~ts must be followed by a new line",
608+
{ error, { Line0, io_lib:format(Message, [Terminator]), [] } }
603609
end.
604610

605-
heredoc_error(ErrorLine, StartLine, Marker) ->
606-
Terminator = [Marker, Marker, Marker],
607-
Message = io_lib:format("missing terminator: ~ts (for heredoc starting at line ~B)", [Terminator, StartLine]),
608-
{ error, { ErrorLine, Message, [] } }.
609-
610611
%% Remove spaces from heredoc based on the position of the final quotes.
611612

612613
remove_heredoc_spaces(Body, 0) ->
613614
lists:reverse([0|Body]);
614-
615615
remove_heredoc_spaces(Body, Spaces) ->
616616
remove_heredoc_spaces([0|Body], [], Spaces, Spaces).
617-
618617
remove_heredoc_spaces([H,$\n|T], [Backtrack|Buffer], Spaces, Original) when Spaces > 0, ?is_horizontal_space(H) ->
619618
remove_heredoc_spaces([Backtrack,$\n|T], Buffer, Spaces - 1, Original);
620-
621619
remove_heredoc_spaces([$\n=H|T], Buffer, _Spaces, Original) ->
622620
remove_heredoc_spaces(T, [H|Buffer], Original, Original);
623-
624621
remove_heredoc_spaces([H|T], Buffer, Spaces, Original) ->
625622
remove_heredoc_spaces(T, [H|Buffer], Spaces, Original);
626-
627623
remove_heredoc_spaces([], Buffer, _Spaces, _Original) ->
628624
Buffer.
629625

626+
%% Extract the heredoc header.
627+
628+
extract_heredoc_header("\r\n" ++ Rest, Buffer, _Scope) ->
629+
{ ok, [$\n|Buffer], Rest };
630+
extract_heredoc_header("\n" ++ Rest, Buffer, _Scope) ->
631+
{ ok, [$\n|Buffer], Rest };
632+
extract_heredoc_header([H|T], Buffer, {Line,File}) when not ?is_horizontal_space(H) ->
633+
elixir_errors:deprecation([{line,Line}], File, "continuable heredocs are deprecated, parsing will no longer continue on the same line as the heredoc starts"),
634+
extract_heredoc_header(T, [H|Buffer], nil);
635+
extract_heredoc_header([H|T], Buffer, _Scope) ->
636+
extract_heredoc_header(T, [H|Buffer], _Scope);
637+
extract_heredoc_header(_, _, _Scope) ->
638+
error.
639+
630640
%% Extract heredoc body. It returns the heredoc body (in reverse order),
631641
%% the remaining of the document and the number of spaces the heredoc
632642
%% is aligned.
633643

644+
%% TODO: This token is deprecated once continuable heredocs are removed
634645
extract_heredoc_body(_Line, Marker, [{line,NewLine}|Rest], Buffer) ->
635646
extract_heredoc_body(NewLine, Marker, Rest, Buffer);
636647

@@ -639,7 +650,7 @@ extract_heredoc_body(Line, Marker, Rest, Buffer) ->
639650
{ ok, NewBuffer, NewRest } ->
640651
extract_heredoc_body(Line + 1, Marker, NewRest, NewBuffer);
641652
{ ok, NewBuffer, NewRest, Spaces } ->
642-
{ Line + 1, NewBuffer, NewRest, Spaces };
653+
{ Line, NewBuffer, NewRest, Spaces };
643654
{ error, eof } ->
644655
{ error, Line }
645656
end.
@@ -648,45 +659,30 @@ extract_heredoc_body(Line, Marker, Rest, Buffer) ->
648659

649660
extract_heredoc_line("\r\n" ++ Rest, Buffer) ->
650661
{ ok, [$\n|Buffer], Rest };
651-
652662
extract_heredoc_line("\n" ++ Rest, Buffer) ->
653663
{ ok, [$\n|Buffer], Rest };
654-
655664
extract_heredoc_line([H|T], Buffer) ->
656665
extract_heredoc_line(T, [H|Buffer]);
657-
658666
extract_heredoc_line(_, _) ->
659667
{ error, eof }.
660668

661669
%% Extract each heredoc line trying to find a match according to the marker.
662670

663671
extract_heredoc_line(Marker, [H|T], Buffer, Counter) when ?is_horizontal_space(H) ->
664672
extract_heredoc_line(Marker, T, [H|Buffer], Counter + 1);
665-
666-
extract_heredoc_line(Marker, [Marker,Marker,Marker|T] = Rest, Buffer, Counter) ->
667-
case next_is_break(T) of
668-
false -> extract_heredoc_line(Rest, Buffer);
669-
Final -> { ok, Buffer, Final, Counter }
670-
end;
671-
673+
extract_heredoc_line(Marker, [Marker,Marker,Marker|T], Buffer, Counter) ->
674+
{ ok, Buffer, T, Counter };
672675
extract_heredoc_line(_Marker, Rest, Buffer, _Counter) ->
673676
extract_heredoc_line(Rest, Buffer).
674677

675-
next_is_break([H|T]) when ?is_horizontal_space(H) -> next_is_break(T);
676-
next_is_break("\r\n" ++ T) -> T;
677-
next_is_break("\n" ++ T) -> T;
678-
next_is_break([]) -> [];
679-
next_is_break(_) -> false.
680-
681678
%% Merge heredoc extra by replying it on the buffer. It also adds
682679
%% a special { line, Line } token to force a line change along the way.
683680

681+
%% TODO: This token is deprecated once continuable heredocs are removed
684682
merge_heredoc_extra(Line, Extra, Buffer) ->
685683
merge_heredoc_extra(Extra, [{line,Line}|Buffer]).
686-
687684
merge_heredoc_extra([H|T], Buffer) ->
688685
merge_heredoc_extra(T, [H|Buffer]);
689-
690686
merge_heredoc_extra([], Buffer) ->
691687
Buffer.
692688

lib/elixir/test/elixir/kernel/binary_test.exs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,19 @@ bar """
1717
"""
1818
end
1919

20-
test :heredoc_with_extra do
21-
assert 21 == __ENV__.line
22-
assert "foo\nbar\nbar\n" == """ <> "bar\n"
23-
foo
24-
bar
25-
"""
26-
end
27-
2820
test :aligned_heredoc do
29-
assert "foo\nbar\nbar\n" == """ <> "bar\n"
21+
assert "foo\nbar\n" == """
3022
foo
3123
bar
3224
"""
3325
end
3426

3527
test :heredoc_with_interpolation do
36-
assert "37\n" == """
28+
assert "29\n" == """
3729
#{__ENV__.line}
3830
"""
3931

40-
assert "\n42\n" == """
32+
assert "\n34\n" == """
4133
4234
#{__ENV__.line}
4335
"""

lib/elixir/test/elixir/kernel/errors_test.exs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -605,41 +605,41 @@ defmodule Kernel.ErrorsTest do
605605

606606
test :macros_error_stacktrace do
607607
assert [{:erlang, :+, [1, :foo], _}, {ErrorsTest, :sample, 1, _}|_] =
608-
rescue_stacktrace(""")
608+
rescue_stacktrace("""
609609
defmodule ErrorsTest do
610610
defmacro sample(num), do: num + :foo
611611
def other, do: sample(1)
612612
end
613-
"""
613+
""")
614614
end
615615

616616
test :macros_function_clause_stacktrace do
617617
assert [{__MODULE__, :sample, 1, _}|_] =
618-
rescue_stacktrace(""")
618+
rescue_stacktrace("""
619619
defmodule ErrorsTest do
620620
import Kernel.ErrorsTest
621621
sample(1)
622622
end
623-
"""
623+
""")
624624
end
625625

626626
test :macros_interpreted_function_clause_stacktrace do
627627
assert [{ErrorsTest, :sample, 1, _}|_] =
628-
rescue_stacktrace(""")
628+
rescue_stacktrace("""
629629
defmodule ErrorsTest do
630630
defmacro sample(0), do: 0
631631
def other, do: sample(1)
632632
end
633-
"""
633+
""")
634634
end
635635

636636
test :macros_compiled_callback do
637637
assert [{Kernel.ErrorsTest, :__before_compile__, [Macro.Env[module: ErrorsTest]], _}|_] =
638-
rescue_stacktrace(""")
638+
rescue_stacktrace("""
639639
defmodule ErrorsTest do
640640
Module.put_attribute(__MODULE__, :before_compile, Kernel.ErrorsTest)
641641
end
642-
"""
642+
""")
643643
end
644644

645645
defmacro sample(0), do: 0

lib/elixir/test/elixir/list_test.exs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,19 +154,4 @@ defmodule ListTest do
154154
assert List.delete_at([1, 2, 3], -3) == [2, 3]
155155
assert List.delete_at([1, 2, 3], -4) == [1, 2, 3]
156156
end
157-
158-
test :heredoc do
159-
assert [ "a\n" ] == [ """ ]
160-
a
161-
"""
162-
163-
assert [ "a\n", "b\n", "c\n", 1 ] == [ """, """, """, 1 ]
164-
a
165-
"""
166-
b
167-
"""
168-
c
169-
"""
170-
171-
end
172157
end

lib/mix/test/mix/deps_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ defmodule Mix.DepsTest do
109109
Mix.Project.push NestedDepsApp
110110

111111
in_fixture "deps_status", fn ->
112-
File.write!("custom/deps_repo/mix.exs", """)
112+
File.write! "custom/deps_repo/mix.exs", """
113113
defmodule DepsRepo do
114114
use Mix.Project
115115
@@ -158,7 +158,7 @@ defmodule Mix.DepsTest do
158158
Mix.Project.push ConvergedDepsApp
159159

160160
in_fixture "deps_status", fn ->
161-
File.write!("custom/deps_repo/mix.exs", """)
161+
File.write! "custom/deps_repo/mix.exs", """
162162
defmodule DepsRepo do
163163
use Mix.Project
164164

lib/mix/test/mix/tasks/compile.erlang_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ defmodule Mix.Tasks.Compile.ErlangTest do
1616

1717
test "compilation continues if one file fails to compile" do
1818
in_fixture "compile_erlang", fn ->
19-
File.write!("src/zzz.erl", """)
19+
File.write! "src/zzz.erl", """
2020
-module(zzz).
2121
def zzz(), do: b
2222
"""

lib/mix/test/mix/tasks/compile.leex_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ defmodule Mix.Tasks.Compile.LeexTest do
1616

1717
test "compilation continues if one file fails to compile" do
1818
in_fixture "compile_leex", fn ->
19-
File.write!("src/zzz.xrl", """)
19+
File.write! "src/zzz.xrl", """
2020
oops.
2121
"""
2222

lib/mix/test/mix/tasks/compile.yecc_test.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ defmodule Mix.Tasks.Compile.YeccTest do
1616

1717
test "compilation continues if one file fails to compile" do
1818
in_fixture "compile_yecc", fn ->
19-
File.write!("src/zzz.yrl", """)
19+
File.write! "src/zzz.yrl", """
2020
oops.
2121
"""
2222

lib/mix/test/mix/tasks/compile_test.exs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ defmodule Mix.Tasks.CompileTest do
4747
in_fixture "no_mixfile", fn ->
4848
import ExUnit.CaptureIO
4949

50-
File.mkdir!("src")
51-
File.write!("src/a.erl", """)
50+
File.mkdir! "src"
51+
File.write! "src/a.erl", """
5252
-module(b).
5353
def b(), do: b
5454
"""

lib/mix/test/mix/tasks/deps_test.exs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ defmodule Mix.Tasks.DepsTest do
9191
Mix.Project.push ReqDepsApp
9292

9393
in_fixture "deps_status", fn ->
94-
File.write!("deps/ok/mix.exs", """)
94+
File.write! "deps/ok/mix.exs", """
9595
defmodule Deps.OkApp do
9696
use Mix.Project
9797
@@ -453,7 +453,7 @@ defmodule Mix.Tasks.DepsTest do
453453
Mix.Project.push ConvergedDepsApp
454454

455455
in_fixture "deps_status", fn ->
456-
File.write!("custom/deps_repo/mix.exs", """)
456+
File.write! "custom/deps_repo/mix.exs", """
457457
defmodule DepsRepo do
458458
use Mix.Project
459459
@@ -489,7 +489,7 @@ defmodule Mix.Tasks.DepsTest do
489489
Mix.Project.push ConvergedDepsApp
490490

491491
in_fixture "deps_status", fn ->
492-
File.write!("custom/deps_repo/mix.exs", """)
492+
File.write! "custom/deps_repo/mix.exs", """
493493
defmodule DepsRepo do
494494
use Mix.Project
495495

0 commit comments

Comments
 (0)