diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb3772ef08..5514740c795 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,15 @@ This release no longer supports WERL (a graphical user interface for the Erlang ### 4. Hard deprecations +#### EEx + + * [EEx] `<%#` is deprecated in favor of `<%!--` or `<% #` + * [EEx] `c:EEx.handle_text/2` is deprecated in favor of `c:EEx.handle_text/3` + +#### Mix + + * [mix cmd] Deprecate `mix cmd --app APP` in favor of `mix do --app APP` + ## v1.17 The CHANGELOG for v1.17 releases can be found [in the v1.17 branch](https://github.com/elixir-lang/elixir/blob/v1.17/CHANGELOG.md). diff --git a/lib/eex/lib/eex/compiler.ex b/lib/eex/lib/eex/compiler.ex index 63910400c6c..4f9f2f7ded9 100644 --- a/lib/eex/lib/eex/compiler.ex +++ b/lib/eex/lib/eex/compiler.ex @@ -48,8 +48,14 @@ defmodule EEx.Compiler do end end - # TODO: Deprecate this on Elixir v1.18 + # TODO: Remove me on Elixir v2.0 defp tokenize(~c"<%#" ++ t, line, column, state, buffer, acc) do + IO.warn("<%# is deprecated, use <%!-- or add a space between <% and # instead", + line: line, + column: column, + file: state.file + ) + case expr(t, line, column + 3, state, []) do {:error, message} -> {:error, message, %{line: line, column: column}} @@ -299,6 +305,13 @@ defmodule EEx.Compiler do } init = state.engine.init(opts) + + if function_exported?(state.engine, :handle_text, 2) do + IO.warn( + "#{inspect(state.engine)}.handle_text/2 is deprecated, implement handle_text/3 instead" + ) + end + generate_buffer(tokens, init, [], state) end @@ -316,8 +329,7 @@ defmodule EEx.Compiler do meta = [line: meta.line, column: meta.column] state.engine.handle_text(buffer, meta, IO.chardata_to_string(chars)) else - # TODO: Deprecate this branch on Elixir v1.18. - # We should most likely move this check to init to emit the deprecation once. + # TODO: Remove this on Elixir v2.0. The deprecation is on init. state.engine.handle_text(buffer, IO.chardata_to_string(chars)) end diff --git a/lib/eex/test/eex/tokenizer_test.exs b/lib/eex/test/eex/tokenizer_test.exs index f452bfbcf2e..38cbca4e317 100644 --- a/lib/eex/test/eex/tokenizer_test.exs +++ b/lib/eex/test/eex/tokenizer_test.exs @@ -126,51 +126,21 @@ defmodule EEx.TokenizerTest do end test "EEx comments" do - exprs = [ - {:text, ~c"foo ", %{column: 1, line: 1}}, - {:eof, %{column: 16, line: 1}} - ] + ExUnit.CaptureIO.capture_io(:stderr, fn -> + exprs = [ + {:text, ~c"foo ", %{column: 1, line: 1}}, + {:eof, %{column: 16, line: 1}} + ] - assert EEx.tokenize(~c"foo <%# true %>", @opts) == {:ok, exprs} + assert EEx.tokenize(~c"foo <%# true %>", @opts) == {:ok, exprs} - exprs = [ - {:text, ~c"foo ", %{column: 1, line: 1}}, - {:eof, %{column: 8, line: 2}} - ] + exprs = [ + {:text, ~c"foo ", %{column: 1, line: 1}}, + {:eof, %{column: 8, line: 2}} + ] - assert EEx.tokenize(~c"foo <%#\ntrue %>", @opts) == {:ok, exprs} - end - - test "EEx comments with do-end" do - exprs = [ - {:text, ~c"foo ", %{column: 1, line: 1}}, - {:text, ~c"bar", %{column: 19, line: 1}}, - {:eof, %{column: 32, line: 1}} - ] - - assert EEx.tokenize(~c"foo <%# true do %>bar<%# end %>", @opts) == {:ok, exprs} - end - - test "EEx comments inside do-end" do - exprs = [ - {:start_expr, ~c"", ~c" if true do ", %{column: 1, line: 1}}, - {:text, ~c"bar", %{column: 31, line: 1}}, - {:end_expr, [], ~c" end ", %{column: 34, line: 1}}, - {:eof, %{column: 43, line: 1}} - ] - - assert EEx.tokenize(~c"<% if true do %><%# comment %>bar<% end %>", @opts) == {:ok, exprs} - - exprs = [ - {:start_expr, [], ~c" case true do ", %{column: 1, line: 1}}, - {:middle_expr, ~c"", ~c" true -> ", %{column: 33, line: 1}}, - {:text, ~c"bar", %{column: 46, line: 1}}, - {:end_expr, [], ~c" end ", %{column: 49, line: 1}}, - {:eof, %{column: 58, line: 1}} - ] - - assert EEx.tokenize(~c"<% case true do %><%# comment %><% true -> %>bar<% end %>", @opts) == - {:ok, exprs} + assert EEx.tokenize(~c"foo <%#\ntrue %>", @opts) == {:ok, exprs} + end) end test "EEx multi-line comments" do @@ -321,15 +291,6 @@ defmodule EEx.TokenizerTest do assert EEx.tokenize(template, [trim: true] ++ @opts) == {:ok, exprs} end - test "trim mode with comment" do - exprs = [ - {:text, ~c"\n123", %{column: 19, line: 1}}, - {:eof, %{column: 4, line: 2}} - ] - - assert EEx.tokenize(~c" <%# comment %> \n123", [trim: true] ++ @opts) == {:ok, exprs} - end - test "trim mode with multi-line comment" do exprs = [ {:comment, ~c" comment ", %{column: 3, line: 1}}, @@ -384,9 +345,6 @@ defmodule EEx.TokenizerTest do assert EEx.tokenize(~c"foo <% :bar", @opts) == {:error, message, %{column: 5, line: 1}} - assert EEx.tokenize(~c"<%# true ", @opts) == - {:error, "expected closing '%>' for EEx expression", %{column: 1, line: 1}} - message = """ expected closing '--%>' for EEx expression | diff --git a/lib/eex/test/eex_test.exs b/lib/eex/test/eex_test.exs index 8436bd65b3b..b4621ca01a7 100644 --- a/lib/eex/test/eex_test.exs +++ b/lib/eex/test/eex_test.exs @@ -198,15 +198,6 @@ defmodule EExTest do assert_eval("foo baz", "foo <%= if false do %>bar<% else %>baz<% end %>") end - test "embedded code with comments in do end" do - assert_eval("foo bar", "foo <%= case true do %><%# comment %><% true -> %>bar<% end %>") - - assert_eval( - "foo\n\nbar\n", - "foo\n<%= case true do %>\n<%# comment %>\n<% true -> %>\nbar\n<% end %>" - ) - end - test "embedded code with multi-line comments in do end" do assert_eval("foo bar", "foo <%= case true do %><%!-- comment --%><% true -> %>bar<% end %>") diff --git a/lib/elixir/lib/code/formatter.ex b/lib/elixir/lib/code/formatter.ex index fc8d8a9cd37..edee3ae9bb2 100644 --- a/lib/elixir/lib/code/formatter.ex +++ b/lib/elixir/lib/code/formatter.ex @@ -16,7 +16,7 @@ defmodule Code.Formatter do @ampersand_prec Code.Identifier.unary_op(:&) |> elem(1) # Operators that are composed of multiple binary operators - @multi_binary_operators [:"..//"] + @multi_binary_operators [:..//] # Operators that do not have space between operands @no_space_binary_operators [:.., :"//"] @@ -492,7 +492,7 @@ defmodule Code.Formatter do end # 1..2//3 - defp quoted_to_algebra({:"..//", meta, [left, middle, right]}, context, state) do + defp quoted_to_algebra({:..//, meta, [left, middle, right]}, context, state) do quoted_to_algebra({:"//", meta, [{:.., meta, [left, middle]}, right]}, context, state) end @@ -524,10 +524,6 @@ defmodule Code.Formatter do if keyword_key?(left_arg) do {left, state} = case left_arg do - # TODO: Remove this clause in v1.18 when we no longer quote operator :..// - {:__block__, _, [:"..//"]} -> - {string(~S{"..//":}), state} - {:__block__, _, [atom]} when is_atom(atom) -> iodata = if Macro.classify_atom(atom) in [:identifier, :unquoted] do @@ -1571,11 +1567,6 @@ defmodule Code.Formatter do Atom.to_string(nil) |> color(nil, inspect_opts) end - # TODO: Remove this clause in v1.18 when we no longer quote operator :..// - defp atom_to_algebra(:"..//", _, inspect_opts) do - string(":\"..//\"") |> color(:atom, inspect_opts) - end - defp atom_to_algebra(:\\, meta, inspect_opts) do # Since we parse strings without unescaping, the atoms # :\\ and :"\\" have the same representation, so we need diff --git a/lib/elixir/lib/code/normalizer.ex b/lib/elixir/lib/code/normalizer.ex index f86ca831424..6b6dfe5e1c0 100644 --- a/lib/elixir/lib/code/normalizer.ex +++ b/lib/elixir/lib/code/normalizer.ex @@ -58,7 +58,7 @@ defmodule Code.Normalizer do {:.., meta, [left, right]} else step = do_normalize(step, state) - {:"..//", meta, [left, right, step]} + {:..//, meta, [left, right, step]} end end diff --git a/lib/elixir/lib/exception.ex b/lib/elixir/lib/exception.ex index 9641b405cd6..9389f7f2199 100644 --- a/lib/elixir/lib/exception.ex +++ b/lib/elixir/lib/exception.ex @@ -346,7 +346,7 @@ defmodule Exception do defp rewrite_arg(arg) do Macro.prewalk(arg, fn {:%{}, meta, [__struct__: Range, first: first, last: last, step: step]} -> - {:"..//", meta, [first, last, step]} + {:..//, meta, [first, last, step]} other -> other diff --git a/lib/elixir/lib/macro.ex b/lib/elixir/lib/macro.ex index e6ded3f4eb7..7839a7c006d 100644 --- a/lib/elixir/lib/macro.ex +++ b/lib/elixir/lib/macro.ex @@ -1484,7 +1484,7 @@ defmodule Macro do :error end - defp op_call({:"..//", _, [left, middle, right]} = ast, fun) do + defp op_call({:..//, _, [left, middle, right]} = ast, fun) do left = op_to_string(left, fun, :.., :left) middle = op_to_string(middle, fun, :.., :right) right = op_to_string(right, fun, :"//", :right) @@ -1935,7 +1935,7 @@ defmodule Macro do @spec operator?(name :: atom(), arity()) :: boolean() def operator?(name, arity) - def operator?(:"..//", 3), + def operator?(:..//, 3), do: true # Code.Identifier treats :// as a binary operator for precedence @@ -2454,7 +2454,7 @@ defmodule Macro do # defp inner_classify(atom) when is_atom(atom) do cond do - atom in [:%, :%{}, :{}, :<<>>, :..., :.., :., :"..//", :->] -> + atom in [:%, :%{}, :{}, :<<>>, :..., :.., :., :..//, :->] -> :not_callable # <|>, ^^^, and ~~~ are deprecated diff --git a/lib/elixir/test/elixir/code_formatter/literals_test.exs b/lib/elixir/test/elixir/code_formatter/literals_test.exs index 7f5a7632faf..99b3456bde5 100644 --- a/lib/elixir/test/elixir/code_formatter/literals_test.exs +++ b/lib/elixir/test/elixir/code_formatter/literals_test.exs @@ -112,10 +112,6 @@ defmodule Code.Formatter.LiteralsTest do test "quoted operators" do assert_same ~S[:"::"] - assert_same ~S[:"..//"] - assert_format ~S[:..//], ~S[:"..//"] - assert_format ~S{[..//: 1]}, ~S{["..//": 1]} - assert_same ~S{["..//": 1]} end test "uses double quotes even when single quotes are used" do diff --git a/lib/elixir/test/elixir/kernel/parser_test.exs b/lib/elixir/test/elixir/kernel/parser_test.exs index 6d5536c8938..72a1d615111 100644 --- a/lib/elixir/test/elixir/kernel/parser_test.exs +++ b/lib/elixir/test/elixir/kernel/parser_test.exs @@ -66,13 +66,13 @@ defmodule Kernel.ParserTest do describe "ternary ops" do test "root" do - assert parse!("1..2//3") == {:"..//", [line: 1], [1, 2, 3]} - assert parse!("(1..2)//3") == {:"..//", [line: 1], [1, 2, 3]} + assert parse!("1..2//3") == {:..//, [line: 1], [1, 2, 3]} + assert parse!("(1..2)//3") == {:..//, [line: 1], [1, 2, 3]} end test "with do-blocks" do assert parse!("foo do end..bar do end//baz do end") == { - :"..//", + :..//, [line: 1], [ {:foo, [line: 1], [[do: {:__block__, [], []}]]}, @@ -84,7 +84,7 @@ defmodule Kernel.ParserTest do test "with no parens" do assert parse!("1..foo do end//bar bat") == { - :"..//", + :..//, [line: 1], [ 1, diff --git a/lib/logger/lib/logger.ex b/lib/logger/lib/logger.ex index a29c5b6628c..14ee9f6f9e8 100644 --- a/lib/logger/lib/logger.ex +++ b/lib/logger/lib/logger.ex @@ -564,7 +564,7 @@ defmodule Logger do delete_process_level(pid) """ - # TODO: Deprecate me on v1.18 + # TODO: Deprecate me on v1.19 @doc deprecated: "Use Logger.delete_process_level(pid) instead" @spec enable(pid) :: :ok def enable(pid) when pid == self() do @@ -580,7 +580,7 @@ defmodule Logger do put_process_level(pid, :none) """ - # TODO: Deprecate me on v1.18 + # TODO: Deprecate me on v1.20 @doc deprecated: "Use Logger.put_process_level(pid, :none) instead" @spec disable(pid) :: :ok def disable(pid) when pid == self() do @@ -592,7 +592,7 @@ defmodule Logger do Currently the only accepted PID is `self()`. """ - # TODO: Deprecate me on v1.18 + # TODO: Deprecate me on v1.20 @doc deprecated: "Use Logger.get_process_level(pid) instead" @spec enabled?(pid) :: boolean def enabled?(pid) when pid == self() do @@ -828,7 +828,7 @@ defmodule Logger do @doc """ Adds a new backend. """ - # TODO: Deprecate this on Elixir v1.19 + # TODO: Deprecate this on Elixir v1.20 @doc deprecated: "Use LoggerBackends.add/2 from :logger_backends dependency" def add_backend(backend, opts \\ []) do Logger.Backends.Internal.add(backend, opts) @@ -837,7 +837,7 @@ defmodule Logger do @doc """ Removes a backend. """ - # TODO: Deprecate this on Elixir v1.19 + # TODO: Deprecate this on Elixir v1.20 @doc deprecated: "Use LoggerBackends.remove/2 from :logger_backends dependency" def remove_backend(backend, opts \\ []) do Logger.Backends.Internal.remove(backend, opts) @@ -846,7 +846,7 @@ defmodule Logger do @doc """ Configures the given backend. """ - # TODO: Deprecate this on Elixir v1.19 + # TODO: Deprecate this on Elixir v1.20 @doc deprecated: "Use LoggerBackends.configure/2 from :logger_backends dependency" def configure_backend(:console, options) when is_list(options) do options = Keyword.merge(Application.get_env(:logger, :console, []), options) diff --git a/lib/mix/lib/mix/tasks/cmd.ex b/lib/mix/lib/mix/tasks/cmd.ex index 63cb96fcb4a..cfe43466db2 100644 --- a/lib/mix/lib/mix/tasks/cmd.ex +++ b/lib/mix/lib/mix/tasks/cmd.ex @@ -33,9 +33,6 @@ defmodule Mix.Tasks.Cmd do ## Command line options - * `--app` - limit running the command to the given app. - This option is currently deprecated in favor of `mix do --app` - * `--cd` *(since v1.10.4)* - the directory to run the command in ## Zombie operating system processes @@ -60,12 +57,15 @@ defmodule Mix.Tasks.Cmd do def run(args) do {opts, args} = OptionParser.parse_head!(args, strict: @switches) - # TODO: Deprecate `--app` flag in Elixir v1.18 apps = opts |> Keyword.get_values(:app) |> Enum.map(&String.to_atom/1) + if apps != [] do + IO.warn("the --app in mix cmd is deprecated") + end + if apps == [] or Mix.Project.config()[:app] in apps do cmd_opts = Keyword.take(opts, [:cd]) diff --git a/lib/mix/lib/mix/tasks/do.ex b/lib/mix/lib/mix/tasks/do.ex index 924b99d8f81..c68fcb1c236 100644 --- a/lib/mix/lib/mix/tasks/do.ex +++ b/lib/mix/lib/mix/tasks/do.ex @@ -49,7 +49,7 @@ defmodule Mix.Tasks.Do do """ - # TODO: Deprecate using comma on Elixir v1.18 + # TODO: Deprecate using comma on Elixir v1.19 @impl true def run(args) do diff --git a/lib/mix/test/mix/tasks/cmd_test.exs b/lib/mix/test/mix/tasks/cmd_test.exs index ac5360e818a..be437998539 100644 --- a/lib/mix/test/mix/tasks/cmd_test.exs +++ b/lib/mix/test/mix/tasks/cmd_test.exs @@ -25,39 +25,45 @@ defmodule Mix.Tasks.CmdTest do end test "runs the command for a single app specified by app flag" do - in_fixture("umbrella_dep/deps/umbrella", fn -> - Mix.Project.in_project(:umbrella, ".", fn _ -> - Mix.Task.run("cmd", ["--app", "bar", "echo", "hello"]) - nl = os_newline() - assert_received {:mix_shell, :info, ["==> bar"]} - assert_received {:mix_shell, :run, ["hello" <> ^nl]} - refute_received {:mix_shell, :info, ["==> foo"]} - refute_received {:mix_shell, :run, ["hello" <> ^nl]} + ExUnit.CaptureIO.capture_io(:stderr, fn -> + in_fixture("umbrella_dep/deps/umbrella", fn -> + Mix.Project.in_project(:umbrella, ".", fn _ -> + Mix.Task.run("cmd", ["--app", "bar", "echo", "hello"]) + nl = os_newline() + assert_received {:mix_shell, :info, ["==> bar"]} + assert_received {:mix_shell, :run, ["hello" <> ^nl]} + refute_received {:mix_shell, :info, ["==> foo"]} + refute_received {:mix_shell, :run, ["hello" <> ^nl]} + end) end) end) end test "runs the command for each app specified by app flag" do - in_fixture("umbrella_dep/deps/umbrella", fn -> - Mix.Project.in_project(:umbrella, ".", fn _ -> - Mix.Task.run("cmd", ["--app", "bar", "--app", "foo", "echo", "hello"]) - nl = os_newline() - assert_received {:mix_shell, :info, ["==> bar"]} - assert_received {:mix_shell, :run, ["hello" <> ^nl]} - assert_received {:mix_shell, :info, ["==> foo"]} - assert_received {:mix_shell, :run, ["hello" <> ^nl]} + ExUnit.CaptureIO.capture_io(:stderr, fn -> + in_fixture("umbrella_dep/deps/umbrella", fn -> + Mix.Project.in_project(:umbrella, ".", fn _ -> + Mix.Task.run("cmd", ["--app", "bar", "--app", "foo", "echo", "hello"]) + nl = os_newline() + assert_received {:mix_shell, :info, ["==> bar"]} + assert_received {:mix_shell, :run, ["hello" <> ^nl]} + assert_received {:mix_shell, :info, ["==> foo"]} + assert_received {:mix_shell, :run, ["hello" <> ^nl]} + end) end) end) end test "only runs the cmd for specified apps and in specific directory" do - in_fixture("umbrella_dep/deps/umbrella", fn -> - Mix.Project.in_project(:umbrella, ".", fn _ -> - Mix.Task.run("cmd", ["--app", "bar", "--cd", "lib", "pwd"]) - assert_received {:mix_shell, :info, ["==> bar"]} - {pwd, 0} = System.cmd("pwd", [], cd: Path.join(["apps", "bar", "lib"])) - assert_received {:mix_shell, :run, [^pwd]} - refute_received {:mix_shell, :info, ["==> foo"]} + ExUnit.CaptureIO.capture_io(:stderr, fn -> + in_fixture("umbrella_dep/deps/umbrella", fn -> + Mix.Project.in_project(:umbrella, ".", fn _ -> + Mix.Task.run("cmd", ["--app", "bar", "--cd", "lib", "pwd"]) + assert_received {:mix_shell, :info, ["==> bar"]} + {pwd, 0} = System.cmd("pwd", [], cd: Path.join(["apps", "bar", "lib"])) + assert_received {:mix_shell, :run, [^pwd]} + refute_received {:mix_shell, :info, ["==> foo"]} + end) end) end) end