diff options
author | José Valim <jose.valim@plataformatec.com.br> | 2018-05-08 16:28:24 +0200 |
---|---|---|
committer | José Valim <jose.valim@plataformatec.com.br> | 2018-07-12 12:12:00 +0200 |
commit | 35ac568044aca779d2c000f39cee974cae9cf0c0 (patch) | |
tree | d4dba95282e31cc7db0092fc3fdb5ce85c5b575b | |
parent | 7b9cb85646355ec0706dd0e0fc165c937d5cbba7 (diff) | |
download | elixir-35ac568044aca779d2c000f39cee974cae9cf0c0.tar.gz |
Require Erlang/OTP 20+
36 files changed, 626 insertions, 905 deletions
diff --git a/.travis.yml b/.travis.yml index c6be9892e..cf9733d02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,10 +5,6 @@ env: global: - ELIXIR_ASSERT_TIMEOUT=2000 matrix: - - OTP_RELEASE=OTP-19.0 - - OTP_RELEASE=OTP-19.1 - - OTP_RELEASE=OTP-19.2 - - OTP_RELEASE=OTP-19.3 - OTP_RELEASE=OTP-20.0 - OTP_RELEASE=OTP-20.1 - OTP_RELEASE=OTP-20.2 @@ -22,9 +22,9 @@ GIT_TAG = $(strip $(shell head="$(call GIT_REVISION)"; git tag --points-at $$hea #==> Functions define CHECK_ERLANG_RELEASE - erl -noshell -eval '{V,_} = string:to_integer(erlang:system_info(otp_release)), io:fwrite("~s", [is_integer(V) and (V >= 19)])' -s erlang halt | grep -q '^true'; \ + erl -noshell -eval '{V,_} = string:to_integer(erlang:system_info(otp_release)), io:fwrite("~s", [is_integer(V) and (V >= 20)])' -s erlang halt | grep -q '^true'; \ if [ $$? != 0 ]; then \ - echo "At least Erlang/OTP 19.0 is required to build Elixir"; \ + echo "At least Erlang/OTP 20.0 is required to build Elixir"; \ exit 1; \ fi endef @@ -30,10 +30,10 @@ If tests pass, you are ready to move on to the [Getting Started guide][1] or to try Interactive Elixir by running `bin/iex` in your terminal. However, if tests fail, it is likely you have an outdated Erlang/OTP version -(Elixir requires Erlang/OTP 19.0 or later). You can check your Erlang/OTP version +(Elixir requires Erlang/OTP 20.0 or later). You can check your Erlang/OTP version by calling `erl` in the command line. You will see some information as follows: - Erlang/OTP 19 [erts-8.0] [smp:2:2] [async-threads:10] [kernel-poll:false] + Erlang/OTP 20 [erts-9.0] [smp:2:2] [async-threads:10] [kernel-poll:false] If you have properly set up your dependencies and tests still fail, you may want to open up a bug report, as explained next. diff --git a/lib/elixir/lib/application.ex b/lib/elixir/lib/application.ex index 1342d9d08..951986bc8 100644 --- a/lib/elixir/lib/application.ex +++ b/lib/elixir/lib/application.ex @@ -197,9 +197,9 @@ defmodule Application do Shutting down a live system cleanly can be done by calling `System.stop/1`. It will shut down every application in the opposite order they had been started. - From Erlang/OTP 19.1, a SIGTERM from the operating system will automatically - translate to `System.stop/0`. Erlang/OTP 20 gives user more explicit control - over OS signals via the `:os.set_signal/2` function. + By default, a SIGTERM from the operating system will automatically translate to + `System.stop/0`. You can also have more explicit control over OS signals via the + `:os.set_signal/2` function. ## Tooling diff --git a/lib/elixir/lib/code/typespec.ex b/lib/elixir/lib/code/typespec.ex index 581c19d52..af33699e2 100644 --- a/lib/elixir/lib/code/typespec.ex +++ b/lib/elixir/lib/code/typespec.ex @@ -152,37 +152,23 @@ defmodule Code.Typespec do end end - # TODO: Do not rely on abstract_code when OTP 20+ support is dropped (v1.8). - # We should then be able to simplify this code and use `with`. defp typespecs_abstract_code(module) do - case get_module_and_beam(module) do - {module, binary} -> - case :beam_lib.chunks(binary, [:debug_info]) do - {:ok, {_, [debug_info: {:debug_info_v1, backend, data}]}} -> - case data do - {:elixir_v1, %{}, specs} -> - # Fast path to avoid translation to Erlang from Elixir. - {:ok, specs} - - _ -> - case backend.debug_info(:erlang_v1, module, data, []) do - {:ok, abstract_code} -> {:ok, abstract_code} - _ -> :error - end - end - - _ -> - case :beam_lib.chunks(binary, [:abstract_code]) do - {:ok, {_, [{:abstract_code, {_raw_abstract_v1, abstract_code}}]}} -> - {:ok, abstract_code} - - _ -> - :error - end - end - - :error -> - :error + with {module, binary} <- get_module_and_beam(module), + {:ok, {_, [debug_info: {:debug_info_v1, backend, data}]}} <- + :beam_lib.chunks(binary, [:debug_info]) do + case data do + {:elixir_v1, %{}, specs} -> + # Fast path to avoid translation to Erlang from Elixir. + {:ok, specs} + + _ -> + case backend.debug_info(:erlang_v1, module, data, []) do + {:ok, abstract_code} -> {:ok, abstract_code} + _ -> :error + end + end + else + _ -> :error end end diff --git a/lib/elixir/lib/exception.ex b/lib/elixir/lib/exception.ex index 9e78ef89a..297a5bccc 100644 --- a/lib/elixir/lib/exception.ex +++ b/lib/elixir/lib/exception.ex @@ -186,8 +186,6 @@ defmodule Exception do This function returns either `{:ok, definition, clauses}` or `:error`. Where `definition` is `:def`, `:defp`, `:defmacro` or `:defmacrop`. - Note this functionality requires Erlang/OTP 20, otherwise `:error` - is always returned. """ @doc since: "1.5.0" @spec blame_mfa(module, function, args :: [term]) :: diff --git a/lib/elixir/lib/protocol.ex b/lib/elixir/lib/protocol.ex index 59c3261c1..4ecd91613 100644 --- a/lib/elixir/lib/protocol.ex +++ b/lib/elixir/lib/protocol.ex @@ -320,10 +320,6 @@ defmodule Protocol do {:compile_info, compile_info} | extra_chunks ] = entries - extra_chunks = - for {name, contents} when is_binary(contents) <- extra_chunks, - do: {List.to_string(name), contents} - case attributes[:protocol] do [fallback_to_any: any] -> {:ok, {protocol, any, abstract_code}, {compile_info, extra_chunks}} @@ -499,7 +495,15 @@ defmodule Protocol do opts = Keyword.take(compile_info, [:source]) opts = if Code.compiler_options()[:debug_info], do: [:debug_info | opts], else: opts {:ok, ^protocol, binary, _warnings} = :compile.forms(code, [:return | opts]) - {:ok, :elixir_erl.add_beam_chunks(binary, extra_chunks)} + {:ok, add_beam_chunks(binary, extra_chunks)} + end + + defp add_beam_chunks(bin, []), do: bin + + defp add_beam_chunks(bin, new_chunks) do + {:ok, _, old_chunks} = :beam_lib.all_chunks(bin) + {:ok, bin} = :beam_lib.build_module(new_chunks ++ old_chunks) + bin end ## Definition callbacks diff --git a/lib/elixir/lib/regex.ex b/lib/elixir/lib/regex.ex index 1183f2ba8..201da459b 100644 --- a/lib/elixir/lib/regex.ex +++ b/lib/elixir/lib/regex.ex @@ -211,13 +211,8 @@ defmodule Regex do """ @doc since: "1.4.0" @spec version :: term() - # TODO: No longer check for function_exported? on OTP 20+. def version do - if function_exported?(:re, :version, 0) do - {:re.version(), :erlang.system_info(:endian)} - else - {"8.33 2013-05-29", :erlang.system_info(:endian)} - end + {:re.version(), :erlang.system_info(:endian)} end @doc """ diff --git a/lib/elixir/lib/string.ex b/lib/elixir/lib/string.ex index 6e21ab7d2..ff4718c50 100644 --- a/lib/elixir/lib/string.ex +++ b/lib/elixir/lib/string.ex @@ -2130,8 +2130,7 @@ defmodule String do By default, the maximum number of atoms is `1_048_576`. This limit can be raised or lowered using the VM option `+t`. - The maximum atom size is of 255 characters. Prior to Erlang/OTP 20, - only latin1 characters are allowed. + The maximum atom size is of 255 unicode characters. Inlined by the compiler. @@ -2149,8 +2148,7 @@ defmodule String do @doc """ Converts a string to an existing atom. - The maximum atom size is of 255 characters. Prior to Erlang/OTP 20, - only latin1 characters are allowed. + The maximum atom size is of 255 unicode characters. Inlined by the compiler. diff --git a/lib/elixir/lib/system.ex b/lib/elixir/lib/system.ex index 80ac75ac1..af5038d39 100644 --- a/lib/elixir/lib/system.ex +++ b/lib/elixir/lib/system.ex @@ -73,10 +73,6 @@ defmodule System do parts_per_second` seconds. For example, using the `:millisecond` time unit is equivalent to using `1000` as the time unit (as the time will be returned in 1/1000 seconds - milliseconds). - - Keep in mind the Erlang API prior to version 19.1 will use `:milli_seconds`, - `:micro_seconds` and `:nano_seconds` as time units although Elixir normalizes - their spelling to match the SI convention. """ # TODO: Warn all old mappings once Elixir requires Erlang/OTP 19.1+ (on v1.8) @type time_unit :: diff --git a/lib/elixir/lib/task/supervised.ex b/lib/elixir/lib/task/supervised.ex index 1f092d6f0..1d00dba4a 100644 --- a/lib/elixir/lib/task/supervised.ex +++ b/lib/elixir/lib/task/supervised.ex @@ -83,43 +83,25 @@ defmodule Task.Supervised do {mod, fun, length(args)} end - # TODO: Remove conditionals once we depend on Erlang/OTP 20+ defp do_apply(info, {module, fun, args} = mfa) do try do apply(module, fun, args) catch - :error, value -> - reason = {value, __STACKTRACE__} - log(info, mfa, reason) - - if :erlang.system_info(:otp_release) >= '20' do - :erlang.raise(:error, value, __STACKTRACE__) - else - exit(reason) - end - - :throw, value -> - reason = {{:nocatch, value}, __STACKTRACE__} - log(info, mfa, reason) - - if :erlang.system_info(:otp_release) >= '20' do - :erlang.raise(:throw, value, __STACKTRACE__) - else - exit(reason) - end - :exit, value when value == :normal when value == :shutdown when tuple_size(value) == 2 and elem(value, 0) == :shutdown -> :erlang.raise(:exit, value, __STACKTRACE__) - :exit, value -> - log(info, mfa, {value, __STACKTRACE__}) - :erlang.raise(:exit, value, __STACKTRACE__) + kind, value -> + log(info, mfa, {log_value(kind, value), __STACKTRACE__}) + :erlang.raise(kind, value, __STACKTRACE__) end end + defp log_value(:throw, value), do: {:nocatch, value} + defp log_value(_, value), do: value + defp log(info, mfa, reason) do {fun, args} = get_running(mfa) diff --git a/lib/elixir/pages/Syntax Reference.md b/lib/elixir/pages/Syntax Reference.md index 74cbd2502..c009c9a07 100644 --- a/lib/elixir/pages/Syntax Reference.md +++ b/lib/elixir/pages/Syntax Reference.md @@ -21,7 +21,7 @@ Integers (`1234`) and floats (`123.4`) in Elixir are represented as a sequence o ### Atoms -Atoms in Elixir start with a colon (`:`) which must be followed by non-combining Unicode characters and underscore. The atom may continue using a sequence of Unicode characters, including numbers, underscore and `@`. Atoms may end in `!` or `?`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. Unicode characters require Erlang/OTP 20. +Atoms in Elixir start with a colon (`:`) which must be followed by non-combining Unicode characters and underscore. The atom may continue using a sequence of Unicode characters, including numbers, underscore and `@`. Atoms may end in `!` or `?`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. All operators in Elixir are also valid atoms. Valid examples are `:foo`, `:FOO`, `:foo_42`, `:foo@bar` and `:++`. Invalid examples are `:@foo` (`@` is not allowed at start), `:123` (numbers are not allowed at start) and `:(*)` (not a valid operator). @@ -80,13 +80,13 @@ Structs built on the map syntax by passing the struct name between `%` and `{`. ### Variables -Variables in Elixir must start with underscore or a non-combining Unicode character that is not in uppercase or titlecase. The variable may continue using a sequence of Unicode characters, including numbers and underscore. Variables may end in `?` or `!`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. Unicode characters require Erlang/OTP 20. +Variables in Elixir must start with underscore or a non-combining Unicode character that is not in uppercase or titlecase. The variable may continue using a sequence of Unicode characters, including numbers and underscore. Variables may end in `?` or `!`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. [Elixir's naming conventions](naming-conventions.html) recommend variables to be in `snake_case` format. ### Non-qualified calls (local calls) -Non-qualified calls, such as `add(1, 2)`, must start with underscore or a non-combining Unicode character that is not in uppercase or titlecase. The call may continue using a sequence of Unicode characters, including numbers and underscore. Calls may end in `?` or `!`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. Unicode characters require Erlang/OTP 20. +Non-qualified calls, such as `add(1, 2)`, must start with underscore or a non-combining Unicode character that is not in uppercase or titlecase. The call may continue using a sequence of Unicode characters, including numbers and underscore. Calls may end in `?` or `!`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. Parentheses for non-qualified calls are optional, except for zero-arity calls, which would then be ambiguous with variables. If parentheses are used, they must immediately follow the function name *without spaces*. For example, `add (1, 2)` is a syntax error, since `(1, 2)` is treated as an invalid block which is attempted to be given as a single argument to `add`. @@ -98,7 +98,7 @@ As many programming languages, Elixir also support operators as non-qualified ca ### Qualified calls (remote calls) -Qualified calls, such as `Math.add(1, 2)`, must start with underscore or a non-combining Unicode character that is not in uppercase or titlecase. The call may continue using a sequence of Unicode characters, including numbers and underscore. Calls may end in `?` or `!`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. Unicode characters require Erlang/OTP 20. +Qualified calls, such as `Math.add(1, 2)`, must start with underscore or a non-combining Unicode character that is not in uppercase or titlecase. The call may continue using a sequence of Unicode characters, including numbers and underscore. Calls may end in `?` or `!`. See [Unicode Syntax](unicode-syntax.html) for a formal specification. [Elixir's naming conventions](naming-conventions.html) recommend calls to be in `snake_case` format. diff --git a/lib/elixir/src/elixir.erl b/lib/elixir/src/elixir.erl index ce2f4c257..3e9967959 100644 --- a/lib/elixir/src/elixir.erl +++ b/lib/elixir/src/elixir.erl @@ -33,9 +33,8 @@ start(_Type, _Args) -> check_file_encoding(Encoding), check_endianness(), - %% TODO: Remove OTPRelease check once we support OTP 20+. Tokenizer = case code:ensure_loaded('Elixir.String.Tokenizer') of - {module, Mod} when OTPRelease >= 20 -> Mod; + {module, Mod} -> Mod; _ -> elixir_tokenizer end, @@ -103,21 +102,15 @@ preload_common_modules() -> %% the codebase we can avoid code:ensure_loaded/1 checks. _ = code:ensure_loaded('Elixir.Kernel'), _ = code:ensure_loaded('Elixir.Macro.Env'), - - %% We need to make sure the re module is preloaded to make - %% function_exported checks inside Regex.version is fast. - %% TODO: Remove this once we support OTP 20+. - _ = code:ensure_loaded(re), - ok. parse_otp_release() -> %% Whenever we change this check, we should also change escript.build and Makefile. case string:to_integer(erlang:system_info(otp_release)) of - {Num, _} when Num >= 19 -> + {Num, _} when Num >= 20 -> Num; _ -> - io:format(standard_error, "unsupported Erlang/OTP version, expected Erlang/OTP 19+~n", []), + io:format(standard_error, "unsupported Erlang/OTP version, expected Erlang/OTP 20+~n", []), erlang:halt(1) end. diff --git a/lib/elixir/src/elixir_erl.erl b/lib/elixir/src/elixir_erl.erl index ac45bff87..671c34e69 100644 --- a/lib/elixir/src/elixir_erl.erl +++ b/lib/elixir/src/elixir_erl.erl @@ -1,20 +1,9 @@ %% Compiler backend to Erlang. -module(elixir_erl). -export([elixir_to_erl/1, definition_to_anonymous/4, compile/1, - get_ann/1, remote/4, add_beam_chunks/2, debug_info/4, - scope/1, format_error/1]). + get_ann/1, remote/4, debug_info/4, scope/1, format_error/1]). -include("elixir.hrl"). -%% TODO: Remove extra chunk functionality when OTP 20+. - -add_beam_chunks(Bin, []) when is_binary(Bin) -> - Bin; -add_beam_chunks(Bin, NewChunks) when is_binary(Bin), is_list(NewChunks) -> - {ok, _, OldChunks} = beam_lib:all_chunks(Bin), - Chunks = [{binary_to_list(K), V} || {K, V} <- NewChunks] ++ OldChunks, - {ok, NewBin} = beam_lib:build_module(Chunks), - NewBin. - %% debug_info callback debug_info(elixir_v1, _Module, none, _Opts) -> @@ -268,43 +257,14 @@ add_info_function(Line, Module, Def, Defmacro, Deprecated) -> AllowedArgs = lists:map(fun(Atom) -> {atom, Line, Atom} end, AllowedAttrs), Spec = - %% TODO: Remove this check once we depend only on 20 - case erlang:system_info(otp_release) of - "19" -> - {attribute, Line, spec, {{'__info__', 1}, - [{type, Line, 'fun', [ - {type, Line, product, [ - {type, Line, union, AllowedArgs} - ]}, - {type, Line, union, [ - {type, Line, atom, []}, - {type, Line, list, [ - {type, Line, union, [ - {type, Line, tuple, [ - {type, Line, atom, []}, - {type, Line, any, []} - ]}, - {type, Line, tuple, [ - {type, Line, atom, []}, - {type, Line, byte, []}, - {type, Line, integer, []} - ]} - ]} - ]} - ]} - ]}] - }}; - - _ -> - {attribute, Line, spec, {{'__info__', 1}, - [{type, Line, 'fun', [ - {type, Line, product, [ - {type, Line, union, AllowedArgs} - ]}, - {type, Line, any, []} - ]}] - }} - end, + {attribute, Line, spec, {{'__info__', 1}, + [{type, Line, 'fun', [ + {type, Line, product, [ + {type, Line, union, AllowedArgs} + ]}, + {type, Line, any, []} + ]}] + }}, Info = {function, 0, '__info__', 1, [ @@ -470,16 +430,14 @@ attributes_form(Line, Attributes, Forms) -> % Loading forms load_form(#{file := File, compile_opts := Opts} = Map, Prefix, Forms, Specs, Chunks) -> - {ExtraChunks, CompileOpts} = extra_chunks(Chunks, debug_opts(Map, Specs, Opts)), + CompileOpts = extra_chunks_opts(Chunks, debug_opts(Map, Specs, Opts)), {_, Binary} = elixir_erl_compiler:forms(Prefix ++ Specs ++ Forms, File, CompileOpts), - add_beam_chunks(Binary, ExtraChunks). + Binary. debug_opts(Map, Specs, Opts) -> - case {supports_debug_tuple(), include_debug_opts(Opts)} of - {true, true} -> [{debug_info, {?MODULE, {elixir_v1, Map, Specs}}}]; - {true, false} -> [{debug_info, {?MODULE, none}}]; - {false, true} -> [debug_info]; - {false, false} -> [] + case include_debug_opts(Opts) of + true -> [{debug_info, {?MODULE, {elixir_v1, Map, Specs}}}]; + false -> [{debug_info, {?MODULE, none}}] end. include_debug_opts(Opts) -> @@ -489,28 +447,8 @@ include_debug_opts(Opts) -> undefined -> elixir_compiler:get_opt(debug_info) end. -supports_debug_tuple() -> - case erlang:system_info(otp_release) of - "18" -> false; - "19" -> false; - _ -> true - end. - -extra_chunks(Chunks, Opts) -> - Supported = supports_extra_chunks_option(), - - case Chunks of - [] -> {[], Opts}; - _ when Supported -> {[], [{extra_chunks, Chunks} | Opts]}; - _ -> {Chunks, Opts} - end. - -supports_extra_chunks_option() -> - case erlang:system_info(otp_release) of - "18" -> false; - "19" -> false; - _ -> true - end. +extra_chunks_opts([], Opts) -> Opts; +extra_chunks_opts(Chunks, Opts) -> [{extra_chunks, Chunks} | Opts]. docs_chunk(Set, Module, Line, Def, Defmacro, Types, Callbacks) -> case elixir_compiler:get_opt(docs) of diff --git a/lib/elixir/src/elixir_erl_for.erl b/lib/elixir/src/elixir_erl_for.erl index b8a6dc161..40aec2e62 100644 --- a/lib/elixir/src/elixir_erl_for.erl +++ b/lib/elixir/src/elixir_erl_for.erl @@ -182,11 +182,10 @@ build_into(Ann, Clauses, Expr, Into, Uniq, S) -> {{block, Ann, [MatchExpr, TryExpr]}, SD}. +%% TODO: Remove this check once we support Erlang/OTP 21+ exclusively. stacktrace_clause(Ann, Fun, Acc, Kind, Reason, Stack) -> - Release = erlang:system_info(otp_release), - - if - Release == "19"; Release == "20" -> + case erlang:system_info(otp_release) of + "20" -> {clause, Ann, [{tuple, Ann, [Kind, Reason, {var, Ann, '_'}]}], [], @@ -194,7 +193,7 @@ stacktrace_clause(Ann, Fun, Acc, Kind, Reason, Stack) -> {call, Ann, Fun, [Acc, {atom, Ann, halt}]}, elixir_erl:remote(Ann, erlang, raise, [Kind, Reason, Stack])]}; - true -> + _ -> {clause, Ann, [{tuple, Ann, [Kind, Reason, Stack]}], [], diff --git a/lib/elixir/src/elixir_erl_try.erl b/lib/elixir/src/elixir_erl_try.erl index 601571b22..b101fc2f6 100644 --- a/lib/elixir/src/elixir_erl_try.erl +++ b/lib/elixir/src/elixir_erl_try.erl @@ -151,7 +151,6 @@ maybe_add_stacktrace(Line, Kind, Expr, Guards, Body, _) -> %% TODO: Remove this check once we support Erlang/OTP 21+ exclusively. supports_stacktrace() -> case erlang:system_info(otp_release) of - "19" -> false; "20" -> false; _ -> true end. diff --git a/lib/elixir/test/elixir/code_formatter/literals_test.exs b/lib/elixir/test/elixir/code_formatter/literals_test.exs index f48da2d16..f9c704ec4 100644 --- a/lib/elixir/test/elixir/code_formatter/literals_test.exs +++ b/lib/elixir/test/elixir/code_formatter/literals_test.exs @@ -94,11 +94,8 @@ defmodule Code.Formatter.LiteralsTest do assert_format ~S[:"double \" quote"], ~S[:"double \" quote"] end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "with unicode" do - assert_same ~S[:ólá] - end + test "with unicode" do + assert_same ~S[:ólá] end test "does not reformat aliases" do diff --git a/lib/elixir/test/elixir/exception_test.exs b/lib/elixir/test/elixir/exception_test.exs index 153e1109d..c6fe89258 100644 --- a/lib/elixir/test/elixir/exception_test.exs +++ b/lib/elixir/test/elixir/exception_test.exs @@ -179,20 +179,14 @@ defmodule ExceptionTest do assert Exception.format_mfa(Foo, :..., 1) == "Foo.\"...\"/1" end - # TODO: Remove this check once we depend only on 20 - # TODO: Remove String.to_atom/1 when we support 20+ - if :erlang.system_info(:otp_release) >= '20' do - test "format_mfa/3 with unicode" do - assert Exception.format_mfa(Foo, String.to_atom("olá"), [1, 2]) == "Foo.olá(1, 2)" - assert Exception.format_mfa(Foo, String.to_atom("Olá"), [1, 2]) == "Foo.\"Olá\"(1, 2)" - assert Exception.format_mfa(Foo, String.to_atom("Ólá"), [1, 2]) == "Foo.\"Ólá\"(1, 2)" - - hello_world = String.to_atom("こんにちは世界") - assert Exception.format_mfa(Foo, hello_world, [1, 2]) == "Foo.こんにちは世界(1, 2)" - - nfd = :unicode.characters_to_nfd_binary("olá") - assert Exception.format_mfa(Foo, String.to_atom(nfd), [1, 2]) == "Foo.\"#{nfd}\"(1, 2)" - end + test "format_mfa/3 with unicode" do + assert Exception.format_mfa(Foo, :olá, [1, 2]) == "Foo.olá(1, 2)" + assert Exception.format_mfa(Foo, :Olá, [1, 2]) == "Foo.\"Olá\"(1, 2)" + assert Exception.format_mfa(Foo, :Ólá, [1, 2]) == "Foo.\"Ólá\"(1, 2)" + assert Exception.format_mfa(Foo, :こんにちは世界, [1, 2]) == "Foo.こんにちは世界(1, 2)" + + nfd = :unicode.characters_to_nfd_binary("olá") + assert Exception.format_mfa(Foo, String.to_atom(nfd), [1, 2]) == "Foo.\"#{nfd}\"(1, 2)" end test "format_fa/2" do @@ -599,83 +593,80 @@ defmodule ExceptionTest do end end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "annotates function clause errors" do - args = [%{}, :key, nil] + test "annotates function clause errors" do + args = [%{}, :key, nil] - {exception, stack} = - Exception.blame(:error, :function_clause, [{Keyword, :pop, args, [line: 13]}]) + {exception, stack} = + Exception.blame(:error, :function_clause, [{Keyword, :pop, args, [line: 13]}]) - assert %FunctionClauseError{kind: :def, args: ^args, clauses: [_]} = exception - assert stack == [{Keyword, :pop, 3, [line: 13]}] - end + assert %FunctionClauseError{kind: :def, args: ^args, clauses: [_]} = exception + assert stack == [{Keyword, :pop, 3, [line: 13]}] + end - test "annotates args and clauses from mfa" do - import PathHelpers + test "annotates args and clauses from mfa" do + import PathHelpers - write_beam( - defmodule Blaming do - def with_elem(x, y) when elem(x, 1) == 0 and elem(x, y) == 1 do - {x, y} - end + write_beam( + defmodule Blaming do + def with_elem(x, y) when elem(x, 1) == 0 and elem(x, y) == 1 do + {x, y} + end - def fetch(%module{} = container, key), do: {module, container, key} - def fetch(map, key) when is_map(map), do: {map, key} - def fetch(list, key) when is_list(list) and is_atom(key), do: {list, key} - def fetch(nil, _key), do: nil + def fetch(%module{} = container, key), do: {module, container, key} + def fetch(map, key) when is_map(map), do: {map, key} + def fetch(list, key) when is_list(list) and is_atom(key), do: {list, key} + def fetch(nil, _key), do: nil - require Integer - def even_and_odd(foo, bar) when Integer.is_even(foo) and Integer.is_odd(bar), do: :ok - end - ) + require Integer + def even_and_odd(foo, bar) when Integer.is_even(foo) and Integer.is_odd(bar), do: :ok + end + ) - :code.delete(Blaming) - :code.purge(Blaming) + :code.delete(Blaming) + :code.purge(Blaming) - {:ok, :def, clauses} = Exception.blame_mfa(Blaming, :with_elem, [1, 2]) + {:ok, :def, clauses} = Exception.blame_mfa(Blaming, :with_elem, [1, 2]) - assert annotated_clauses_to_string(clauses) == [ - "{[+x+, +y+], [-elem(x, 1) == 0- and -elem(x, y) == 1-]}" - ] + assert annotated_clauses_to_string(clauses) == [ + "{[+x+, +y+], [-elem(x, 1) == 0- and -elem(x, y) == 1-]}" + ] - {:ok, :def, clauses} = Exception.blame_mfa(Blaming, :fetch, [self(), "oops"]) + {:ok, :def, clauses} = Exception.blame_mfa(Blaming, :fetch, [self(), "oops"]) - assert annotated_clauses_to_string(clauses) == [ - "{[-%module{} = container-, +key+], []}", - "{[+map+, +key+], [-is_map(map)-]}", - "{[+list+, +key+], [-is_list(list)- and -is_atom(key)-]}", - "{[-nil-, +_key+], []}" - ] + assert annotated_clauses_to_string(clauses) == [ + "{[-%module{} = container-, +key+], []}", + "{[+map+, +key+], [-is_map(map)-]}", + "{[+list+, +key+], [-is_list(list)- and -is_atom(key)-]}", + "{[-nil-, +_key+], []}" + ] - {:ok, :def, clauses} = Exception.blame_mfa(Blaming, :even_and_odd, [1, 1]) + {:ok, :def, clauses} = Exception.blame_mfa(Blaming, :even_and_odd, [1, 1]) - assert annotated_clauses_to_string(clauses) == [ - "{[+foo+, +bar+], [+is_integer(foo)+ and -Bitwise.band(foo, 1) == 0- and (+is_integer(bar)+ and +Bitwise.band(bar, 1) == 1+)]}" - ] + assert annotated_clauses_to_string(clauses) == [ + "{[+foo+, +bar+], [+is_integer(foo)+ and -Bitwise.band(foo, 1) == 0- and (+is_integer(bar)+ and +Bitwise.band(bar, 1) == 1+)]}" + ] - {:ok, :defmacro, clauses} = Exception.blame_mfa(Kernel, :!, [true]) + {:ok, :defmacro, clauses} = Exception.blame_mfa(Kernel, :!, [true]) - assert annotated_clauses_to_string(clauses) == [ - "{[-{:!, _, [value]}-], []}", - "{[+value+], []}" - ] - end + assert annotated_clauses_to_string(clauses) == [ + "{[-{:!, _, [value]}-], []}", + "{[+value+], []}" + ] + end - defp annotated_clauses_to_string(clauses) do - Enum.map(clauses, fn args_and_clauses -> - Macro.to_string(args_and_clauses, fn - %{match?: true, node: node}, _string -> - "+" <> Macro.to_string(node) <> "+" + defp annotated_clauses_to_string(clauses) do + Enum.map(clauses, fn args_and_clauses -> + Macro.to_string(args_and_clauses, fn + %{match?: true, node: node}, _string -> + "+" <> Macro.to_string(node) <> "+" - %{match?: false, node: node}, _string -> - "-" <> Macro.to_string(node) <> "-" + %{match?: false, node: node}, _string -> + "-" <> Macro.to_string(node) <> "-" - _node, string -> - string - end) + _node, string -> + string end) - end + end) end end @@ -738,28 +729,25 @@ defmodule ExceptionTest do |> message == "no function clause matching in Foo.bar/1" end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "FunctionClauseError with blame" do - {exception, _} = - Exception.blame(:error, :function_clause, [{Access, :fetch, [:foo, :bar], [line: 13]}]) + test "FunctionClauseError with blame" do + {exception, _} = + Exception.blame(:error, :function_clause, [{Access, :fetch, [:foo, :bar], [line: 13]}]) - assert message(exception) =~ """ - no function clause matching in Access.fetch/2 + assert message(exception) =~ """ + no function clause matching in Access.fetch/2 - The following arguments were given to Access.fetch/2: + The following arguments were given to Access.fetch/2: - # 1 - :foo + # 1 + :foo - # 2 - :bar + # 2 + :bar - Attempted function clauses (showing 5 out of 5): + Attempted function clauses (showing 5 out of 5): - def fetch(-%module{} = container-, key) - """ - end + def fetch(-%module{} = container-, key) + """ end test "ErlangError" do diff --git a/lib/elixir/test/elixir/inspect_test.exs b/lib/elixir/test/elixir/inspect_test.exs index b4123fc15..f7a4b8e68 100644 --- a/lib/elixir/test/elixir/inspect_test.exs +++ b/lib/elixir/test/elixir/inspect_test.exs @@ -86,20 +86,14 @@ defmodule Inspect.AtomTest do assert inspect(:hello, opts) == ":hello" end - # TODO: Remove this check once we depend only on 20 - # TODO: Remove String.to_atom/1 calls when we support 20+ - if :erlang.system_info(:otp_release) >= '20' do - test "unicode" do - assert inspect(String.to_atom("olá")) == ":olá" - assert inspect(String.to_atom("Olá")) == ":Olá" - assert inspect(String.to_atom("Ólá")) == ":Ólá" - - hello_world = String.to_atom("こんにちは世界") - assert inspect(hello_world) == ":こんにちは世界" - - nfd = :unicode.characters_to_nfd_binary("olá") - assert inspect(String.to_atom(nfd)) == ":\"#{nfd}\"" - end + test "unicode" do + assert inspect(:olá) == ":olá" + assert inspect(:Olá) == ":Olá" + assert inspect(:Ólá) == ":Ólá" + assert inspect(:こんにちは世界) == ":こんにちは世界" + + nfd = :unicode.characters_to_nfd_binary("olá") + assert inspect(String.to_atom(nfd)) == ":\"#{nfd}\"" end end diff --git a/lib/elixir/test/elixir/kernel/binary_test.exs b/lib/elixir/test/elixir/kernel/binary_test.exs index b8dbf14be..8dd1bf2ab 100644 --- a/lib/elixir/test/elixir/kernel/binary_test.exs +++ b/lib/elixir/test/elixir/kernel/binary_test.exs @@ -165,9 +165,8 @@ defmodule Kernel.BinaryTest do end test "literal" do - # TODO: Remove String.to_atom/1 when we support 20+ assert <<106, 111, 115, 195, 169>> == <<"josé">> - assert <<106, 111, 115, 195, 169>> == <<"#{String.to_atom("josé")}">> + assert <<106, 111, 115, 195, 169>> == <<"#{:josé}">> assert <<106, 111, 115, 195, 169>> == <<"josé"::binary>> assert <<106, 111, 115, 195, 169>> == <<"josé"::bits>> assert <<106, 111, 115, 195, 169>> == <<"josé"::bitstring>> diff --git a/lib/elixir/test/elixir/kernel/cli_test.exs b/lib/elixir/test/elixir/kernel/cli_test.exs index 011a7f9ef..39f7664ca 100644 --- a/lib/elixir/test/elixir/kernel/cli_test.exs +++ b/lib/elixir/test/elixir/kernel/cli_test.exs @@ -75,16 +75,13 @@ defmodule Kernel.CLI.ErrorTest do 'false\n' end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "blames exceptions" do - error = to_string(elixir('-e "Access.fetch :foo, :bar"')) - assert error =~ "** (FunctionClauseError) no function clause matching in Access.fetch/2" - assert error =~ "The following arguments were given to Access.fetch/2" - assert error =~ ":foo" - assert error =~ "def fetch(-%module{} = container-, +key+)" - assert error =~ ~r"\(elixir\) lib/access\.ex:\d+: Access\.fetch/2" - end + test "blames exceptions" do + error = to_string(elixir('-e "Access.fetch :foo, :bar"')) + assert error =~ "** (FunctionClauseError) no function clause matching in Access.fetch/2" + assert error =~ "The following arguments were given to Access.fetch/2" + assert error =~ ":foo" + assert error =~ "def fetch(-%module{} = container-, +key+)" + assert error =~ ~r"\(elixir\) lib/access\.ex:\d+: Access\.fetch/2" end end diff --git a/lib/elixir/test/elixir/kernel/dialyzer_test.exs b/lib/elixir/test/elixir/kernel/dialyzer_test.exs index af1428e76..022dd99cc 100644 --- a/lib/elixir/test/elixir/kernel/dialyzer_test.exs +++ b/lib/elixir/test/elixir/kernel/dialyzer_test.exs @@ -130,11 +130,9 @@ defmodule Kernel.DialyzerTest do assert_dialyze_no_warnings!(context) end - if :erlang.system_info(:otp_release) >= '20' do - test "no warnings on with/else", context do - copy_beam!(context, Dialyzer.With) - assert_dialyze_no_warnings!(context) - end + test "no warnings on with/else", context do + copy_beam!(context, Dialyzer.With) + assert_dialyze_no_warnings!(context) end test "no warnings on defmacrop", context do diff --git a/lib/elixir/test/elixir/kernel/errors_test.exs b/lib/elixir/test/elixir/kernel/errors_test.exs index b21cc4abb..b57a73f29 100644 --- a/lib/elixir/test/elixir/kernel/errors_test.exs +++ b/lib/elixir/test/elixir/kernel/errors_test.exs @@ -100,28 +100,25 @@ defmodule Kernel.ErrorsTest do message = "nofile:1: invalid character \"?\" (codepoint U+003F) in alias: Foo?" assert_eval_raise SyntaxError, message, 'Foo?' - # TODO: Remove this check once we depend on OTP 20+ - if :erlang.system_info(:otp_release) >= '20' do - message = - "nofile:1: invalid character \"ó\" (codepoint U+00F3) in alias (only ascii characters are allowed): Foó" + message = + "nofile:1: invalid character \"ó\" (codepoint U+00F3) in alias (only ascii characters are allowed): Foó" - assert_eval_raise SyntaxError, message, 'Foó' + assert_eval_raise SyntaxError, message, 'Foó' - message = ~r""" - Elixir expects unquoted Unicode atoms and variables to be in NFC form. + message = ~r""" + Elixir expects unquoted Unicode atoms and variables to be in NFC form. - Got: + Got: - "foó" \(codepoints 0066 006F 006F 0301\) + "foó" \(codepoints 0066 006F 006F 0301\) - Expected: + Expected: - "foó" \(codepoints 0066 006F 00F3\) + "foó" \(codepoints 0066 006F 00F3\) - """ + """ - assert_eval_raise SyntaxError, message, :unicode.characters_to_nfd_list("foó") - end + assert_eval_raise SyntaxError, message, :unicode.characters_to_nfd_list("foó") end test "kw missing space" do diff --git a/lib/elixir/test/elixir/kernel/string_tokenizer_test.exs b/lib/elixir/test/elixir/kernel/string_tokenizer_test.exs index 2b543071a..9a9b2923e 100644 --- a/lib/elixir/test/elixir/kernel/string_tokenizer_test.exs +++ b/lib/elixir/test/elixir/kernel/string_tokenizer_test.exs @@ -1,76 +1,72 @@ Code.require_file("../test_helper.exs", __DIR__) -# TODO: Remove this check once we depend only on 20 -# TODO: Remove String.to_atom/1 calls when we support 20+ -if :erlang.system_info(:otp_release) >= '20' do - defmodule Kernel.StringTokenizerTest do - use ExUnit.Case, async: true +defmodule Kernel.StringTokenizerTest do + use ExUnit.Case, async: true - defp var({var, _, nil}), do: var - defp aliases({:__aliases__, _, [alias]}), do: alias + defp var({var, _, nil}), do: var + defp aliases({:__aliases__, _, [alias]}), do: alias - test "tokenizes vars" do - assert Code.string_to_quoted!("_12") |> var() == String.to_atom("_12") - assert Code.string_to_quoted!("ola") |> var() == String.to_atom("ola") - assert Code.string_to_quoted!("ólá") |> var() == String.to_atom("ólá") - assert Code.string_to_quoted!("óLÁ") |> var() == String.to_atom("óLÁ") - assert Code.string_to_quoted!("ólá?") |> var() == String.to_atom("ólá?") - assert Code.string_to_quoted!("ólá!") |> var() == String.to_atom("ólá!") - assert Code.string_to_quoted!("こんにちは世界") |> var() == String.to_atom("こんにちは世界") - assert {:error, _} = Code.string_to_quoted("v@r") - assert {:error, _} = Code.string_to_quoted("1var") - end + test "tokenizes vars" do + assert Code.string_to_quoted!("_12") |> var() == :_12 + assert Code.string_to_quoted!("ola") |> var() == :ola + assert Code.string_to_quoted!("ólá") |> var() == :ólá + assert Code.string_to_quoted!("óLÁ") |> var() == :óLÁ + assert Code.string_to_quoted!("ólá?") |> var() == :ólá? + assert Code.string_to_quoted!("ólá!") |> var() == :ólá! + assert Code.string_to_quoted!("こんにちは世界") |> var() == :こんにちは世界 + assert {:error, _} = Code.string_to_quoted("v@r") + assert {:error, _} = Code.string_to_quoted("1var") + end - test "tokenizes atoms" do - assert Code.string_to_quoted!(":_12") == String.to_atom("_12") - assert Code.string_to_quoted!(":ola") == String.to_atom("ola") - assert Code.string_to_quoted!(":ólá") == String.to_atom("ólá") - assert Code.string_to_quoted!(":ólá?") == String.to_atom("ólá?") - assert Code.string_to_quoted!(":ólá!") == String.to_atom("ólá!") - assert Code.string_to_quoted!(":ól@") == String.to_atom("ól@") - assert Code.string_to_quoted!(":ól@!") == String.to_atom("ól@!") - assert Code.string_to_quoted!(":ó@@!") == String.to_atom("ó@@!") - assert Code.string_to_quoted!(":Ola") == String.to_atom("Ola") - assert Code.string_to_quoted!(":Ólá") == String.to_atom("Ólá") - assert Code.string_to_quoted!(":ÓLÁ") == String.to_atom("ÓLÁ") - assert Code.string_to_quoted!(":ÓLÁ?") == String.to_atom("ÓLÁ?") - assert Code.string_to_quoted!(":ÓLÁ!") == String.to_atom("ÓLÁ!") - assert Code.string_to_quoted!(":ÓL@!") == String.to_atom("ÓL@!") - assert Code.string_to_quoted!(":Ó@@!") == String.to_atom("Ó@@!") - assert Code.string_to_quoted!(":こんにちは世界") == String.to_atom("こんにちは世界") - assert {:error, _} = Code.string_to_quoted(":123") - assert {:error, _} = Code.string_to_quoted(":@123") - end + test "tokenizes atoms" do + assert Code.string_to_quoted!(":_12") == :_12 + assert Code.string_to_quoted!(":ola") == :ola + assert Code.string_to_quoted!(":ólá") == :ólá + assert Code.string_to_quoted!(":ólá?") == :ólá? + assert Code.string_to_quoted!(":ólá!") == :ólá! + assert Code.string_to_quoted!(":ól@") == :ól@ + assert Code.string_to_quoted!(":ól@!") == :ól@! + assert Code.string_to_quoted!(":ó@@!") == :ó@@! + assert Code.string_to_quoted!(":Ola") == :Ola + assert Code.string_to_quoted!(":Ólá") == :Ólá + assert Code.string_to_quoted!(":ÓLÁ") == :ÓLÁ + assert Code.string_to_quoted!(":ÓLÁ?") == :ÓLÁ? + assert Code.string_to_quoted!(":ÓLÁ!") == :ÓLÁ! + assert Code.string_to_quoted!(":ÓL@!") == :ÓL@! + assert Code.string_to_quoted!(":Ó@@!") == :Ó@@! + assert Code.string_to_quoted!(":こんにちは世界") == :こんにちは世界 + assert {:error, _} = Code.string_to_quoted(":123") + assert {:error, _} = Code.string_to_quoted(":@123") + end - test "tokenizes keywords" do - assert Code.string_to_quoted!("[_12: 0]") == [{String.to_atom("_12"), 0}] - assert Code.string_to_quoted!("[ola: 0]") == [{String.to_atom("ola"), 0}] - assert Code.string_to_quoted!("[ólá: 0]") == [{String.to_atom("ólá"), 0}] - assert Code.string_to_quoted!("[ólá?: 0]") == [{String.to_atom("ólá?"), 0}] - assert Code.string_to_quoted!("[ólá!: 0]") == [{String.to_atom("ólá!"), 0}] - assert Code.string_to_quoted!("[ól@: 0]") == [{String.to_atom("ól@"), 0}] - assert Code.string_to_quoted!("[ól@!: 0]") == [{String.to_atom("ól@!"), 0}] - assert Code.string_to_quoted!("[ó@@!: 0]") == [{String.to_atom("ó@@!"), 0}] - assert Code.string_to_quoted!("[Ola: 0]") == [{String.to_atom("Ola"), 0}] - assert Code.string_to_quoted!("[Ólá: 0]") == [{String.to_atom("Ólá"), 0}] - assert Code.string_to_quoted!("[ÓLÁ: 0]") == [{String.to_atom("ÓLÁ"), 0}] - assert Code.string_to_quoted!("[ÓLÁ?: 0]") == [{String.to_atom("ÓLÁ?"), 0}] - assert Code.string_to_quoted!("[ÓLÁ!: 0]") == [{String.to_atom("ÓLÁ!"), 0}] - assert Code.string_to_quoted!("[ÓL@!: 0]") == [{String.to_atom("ÓL@!"), 0}] - assert Code.string_to_quoted!("[Ó@@!: 0]") == [{String.to_atom("Ó@@!"), 0}] - assert Code.string_to_quoted!("[こんにちは世界: 0]") == [{String.to_atom("こんにちは世界"), 0}] - assert {:error, _} = Code.string_to_quoted("[123: 0]") - assert {:error, _} = Code.string_to_quoted("[@123: 0]") - end + test "tokenizes keywords" do + assert Code.string_to_quoted!("[_12: 0]") == [_12: 0] + assert Code.string_to_quoted!("[ola: 0]") == [ola: 0] + assert Code.string_to_quoted!("[ólá: 0]") == [ólá: 0] + assert Code.string_to_quoted!("[ólá?: 0]") == [ólá?: 0] + assert Code.string_to_quoted!("[ólá!: 0]") == [ólá!: 0] + assert Code.string_to_quoted!("[ól@: 0]") == [ól@: 0] + assert Code.string_to_quoted!("[ól@!: 0]") == [ól@!: 0] + assert Code.string_to_quoted!("[ó@@!: 0]") == [ó@@!: 0] + assert Code.string_to_quoted!("[Ola: 0]") == [Ola: 0] + assert Code.string_to_quoted!("[Ólá: 0]") == [Ólá: 0] + assert Code.string_to_quoted!("[ÓLÁ: 0]") == [ÓLÁ: 0] + assert Code.string_to_quoted!("[ÓLÁ?: 0]") == [ÓLÁ?: 0] + assert Code.string_to_quoted!("[ÓLÁ!: 0]") == [ÓLÁ!: 0] + assert Code.string_to_quoted!("[ÓL@!: 0]") == [ÓL@!: 0] + assert Code.string_to_quoted!("[Ó@@!: 0]") == [Ó@@!: 0] + assert Code.string_to_quoted!("[こんにちは世界: 0]") == [こんにちは世界: 0] + assert {:error, _} = Code.string_to_quoted("[123: 0]") + assert {:error, _} = Code.string_to_quoted("[@123: 0]") + end - test "tokenizes aliases" do - assert Code.string_to_quoted!("Ola") |> aliases() == String.to_atom("Ola") - assert Code.string_to_quoted!("M_123") |> aliases() == String.to_atom("M_123") - assert {:error, _} = Code.string_to_quoted("Óla") - assert {:error, _} = Code.string_to_quoted("Olá") - assert {:error, _} = Code.string_to_quoted("Ol@") - assert {:error, _} = Code.string_to_quoted("Ola?") - assert {:error, _} = Code.string_to_quoted("Ola!") - end + test "tokenizes aliases" do + assert Code.string_to_quoted!("Ola") |> aliases() == String.to_atom("Ola") + assert Code.string_to_quoted!("M_123") |> aliases() == String.to_atom("M_123") + assert {:error, _} = Code.string_to_quoted("Óla") + assert {:error, _} = Code.string_to_quoted("Olá") + assert {:error, _} = Code.string_to_quoted("Ol@") + assert {:error, _} = Code.string_to_quoted("Ola?") + assert {:error, _} = Code.string_to_quoted("Ola!") end end diff --git a/lib/elixir/test/elixir/kernel/warning_test.exs b/lib/elixir/test/elixir/kernel/warning_test.exs index 8546f152c..e39ed8819 100644 --- a/lib/elixir/test/elixir/kernel/warning_test.exs +++ b/lib/elixir/test/elixir/kernel/warning_test.exs @@ -535,20 +535,17 @@ defmodule Kernel.WarningTest do purge(Sample) end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "duplicate map keys" do - output = - capture_err(fn -> - defmodule DuplicateMapKeys do - assert %{a: :b, a: :c} == %{a: :c} - assert %{1 => 2, 1 => 3} == %{1 => 3} - end - end) + test "duplicate map keys" do + output = + capture_err(fn -> + defmodule DuplicateMapKeys do + assert %{a: :b, a: :c} == %{a: :c} + assert %{1 => 2, 1 => 3} == %{1 => 3} + end + end) - assert output =~ "key :a will be overridden in map" - assert output =~ "key 1 will be overridden in map" - end + assert output =~ "key :a will be overridden in map" + assert output =~ "key 1 will be overridden in map" end test "unused guard" do diff --git a/lib/elixir/test/elixir/module_test.exs b/lib/elixir/test/elixir/module_test.exs index 5da0d0cc5..e73f079de 100644 --- a/lib/elixir/test/elixir/module_test.exs +++ b/lib/elixir/test/elixir/module_test.exs @@ -310,29 +310,25 @@ defmodule ModuleTest do assert ModuleCreateGenerated.world() end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "uses the new debug_info chunk" do - {:module, ModuleCreateDebugInfo, binary, _} = - Module.create(ModuleCreateDebugInfo, :ok, __ENV__) + test "uses the debug_info chunk" do + {:module, ModuleCreateDebugInfo, binary, _} = + Module.create(ModuleCreateDebugInfo, :ok, __ENV__) - {:ok, {_, [debug_info: {:debug_info_v1, backend, data}]}} = - :beam_lib.chunks(binary, [:debug_info]) + {:ok, {_, [debug_info: {:debug_info_v1, backend, data}]}} = + :beam_lib.chunks(binary, [:debug_info]) - {:ok, map} = backend.debug_info(:elixir_v1, ModuleCreateDebugInfo, data, []) - assert map.module == ModuleCreateDebugInfo - end + {:ok, map} = backend.debug_info(:elixir_v1, ModuleCreateDebugInfo, data, []) + assert map.module == ModuleCreateDebugInfo + end - test "uses the new debug_info chunk even if debug_info is set to false" do - {:module, ModuleCreateNoDebugInfo, binary, _} = - Module.create(ModuleCreateNoDebugInfo, quote(do: @compile({:debug_info, false})), __ENV__) + test "uses the debug_info chunk even if debug_info is set to false" do + {:module, ModuleCreateNoDebugInfo, binary, _} = + Module.create(ModuleCreateNoDebugInfo, quote(do: @compile({:debug_info, false})), __ENV__) - {:ok, {_, [debug_info: {:debug_info_v1, backend, data}]}} = - :beam_lib.chunks(binary, [:debug_info]) + {:ok, {_, [debug_info: {:debug_info_v1, backend, data}]}} = + :beam_lib.chunks(binary, [:debug_info]) - assert backend.debug_info(:elixir_v1, ModuleCreateNoDebugInfo, data, []) == - {:error, :missing} - end + assert backend.debug_info(:elixir_v1, ModuleCreateNoDebugInfo, data, []) == {:error, :missing} end test "no function in module body" do diff --git a/lib/ex_unit/test/ex_unit/formatter_test.exs b/lib/ex_unit/test/ex_unit/formatter_test.exs index b243f5e5b..4efe7bc11 100644 --- a/lib/ex_unit/test/ex_unit/formatter_test.exs +++ b/lib/ex_unit/test/ex_unit/formatter_test.exs @@ -147,42 +147,39 @@ defmodule ExUnit.FormatterTest do """ end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - defp trim_multiline_whitespace(string) do - String.replace(string, ~r"\n\s+\n", "\n\n") - end + defp trim_multiline_whitespace(string) do + String.replace(string, ~r"\n\s+\n", "\n\n") + end - test "blames function clause error" do - {error, stack} = - try do - Access.fetch(:foo, :bar) - rescue - exception -> {exception, __STACKTRACE__} - end + test "blames function clause error" do + {error, stack} = + try do + Access.fetch(:foo, :bar) + rescue + exception -> {exception, __STACKTRACE__} + end - failure = format_test_failure(test(), [{:error, error, [hd(stack)]}], 1, 80, &formatter/2) + failure = format_test_failure(test(), [{:error, error, [hd(stack)]}], 1, 80, &formatter/2) - assert trim_multiline_whitespace(failure) =~ """ - 1) world (Hello) - test/ex_unit/formatter_test.exs:1 - ** (FunctionClauseError) no function clause matching in Access.fetch/2 + assert trim_multiline_whitespace(failure) =~ """ + 1) world (Hello) + test/ex_unit/formatter_test.exs:1 + ** (FunctionClauseError) no function clause matching in Access.fetch/2 - The following arguments were given to Access.fetch/2: + The following arguments were given to Access.fetch/2: - # 1 - :foo + # 1 + :foo - # 2 - :bar + # 2 + :bar - Attempted function clauses (showing 5 out of 5): + Attempted function clauses (showing 5 out of 5): - def fetch(%module{} = container, key) - """ + def fetch(%module{} = container, key) + """ - assert failure =~ ~r"\(elixir\) lib/access\.ex:\d+: Access\.fetch/2" - end + assert failure =~ ~r"\(elixir\) lib/access\.ex:\d+: Access\.fetch/2" end test "formats setup all errors" do diff --git a/lib/iex/lib/iex.ex b/lib/iex/lib/iex.ex index 84bcc20f0..59424757e 100644 --- a/lib/iex/lib/iex.ex +++ b/lib/iex/lib/iex.ex @@ -31,9 +31,8 @@ defmodule IEx do ## Shell history - From Erlang/OTP 20, it is possible to get shell history by passing some - flags that enable it in the VM. This can be done on a per-need basis - when starting IEx: + It is possible to get shell history by passing some flags that enable it + in the VM. This can be done on a per-need basis when starting IEx: iex --erl "-kernel shell_history enabled" @@ -117,9 +116,8 @@ defmodule IEx do Alternatively, you can use `IEx.break!/4` to setup a breakpoint on a given module, function and arity you have no control of. - While `IEx.break!/4` is more flexible, it requires Erlang/OTP 20+ and - it does not contain information about imports and aliases from - the source code. + While `IEx.break!/4` is more flexible, it does not contain + information about imports and aliases from the source code. ## The User Switch command @@ -255,7 +253,7 @@ defmodule IEx do results in: $ iex - Erlang 19 [...] + Erlang/OTP 20 [...] hello world Interactive Elixir - press Ctrl+C to exit (type h() ENTER for help) @@ -279,7 +277,7 @@ defmodule IEx do Now run the shell: $ iex - Erlang 19 [...] + Erlang/OTP 20 [...] Interactive Elixir - press Ctrl+C to exit (type h() ENTER for help) iex(1)> [1, 2, 3, 4, 5] @@ -469,9 +467,8 @@ defmodule IEx do Alternatively, you can use `IEx.break!/4` to setup a breakpoint on a given module, function and arity you have no control of. - While `IEx.break!/4` is more flexible, it requires Erlang/OTP 20+ and - it does not contain information about imports and aliases from - the source code. + While `IEx.break!/4` is more flexible, it does not contain + information about imports and aliases from the source code. ## Examples @@ -663,8 +660,6 @@ defmodule IEx do the process terminates, or invoke `respawn()`, which starts a new IEx shell, freeing up the pried one. - This functionality only works on Elixir code and requires Erlang/OTP 20+. - ## Examples The examples below will use `break!`, assuming that you are setting diff --git a/lib/iex/lib/iex/helpers.ex b/lib/iex/lib/iex/helpers.ex index 637496411..2df3c13ad 100644 --- a/lib/iex/lib/iex/helpers.ex +++ b/lib/iex/lib/iex/helpers.ex @@ -580,10 +580,7 @@ defmodule IEx.Helpers do print_uptime() print_entry("Run queue", :erlang.statistics(:run_queue)) - if :erlang.system_info(:otp_release) >= '20' do - print_percentage("Atoms", :atom_count, :atom_limit) - end - + print_percentage("Atoms", :atom_count, :atom_limit) print_percentage("ETS", :ets_count, :ets_limit) print_percentage("Ports", :port_count, :port_limit) print_percentage("Processes", :process_count, :process_limit) diff --git a/lib/iex/lib/iex/pry.ex b/lib/iex/lib/iex/pry.ex index 8bfb23097..e7317e19f 100644 --- a/lib/iex/lib/iex/pry.ex +++ b/lib/iex/lib/iex/pry.ex @@ -156,7 +156,6 @@ defmodule IEx.Pry do :recompilation_failed | :no_beam_file | :unknown_function_arity - | :otp_20_is_required | :missing_debug_info | :outdated_debug_info | :non_elixir_module @@ -188,9 +187,6 @@ defmodule IEx.Pry do :non_elixir_module -> "module #{inspect(module)} was not written in Elixir" - :otp_20_is_required -> - "you are running on an earlier version than Erlang/OTP 20" - :outdated_debug_info -> "module #{inspect(module)} was not compiled with the latest debug_info" @@ -383,10 +379,6 @@ defmodule IEx.Pry do {:ok, {_, [debug_info: {:debug_info_v1, _, _}]}} -> {:error, :non_elixir_module} - {:error, :beam_lib, {:unknown_chunk, _, _}} -> - # TODO: Remove this when we require OTP 20+ - {:error, :otp_20_is_required} - {:error, :beam_lib, {:missing_chunk, _, _}} -> {:error, :missing_debug_info} diff --git a/lib/iex/test/iex/helpers_test.exs b/lib/iex/test/iex/helpers_test.exs index a8a1db0df..d15967485 100644 --- a/lib/iex/test/iex/helpers_test.exs +++ b/lib/iex/test/iex/helpers_test.exs @@ -27,115 +27,113 @@ defmodule IEx.HelpersTest do end end - if :erlang.system_info(:otp_release) >= '20' do - describe "breakpoints" do - setup do - on_exit(fn -> IEx.Pry.remove_breaks() end) - end + describe "breakpoints" do + setup do + on_exit(fn -> IEx.Pry.remove_breaks() end) + end - test "sets up a breakpoint with capture syntax" do - assert break!(URI.decode_query() / 2) == 1 - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] - end + test "sets up a breakpoint with capture syntax" do + assert break!(URI.decode_query() / 2) == 1 + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] + end - test "sets up a breakpoint with call syntax" do - assert break!(URI.decode_query(_, %{})) == 1 - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] - end + test "sets up a breakpoint with call syntax" do + assert break!(URI.decode_query(_, %{})) == 1 + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] + end - test "sets up a breakpoint with guards syntax" do - assert break!(URI.decode_query(_, map) when is_map(map)) == 1 - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] - end + test "sets up a breakpoint with guards syntax" do + assert break!(URI.decode_query(_, map) when is_map(map)) == 1 + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] + end - test "sets up a breakpoint on the given module" do - assert break!(URI, :decode_query, 2) == 1 - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] - end + test "sets up a breakpoint on the given module" do + assert break!(URI, :decode_query, 2) == 1 + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] + end - test "resets breaks on the given id" do - assert break!(URI, :decode_query, 2) == 1 - assert reset_break(1) == :ok - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] - end + test "resets breaks on the given id" do + assert break!(URI, :decode_query, 2) == 1 + assert reset_break(1) == :ok + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] + end - test "resets breaks on the given module" do - assert break!(URI, :decode_query, 2) == 1 - assert reset_break(URI, :decode_query, 2) == :ok - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] - end + test "resets breaks on the given module" do + assert break!(URI, :decode_query, 2) == 1 + assert reset_break(URI, :decode_query, 2) == :ok + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] + end - test "removes breaks in the given module" do - assert break!(URI.decode_query() / 2) == 1 - assert remove_breaks(URI) == :ok - assert IEx.Pry.breaks() == [] - end + test "removes breaks in the given module" do + assert break!(URI.decode_query() / 2) == 1 + assert remove_breaks(URI) == :ok + assert IEx.Pry.breaks() == [] + end - test "removes breaks on all modules" do - assert break!(URI.decode_query() / 2) == 1 - assert remove_breaks() == :ok - assert IEx.Pry.breaks() == [] - end + test "removes breaks on all modules" do + assert break!(URI.decode_query() / 2) == 1 + assert remove_breaks() == :ok + assert IEx.Pry.breaks() == [] + end - test "errors when setting up a breakpoint with invalid guard" do - assert_raise CompileError, ~r"cannot invoke local is_whatever/1 inside guard", fn -> - break!(URI.decode_query(_, map) when is_whatever(map)) - end + test "errors when setting up a breakpoint with invalid guard" do + assert_raise CompileError, ~r"cannot invoke local is_whatever/1 inside guard", fn -> + break!(URI.decode_query(_, map) when is_whatever(map)) end + end - test "errors when setting up a break with no beam" do - assert_raise RuntimeError, - "could not set breakpoint, could not find .beam file for IEx.HelpersTest", - fn -> break!(__MODULE__, :setup, 1) end - end + test "errors when setting up a break with no beam" do + assert_raise RuntimeError, + "could not set breakpoint, could not find .beam file for IEx.HelpersTest", + fn -> break!(__MODULE__, :setup, 1) end + end - test "errors when setting up a break for unknown function" do - assert_raise RuntimeError, - "could not set breakpoint, unknown function/macro URI.unknown/2", - fn -> break!(URI, :unknown, 2) end - end + test "errors when setting up a break for unknown function" do + assert_raise RuntimeError, + "could not set breakpoint, unknown function/macro URI.unknown/2", + fn -> break!(URI, :unknown, 2) end + end - test "errors for non-Elixir modules" do - assert_raise RuntimeError, - "could not set breakpoint, module :elixir was not written in Elixir", - fn -> break!(:elixir, :unknown, 2) end - end + test "errors for non-Elixir modules" do + assert_raise RuntimeError, + "could not set breakpoint, module :elixir was not written in Elixir", + fn -> break!(:elixir, :unknown, 2) end + end - test "prints table with breaks" do - break!(URI, :decode_query, 2) + test "prints table with breaks" do + break!(URI, :decode_query, 2) - assert capture_io(fn -> breaks() end) == """ + assert capture_io(fn -> breaks() end) == """ - ID Module.function/arity Pending stops - ---- ----------------------- --------------- - 1 URI.decode_query/2 1 + ID Module.function/arity Pending stops + ---- ----------------------- --------------- + 1 URI.decode_query/2 1 - """ + """ - assert capture_io(fn -> URI.decode_query("foo=bar", %{}) end) != "" + assert capture_io(fn -> URI.decode_query("foo=bar", %{}) end) != "" - assert capture_io(fn -> breaks() end) == """ + assert capture_io(fn -> breaks() end) == """ - ID Module.function/arity Pending stops - ---- ----------------------- --------------- - 1 URI.decode_query/2 0 + ID Module.function/arity Pending stops + ---- ----------------------- --------------- + 1 URI.decode_query/2 0 - """ + """ - assert capture_io(fn -> URI.decode_query("foo=bar", %{}) end) == "" + assert capture_io(fn -> URI.decode_query("foo=bar", %{}) end) == "" - assert capture_io(fn -> breaks() end) == """ + assert capture_io(fn -> breaks() end) == """ - ID Module.function/arity Pending stops - ---- ----------------------- --------------- - 1 URI.decode_query/2 0 + ID Module.function/arity Pending stops + ---- ----------------------- --------------- + 1 URI.decode_query/2 0 - """ - end + """ + end - test "does not print table when there are no breaks" do - assert capture_io(fn -> breaks() end) == "No breakpoints set\n" - end + test "does not print table when there are no breaks" do + assert capture_io(fn -> breaks() end) == "No breakpoints set\n" end end diff --git a/lib/iex/test/iex/interaction_test.exs b/lib/iex/test/iex/interaction_test.exs index 25e6190bc..591d1f53f 100644 --- a/lib/iex/test/iex/interaction_test.exs +++ b/lib/iex/test/iex/interaction_test.exs @@ -144,16 +144,13 @@ defmodule IEx.InteractionTest do ~r"\*\* \(exit\) exited in: :gen_server\.call\(#PID<\d+\.\d+\.\d+>, :hello\)\n\s{4}\*\* \(EXIT\) :bye" end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "blames function clause error" do - content = capture_iex("Access.fetch(:foo, :bar)") - assert content =~ "** (FunctionClauseError) no function clause matching in Access.fetch/2" - assert content =~ "The following arguments were given to Access.fetch/2" - assert content =~ ":foo" - assert content =~ "def fetch(-%module{} = container-, key)" - assert content =~ ~r"\(elixir\) lib/access\.ex:\d+: Access\.fetch/2" - end + test "blames function clause error" do + content = capture_iex("Access.fetch(:foo, :bar)") + assert content =~ "** (FunctionClauseError) no function clause matching in Access.fetch/2" + assert content =~ "The following arguments were given to Access.fetch/2" + assert content =~ ":foo" + assert content =~ "def fetch(-%module{} = container-, key)" + assert content =~ ~r"\(elixir\) lib/access\.ex:\d+: Access\.fetch/2" end ## .iex file loading diff --git a/lib/iex/test/iex/pry_test.exs b/lib/iex/test/iex/pry_test.exs index 1532d42a2..b77179850 100644 --- a/lib/iex/test/iex/pry_test.exs +++ b/lib/iex/test/iex/pry_test.exs @@ -43,158 +43,156 @@ defmodule IEx.PryTest do end end - if :erlang.system_info(:otp_release) >= '20' do - describe "break" do - test "sets up a breakpoint on the given module" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert instrumented?(URI) - assert [_] = IEx.Pry.breaks() - end - - test "sets up multiple breakpoints in the same module" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert instrumented?(URI) - assert IEx.Pry.break(URI, :parse, 1, quote(do: _)) == {:ok, 2} - assert instrumented?(URI) - assert [_, _] = IEx.Pry.breaks() - end - - test "reinstruments if module has been reloaded" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert instrumented?(URI) - deinstrument!(URI) - refute instrumented?(URI) - assert IEx.Pry.break(URI, :parse, 1, quote(do: _)) == {:ok, 2} - assert instrumented?(URI) - assert [_, _] = IEx.Pry.breaks() - end - - test "returns id when breakpoint is already set" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert [_] = IEx.Pry.breaks() - end - - test "returns id even when breakpoint is already set on deinstrument" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - deinstrument!(URI) - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert [_] = IEx.Pry.breaks() - end - - test "errors when setting up a break with no beam" do - assert IEx.Pry.break(__MODULE__, :setup, 2, quote(do: _)) == {:error, :no_beam_file} - end - - test "errors when setting up a break for unknown function" do - assert IEx.Pry.break(URI, :unknown, 2, quote(do: _)) == {:error, :unknown_function_arity} - end - - test "errors for non-Elixir modules" do - assert IEx.Pry.break(:elixir, :unknown, 2, quote(do: _)) == {:error, :non_elixir_module} - end - end - - describe "breaks" do - test "returns all breaks" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] - - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _), 10) == {:ok, 1} - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 10}] - - assert IEx.Pry.break(URI, :parse, 1, quote(do: _), 1) == {:ok, 2} - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 10}, {2, URI, {:parse, 1}, 1}] - end - - test "sets negative break to 0" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - :ets.insert(IEx.Pry, {1, URI, {:decode_query, 2}, {[], true}, -1}) - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] - end - - test "do not return break points for deinstrumented modules" do - assert IEx.Pry.break(URI, :parse, 1, quote(do: _)) == {:ok, 1} - assert IEx.Pry.breaks() == [{1, URI, {:parse, 1}, 1}] - deinstrument!(URI) - assert IEx.Pry.breaks() == [] - end - end - - describe "reset_break" do - test "resets break for given id" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert IEx.Pry.reset_break(1) == :ok - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] - end - - test "resets break for given mfa" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert IEx.Pry.reset_break(URI, :decode_query, 2) == :ok - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] - end - - test "returns not_found if module is deinstrumented" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - deinstrument!(URI) - assert IEx.Pry.reset_break(URI, :decode_query, 2) == :not_found - assert IEx.Pry.breaks() == [] - end - - test "returns not_found if mfa has no break" do - assert IEx.Pry.reset_break(URI, :decode_query, 2) == :not_found - end - - test "returns not_found if id is deinstrumented" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - deinstrument!(URI) - assert IEx.Pry.reset_break(1) == :not_found - assert IEx.Pry.breaks() == [] - end - - test "returns not_found if id has no break" do - assert IEx.Pry.reset_break(1) == :not_found - end - end - - describe "remove_breaks" do - test "removes all breaks" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert IEx.Pry.remove_breaks() == :ok - assert IEx.Pry.breaks() == [] - end - - test "removes all breaks even if module is deinstrumented" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - deinstrument!(URI) - assert IEx.Pry.remove_breaks() == :ok - assert IEx.Pry.breaks() == [] - end - - test "remove breaks in a given module" do - assert IEx.Pry.remove_breaks(Date.Range) == :ok - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - assert IEx.Pry.break(Date.Range, :__struct__, 1, quote(do: _)) == {:ok, 2} - assert IEx.Pry.remove_breaks(Date.Range) == :ok - assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] - end - - test "remove breaks in a given module even if deinstrumented" do - assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} - deinstrument!(URI) - assert IEx.Pry.breaks() == [] - end - end - - defp instrumented?(module) do - module.module_info(:attributes)[:iex_pry] == [true] - end - - defp deinstrument!(module) do - beam = :code.which(module) - :code.purge(module) - {:module, _} = :code.load_binary(module, beam, File.read!(beam)) - :ok + describe "break" do + test "sets up a breakpoint on the given module" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert instrumented?(URI) + assert [_] = IEx.Pry.breaks() end + + test "sets up multiple breakpoints in the same module" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert instrumented?(URI) + assert IEx.Pry.break(URI, :parse, 1, quote(do: _)) == {:ok, 2} + assert instrumented?(URI) + assert [_, _] = IEx.Pry.breaks() + end + + test "reinstruments if module has been reloaded" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert instrumented?(URI) + deinstrument!(URI) + refute instrumented?(URI) + assert IEx.Pry.break(URI, :parse, 1, quote(do: _)) == {:ok, 2} + assert instrumented?(URI) + assert [_, _] = IEx.Pry.breaks() + end + + test "returns id when breakpoint is already set" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert [_] = IEx.Pry.breaks() + end + + test "returns id even when breakpoint is already set on deinstrument" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + deinstrument!(URI) + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert [_] = IEx.Pry.breaks() + end + + test "errors when setting up a break with no beam" do + assert IEx.Pry.break(__MODULE__, :setup, 2, quote(do: _)) == {:error, :no_beam_file} + end + + test "errors when setting up a break for unknown function" do + assert IEx.Pry.break(URI, :unknown, 2, quote(do: _)) == {:error, :unknown_function_arity} + end + + test "errors for non-Elixir modules" do + assert IEx.Pry.break(:elixir, :unknown, 2, quote(do: _)) == {:error, :non_elixir_module} + end + end + + describe "breaks" do + test "returns all breaks" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] + + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _), 10) == {:ok, 1} + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 10}] + + assert IEx.Pry.break(URI, :parse, 1, quote(do: _), 1) == {:ok, 2} + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 10}, {2, URI, {:parse, 1}, 1}] + end + + test "sets negative break to 0" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + :ets.insert(IEx.Pry, {1, URI, {:decode_query, 2}, {[], true}, -1}) + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] + end + + test "do not return break points for deinstrumented modules" do + assert IEx.Pry.break(URI, :parse, 1, quote(do: _)) == {:ok, 1} + assert IEx.Pry.breaks() == [{1, URI, {:parse, 1}, 1}] + deinstrument!(URI) + assert IEx.Pry.breaks() == [] + end + end + + describe "reset_break" do + test "resets break for given id" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert IEx.Pry.reset_break(1) == :ok + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] + end + + test "resets break for given mfa" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert IEx.Pry.reset_break(URI, :decode_query, 2) == :ok + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 0}] + end + + test "returns not_found if module is deinstrumented" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + deinstrument!(URI) + assert IEx.Pry.reset_break(URI, :decode_query, 2) == :not_found + assert IEx.Pry.breaks() == [] + end + + test "returns not_found if mfa has no break" do + assert IEx.Pry.reset_break(URI, :decode_query, 2) == :not_found + end + + test "returns not_found if id is deinstrumented" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + deinstrument!(URI) + assert IEx.Pry.reset_break(1) == :not_found + assert IEx.Pry.breaks() == [] + end + + test "returns not_found if id has no break" do + assert IEx.Pry.reset_break(1) == :not_found + end + end + + describe "remove_breaks" do + test "removes all breaks" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert IEx.Pry.remove_breaks() == :ok + assert IEx.Pry.breaks() == [] + end + + test "removes all breaks even if module is deinstrumented" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + deinstrument!(URI) + assert IEx.Pry.remove_breaks() == :ok + assert IEx.Pry.breaks() == [] + end + + test "remove breaks in a given module" do + assert IEx.Pry.remove_breaks(Date.Range) == :ok + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + assert IEx.Pry.break(Date.Range, :__struct__, 1, quote(do: _)) == {:ok, 2} + assert IEx.Pry.remove_breaks(Date.Range) == :ok + assert IEx.Pry.breaks() == [{1, URI, {:decode_query, 2}, 1}] + end + + test "remove breaks in a given module even if deinstrumented" do + assert IEx.Pry.break(URI, :decode_query, 2, quote(do: _)) == {:ok, 1} + deinstrument!(URI) + assert IEx.Pry.breaks() == [] + end + end + + defp instrumented?(module) do + module.module_info(:attributes)[:iex_pry] == [true] + end + + defp deinstrument!(module) do + beam = :code.which(module) + :code.purge(module) + {:module, _} = :code.load_binary(module, beam, File.read!(beam)) + :ok end end diff --git a/lib/logger/lib/logger/translator.ex b/lib/logger/lib/logger/translator.ex index 592f0d7d7..5a5b11699 100644 --- a/lib/logger/lib/logger/translator.ex +++ b/lib/logger/lib/logger/translator.ex @@ -555,10 +555,7 @@ defmodule Logger.Translator do defp format_mfa(mod, fun, args), do: Exception.format_mfa(mod, fun, args) - defp exit_reason(:exit, reason, stack) do - if :erlang.system_info(:otp_release) >= '20', do: {reason, stack}, else: nil - end - + defp exit_reason(:exit, reason, stack), do: {reason, stack} defp exit_reason(:error, reason, stack), do: {reason, stack} defp exit_reason(:throw, value, stack), do: {{:nocatch, value}, stack} diff --git a/lib/logger/test/logger/translator_test.exs b/lib/logger/test/logger/translator_test.exs index 92f7a5fc7..3b9772db9 100644 --- a/lib/logger/test/logger/translator_test.exs +++ b/lib/logger/test/logger/translator_test.exs @@ -110,10 +110,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert {%RuntimeError{message: "oops"}, [_ | _]} = gen_server_metadata[:crash_reason] - - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] refute Keyword.has_key?(gen_server_metadata, :initial_call) assert process_metadata[:initial_call] == {Logger.TranslatorTest.MyGenServer, :init, 1} @@ -157,95 +154,67 @@ defmodule Logger.TranslatorTest do Application.put_env(:logger, :translator_inspect_opts, []) end - # TODO: Remove this check once we depend only on 20 - if :erlang.system_info(:otp_release) >= '20' do - test "translates GenServer crashes on debug" do - {:ok, pid} = GenServer.start(MyGenServer, :ok) + test "translates GenServer crashes on debug" do + {:ok, pid} = GenServer.start(MyGenServer, :ok) - assert capture_log(:debug, fn -> - catch_exit(GenServer.call(pid, :error)) - end) =~ ~r""" - \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating - \*\* \(RuntimeError\) oops - .* - Last message \(from #PID<\d+\.\d+\.\d+>\): :error - State: :ok - Client #PID<\d+\.\d+\.\d+> is alive - .* - """s + assert capture_log(:debug, fn -> + catch_exit(GenServer.call(pid, :error)) + end) =~ ~r""" + \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating + \*\* \(RuntimeError\) oops + .* + Last message \(from #PID<\d+\.\d+\.\d+>\): :error + State: :ok + Client #PID<\d+\.\d+\.\d+> is alive + .* + """s - assert_receive {:error, _pid, - {Logger, [["GenServer " <> _ | _] | _], _ts, gen_server_metadata}} + assert_receive {:error, _pid, + {Logger, [["GenServer " <> _ | _] | _], _ts, gen_server_metadata}} - assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} + assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - assert {%RuntimeError{message: "oops"}, [_ | _]} = gen_server_metadata[:crash_reason] + assert {%RuntimeError{message: "oops"}, [_ | _]} = gen_server_metadata[:crash_reason] - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] + end - test "translates GenServer crashes with named client on debug" do - {:ok, pid} = GenServer.start(MyGenServer, :ok) - - assert capture_log(:debug, fn -> - Process.register(self(), :named_client) - catch_exit(GenServer.call(pid, :error)) - end) =~ ~r""" - \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating - \*\* \(RuntimeError\) oops - .* - Last message \(from :named_client\): :error - State: :ok - Client :named_client is alive - .* - """s - end + test "translates GenServer crashes with named client on debug" do + {:ok, pid} = GenServer.start(MyGenServer, :ok) - test "translates GenServer crashes with dead client on debug" do - {:ok, pid} = GenServer.start(MyGenServer, :ok) - - assert capture_log(:debug, fn -> - mon = Process.monitor(pid) - - spawn_link(fn -> - catch_exit(GenServer.call(pid, :error_on_down, 0)) - end) - - assert_receive {:DOWN, ^mon, _, _, _} - end) =~ ~r""" - \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating - \*\* \(RuntimeError\) oops - .* - Last message \(from #PID<\d+\.\d+\.\d+>\): :error_on_down - State: :ok - Client #PID<\d+\.\d+\.\d+> is dead - """s - end - else - test "translates GenServer crashes on debug" do - {:ok, pid} = GenServer.start(MyGenServer, :ok) - - assert capture_log(:debug, fn -> - catch_exit(GenServer.call(pid, :error)) - end) =~ ~r""" - \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating - \*\* \(RuntimeError\) oops - .* - Last message: :error - State: :ok - """s - - assert_receive {:error, _pid, - {Logger, [["GenServer " <> _ | _] | _], _ts, gen_server_metadata}} - - assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - - assert {%RuntimeError{message: "oops"}, [_ | _]} = gen_server_metadata[:crash_reason] - - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end - end + assert capture_log(:debug, fn -> + Process.register(self(), :named_client) + catch_exit(GenServer.call(pid, :error)) + end) =~ ~r""" + \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating + \*\* \(RuntimeError\) oops + .* + Last message \(from :named_client\): :error + State: :ok + Client :named_client is alive + .* + """s + end + + test "translates GenServer crashes with dead client on debug" do + {:ok, pid} = GenServer.start(MyGenServer, :ok) + + assert capture_log(:debug, fn -> + mon = Process.monitor(pid) + + spawn_link(fn -> + catch_exit(GenServer.call(pid, :error_on_down, 0)) + end) + + assert_receive {:DOWN, ^mon, _, _, _} + end) =~ ~r""" + \[error\] GenServer #PID<\d+\.\d+\.\d+> terminating + \*\* \(RuntimeError\) oops + .* + Last message \(from #PID<\d+\.\d+\.\d+>\): :error_on_down + State: :ok + Client #PID<\d+\.\d+\.\d+> is dead + """s end test "translates GenServer crashes with no client" do @@ -349,11 +318,9 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - assert {%RuntimeError{message: "oops"}, [_ | _]} = task_metadata[:crash_reason] - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = task_metadata[:crash_reason] + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] refute Keyword.has_key?(task_metadata, :initial_call) assert process_metadata[:initial_call] == {Logger.TranslatorTest, :task, 1} @@ -375,11 +342,9 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - assert {:oops, [_ | _]} = task_metadata[:crash_reason] - if :erlang.system_info(:otp_release) >= '20' do - assert {%ErlangError{original: :oops}, [_ | _]} = process_metadata[:crash_reason] - end + assert {:oops, [_ | _]} = task_metadata[:crash_reason] + assert {%ErlangError{original: :oops}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task undef module crash" do @@ -399,11 +364,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = task_metadata[:crash_reason] - - if :erlang.system_info(:otp_release) >= '20' do - assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = - process_metadata[:crash_reason] - end + assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task undef function crash" do @@ -423,11 +384,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = task_metadata[:crash_reason] - - if :erlang.system_info(:otp_release) >= '20' do - assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = - process_metadata[:crash_reason] - end + assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task raising ErlangError" do @@ -453,11 +410,9 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - assert {%ErlangError{original: :foo}, [_ | _]} = task_metadata[:crash_reason] - if :erlang.system_info(:otp_release) >= '20' do - assert {%ErlangError{original: :foo}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%ErlangError{original: :foo}, [_ | _]} = task_metadata[:crash_reason] + assert {%ErlangError{original: :foo}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task raising Erlang badarg error" do @@ -477,11 +432,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert {%ArgumentError{message: "argument error"}, [_ | _]} = task_metadata[:crash_reason] - - if :erlang.system_info(:otp_release) >= '20' do - assert {%ArgumentError{message: "argument error"}, [_ | _]} = - process_metadata[:crash_reason] - end + assert {%ArgumentError{message: "argument error"}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task exiting abnormally" do @@ -499,11 +450,9 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - assert {:abnormal, [_ | _]} = task_metadata[:crash_reason] - if :erlang.system_info(:otp_release) >= '20' do - assert {:abnormal, [_ | _]} = process_metadata[:crash_reason] - end + assert {:abnormal, [_ | _]} = task_metadata[:crash_reason] + assert {:abnormal, [_ | _]} = process_metadata[:crash_reason] end test "translates application start" do @@ -537,10 +486,7 @@ defmodule Logger.TranslatorTest do """ assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] end test "translates :proc_lib crashes" do @@ -566,10 +512,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert process_metadata[:foo] == :bar - - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] end test "skips :proc_lib crashes with disabled metadata" do @@ -609,10 +552,7 @@ defmodule Logger.TranslatorTest do """s assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] end test "translates :proc_lib crashes without initial call" do @@ -636,10 +576,7 @@ defmodule Logger.TranslatorTest do """s assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] end test "translates :proc_lib crashes with neighbour" do @@ -692,8 +629,8 @@ defmodule Logger.TranslatorTest do send(pid, :go) receive do: ({:DOWN, ^ref, _, _, _} -> :ok) end) =~ ~r""" - Ancestors: \[#PID<\d+\.\d+\.\d+>\](?: - Message Queue Length: 1(?#TODO: Require once depend on Erlang/OTP 20)|) + Ancestors: \[#PID<\d+\.\d+\.\d+>\] + Message Queue Length: 1 Messages: \[:message\] Links: \[\] Dictionary: \[\] @@ -706,11 +643,9 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} - assert {%RuntimeError{message: "oops"}, [_ | _]} = task_metadata[:crash_reason] - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = task_metadata[:crash_reason] + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] end test "translates :proc_lib crashes with neighbour on debug" do @@ -731,9 +666,9 @@ defmodule Logger.TranslatorTest do Status: :waiting Heap Size: \d+ Stack Size: \d+ - Reductions: \d+(?: + Reductions: \d+ Current Stacktrace: - test/logger/translator_test.exs:\d+: Logger.TranslatorTest.sleep/1(?#TODO: Require once depend on Erlang/OTP 20)|) + test/logger/translator_test.exs:\d+: Logger.TranslatorTest.sleep/1 """ end @@ -854,11 +789,9 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, _child_metadata}} assert_receive {:error, _pid, {Logger, ["Child ", "Task" | _], _ts, _child_task_metadata}} - assert {:stop, [_ | _]} = task_metadata[:crash_reason] - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = task_metadata[:crash_reason] + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates Supervisor reports max restarts shutdown" do @@ -878,11 +811,9 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, _child_metadata}} assert_receive {:error, _pid, {Logger, ["Child ", "Task" | _], _ts, _child_task_metadata}} - assert {:stop, [_ | _]} = task_metadata[:crash_reason] - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = task_metadata[:crash_reason] + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates Supervisor reports abnormal shutdown" do @@ -899,10 +830,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, _child_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates Supervisor reports abnormal shutdown on debug" do @@ -924,10 +852,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, _child_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates Supervisor reports abnormal shutdown in simple_one_for_one" do @@ -948,10 +873,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Children " | _], _ts, _children_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates DynamicSupervisor reports abnormal shutdown" do @@ -972,10 +894,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, child_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates DynamicSupervisor reports abnormal shutdown including extra_arguments" do @@ -999,10 +918,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, _child_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates named DynamicSupervisor reports abnormal shutdown" do @@ -1023,10 +939,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, _child_metadata}} - - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "translates :supervisor_bridge progress" do @@ -1060,10 +973,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Child of Supervisor " | _], _ts, _child_metadata}} assert {:stop, [_ | _]} = task_metadata[:crash_reason] - - if :erlang.system_info(:otp_release) >= '20' do - assert {:stop, [_ | _]} = process_metadata[:crash_reason] - end + assert {:stop, [_ | _]} = process_metadata[:crash_reason] end test "reports :undefined MFA properly" do @@ -1092,10 +1002,7 @@ defmodule Logger.TranslatorTest do assert_receive {:error, _pid, {Logger, ["Child " | _], _ts, child_metadata}} assert {%RuntimeError{message: "oops"}, [_ | _]} = server_metadata[:crash_reason] - - if :erlang.system_info(:otp_release) >= '20' do - assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] - end + assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] after :code.purge(WeirdFunctionNamesGenServer) :code.delete(WeirdFunctionNamesGenServer) diff --git a/lib/mix/lib/mix/tasks/escript.build.ex b/lib/mix/lib/mix/tasks/escript.build.ex index 3b06f09e2..10959a50e 100644 --- a/lib/mix/lib/mix/tasks/escript.build.ex +++ b/lib/mix/lib/mix/tasks/escript.build.ex @@ -387,14 +387,14 @@ defmodule Mix.Tasks.Escript.Build do erl_version = :erlang.system_info(:otp_release) case :string.to_integer(erl_version) do - {num, _} when num >= 19 -> - nil + {num, _} when num >= 20 -> + :ok _ -> error_message = [ "Incompatible Erlang/OTP release: ", erl_version, - ".\nThis escript requires at least Erlang/OTP 19.0\n" + ".\nThis escript requires at least Erlang/OTP 20.0\n" ] io_error(error_message) |