Hello community, here is the log from the commit of package elixir for openSUSE:Factory checked in at 2020-07-24 09:57:15 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/elixir (Old) and /work/SRC/openSUSE:Factory/.elixir.new.3592 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "elixir" Fri Jul 24 09:57:15 2020 rev:11 rq:822142 version:1.10.4 Changes: -------- --- /work/SRC/openSUSE:Factory/elixir/elixir.changes 2020-04-27 23:36:48.243338891 +0200 +++ /work/SRC/openSUSE:Factory/.elixir.new.3592/elixir.changes 2020-07-24 09:59:09.669598618 +0200 @@ -1,0 +2,22 @@ +Wed Jul 8 08:09:49 UTC 2020 - Gabriele Santomaggio <[email protected]> + +- Elixir 1.10.4 + * Bug fixes + Elixir + [Kernel] Fix a bug where custom types were printed as built-in types + [Kernel] Don't add compile-time dependency on defdelegate + [Kernel] Add line numbers to warnings on deprecated imports + [Kernel] Report the correct line number when raising inside a macro + [Task] Include callers in translated Logger metadata for Task + [Task] Fix Task PID and caller in Task Supervisor reports + ExUnit + [ExUnit.Formatter] Avoid crashes when diffing guards when the pattern does not match + [ExUnit.Formatter] Also blame exceptions that come from linked and trapped exits + IEx + [IEx.Helpers] Do not crash when printing a type that cannot be code formatted + Mix + [mix app.start] Fix reading .app file located in archives (.ez files) + [mix local.hex] Provide more guidance when Hex can't be installed + [mix release] Properly encode config in releases + +------------------------------------------------------------------- Old: ---- elixir-1.10.3.tar.gz New: ---- elixir-1.10.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ elixir-doc.spec ++++++ --- /var/tmp/diff_new_pack.FadwED/_old 2020-07-24 09:59:12.177600954 +0200 +++ /var/tmp/diff_new_pack.FadwED/_new 2020-07-24 09:59:12.181600957 +0200 @@ -17,7 +17,7 @@ Name: elixir-doc -Version: 1.10.3 +Version: 1.10.4 Release: 0 Summary: Documentation for elixir License: Apache-2.0 @@ -26,6 +26,7 @@ Source0: https://github.com/elixir-lang/elixir/archive/v%{version}.tar.gz#/elixir-%{version}.tar.gz BuildRequires: elixir BuildRequires: elixir-ex_doc +BuildRequires: unzip BuildRoot: %{_tmppath}/%{name}-%{version}-build BuildArch: noarch Requires: elixir = %{version} ++++++ elixir.spec ++++++ --- /var/tmp/diff_new_pack.FadwED/_old 2020-07-24 09:59:12.201600976 +0200 +++ /var/tmp/diff_new_pack.FadwED/_new 2020-07-24 09:59:12.205600980 +0200 @@ -17,7 +17,7 @@ Name: elixir -Version: 1.10.3 +Version: 1.10.4 Release: 0 Summary: Functional meta-programming aware language built atop Erlang License: Apache-2.0 ++++++ elixir-1.10.3.tar.gz -> elixir-1.10.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/.cirrus.yml new/elixir-1.10.4/.cirrus.yml --- old/elixir-1.10.3/.cirrus.yml 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/.cirrus.yml 2020-07-04 13:10:47.000000000 +0200 @@ -27,16 +27,14 @@ - env: CHECK_POSIX_COMPLIANT: true CHECK_REPRODUCIBLE: true - OTP_RELEASE: OTP-22.1 + OTP_RELEASE: OTP-23.0 + - env: + OTP_RELEASE: OTP-22.3 - env: OTP_RELEASE: OTP-22.0 - env: OTP_RELEASE: OTP-21.3.8 - env: - OTP_RELEASE: OTP-21.2 - - env: - OTP_RELEASE: OTP-21.1 - - env: OTP_RELEASE: OTP-21.0 - name: Linux, OTP-${OTP_RELEASE}, development, Ubuntu 14.04 @@ -110,7 +108,7 @@ image: fertapric/elixir-ci:otp-win64-${OTP_RELEASE} os_version: ${OS_VERSION} cpu: 4 - memory: 3840Mi + memory: 6GB install_script: - rmdir /s /q .git @@ -143,7 +141,6 @@ LC_ALL: en_US.UTF-8 install_script: - - sudo pkg update - pkg install -y erlang git gmake - rm -rf .git - gmake compile diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/CHANGELOG.md new/elixir-1.10.4/CHANGELOG.md --- old/elixir-1.10.3/CHANGELOG.md 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/CHANGELOG.md 2020-07-04 13:10:47.000000000 +0200 @@ -155,6 +155,34 @@ Now imagine that `json_payload` is a large JSON blob and the `"key"` inside the `"body"` did not have value of `"foo"`. In previous Elixir versions, if the assertion failed, Elixir would print the right side and let you up to your own devices to figure out what went wrong. In Elixir v1.10, we diff the data structure against the pattern so you can see exactly which parts of the data matched the pattern and which ones did not. Note ExUnit already performed diffing when comparing data types, this new version adds diffing when matching data against a pattern. +## v1.10.4 (2020-07-04) + +### 1. Bug fixes + +#### Elixir + + * [Kernel] Fix a bug where custom types were printed as built-in types + * [Kernel] Don't add compile-time dependency on `defdelegate` + * [Kernel] Add line numbers to warnings on deprecated imports + * [Kernel] Report the correct line number when raising inside a macro + * [Task] Include callers in translated Logger metadata for Task + * [Task] Fix Task PID and caller in Task Supervisor reports + +#### ExUnit + + * [ExUnit.Formatter] Avoid crashes when diffing guards when the pattern does not match + * [ExUnit.Formatter] Also blame exceptions that come from linked and trapped exits + +#### IEx + + * [IEx.Helpers] Do not crash when printing a type that cannot be code formatted + +#### Mix + + * [mix app.start] Fix reading `.app` file located in archives (`.ez` files) + * [mix local.hex] Provide more guidance when Hex can't be installed + * [mix release] Properly encode config in releases + ## v1.10.3 (2020-04-25) ### 1. Bug fixes diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/VERSION new/elixir-1.10.4/VERSION --- old/elixir-1.10.3/VERSION 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/VERSION 2020-07-04 13:10:47.000000000 +0200 @@ -1 +1 @@ -1.10.3 \ No newline at end of file +1.10.4 \ No newline at end of file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/lib/code/typespec.ex new/elixir-1.10.4/lib/elixir/lib/code/typespec.ex --- old/elixir-1.10.3/lib/elixir/lib/code/typespec.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/lib/code/typespec.ex 2020-07-04 13:10:47.000000000 +0200 @@ -217,7 +217,8 @@ end defp typespec_to_quoted({:user_type, line, name, args}) do - typespec_to_quoted({:type, line, name, args}) + args = for arg <- args, do: typespec_to_quoted(arg) + {name, [line: line], args} end defp typespec_to_quoted({:type, line, :tuple, :any}) do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/lib/config/provider.ex new/elixir-1.10.4/lib/elixir/lib/config/provider.ex --- old/elixir-1.10.3/lib/elixir/lib/config/provider.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/lib/config/provider.ex 2020-07-04 13:10:47.000000000 +0200 @@ -350,7 +350,7 @@ defp write_config!(config, path) do contents = :io_lib.format("%% coding: utf-8~n~tw.~n", [config]) - case File.write(path, contents, [:utf8]) do + case File.write(path, IO.chardata_to_string(contents)) do :ok -> :ok diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/lib/io.ex new/elixir-1.10.4/lib/elixir/lib/io.ex --- old/elixir-1.10.3/lib/elixir/lib/io.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/lib/io.ex 2020-07-04 13:10:47.000000000 +0200 @@ -317,7 +317,7 @@ @spec warn(chardata | String.Chars.t(), Exception.stacktrace()) :: :ok def warn(message, []) do message = [to_chardata(message), ?\n] - :elixir_errors.io_warn(nil, nil, message, message) + :elixir_errors.io_warn(0, nil, message, message) end def warn(message, [{_, _, _, opts} | _] = stacktrace) do @@ -327,7 +327,7 @@ file = opts[:file] :elixir_errors.io_warn( - line, + line || 0, file && List.to_string(file), message, [message, ?\n, " ", formatted_trace, ?\n] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/lib/kernel/parallel_compiler.ex new/elixir-1.10.4/lib/elixir/lib/kernel/parallel_compiler.ex --- old/elixir-1.10.3/lib/elixir/lib/kernel/parallel_compiler.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/lib/kernel/parallel_compiler.ex 2020-07-04 13:10:47.000000000 +0200 @@ -652,6 +652,12 @@ end end + defp get_line(file, _reason, [{_, _, _, [file: 'expanding macro']}, {_, _, _, info} | _]) do + if Keyword.get(info, :file) == to_charlist(Path.relative_to_cwd(file)) do + Keyword.get(info, :line) + end + end + defp get_line(file, _reason, [{_, _, _, info} | _]) do if Keyword.get(info, :file) == to_charlist(Path.relative_to_cwd(file)) do Keyword.get(info, :line) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/lib/kernel.ex new/elixir-1.10.4/lib/elixir/lib/kernel.ex --- old/elixir-1.10.3/lib/elixir/lib/kernel.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/lib/kernel.ex 2020-07-04 13:10:47.000000000 +0200 @@ -4881,6 +4881,18 @@ defmacro defdelegate(funs, opts) do funs = Macro.escape(funs, unquote: true) + # don't add compile-time dependency on :to + opts = + with true <- is_list(opts), + {:ok, target} <- Keyword.fetch(opts, :to), + {:__aliases__, _, _} <- target do + target = Macro.expand(target, %{__CALLER__ | function: {:__info__, 1}}) + Keyword.replace!(opts, :to, target) + else + _ -> + opts + end + quote bind_quoted: [funs: funs, opts: opts] do target = Keyword.get(opts, :to) || raise ArgumentError, "expected to: to be given as argument" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/lib/module/checker.ex new/elixir-1.10.4/lib/elixir/lib/module/checker.ex --- old/elixir-1.10.3/lib/elixir/lib/module/checker.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/lib/module/checker.ex 2020-07-04 13:10:47.000000000 +0200 @@ -231,7 +231,7 @@ defp warn(meta, state, warning) do {fun, arity} = state.function - location = {state.file, meta[:line], {state.module, fun, arity}} + location = {state.file, meta[:line] || 0, {state.module, fun, arity}} %{state | warnings: [{__MODULE__, warning, location} | state.warnings]} end @@ -306,7 +306,7 @@ defp format_location({file, line, {module, fun, arity}}) do file = Path.relative_to_cwd(file) - line = if line, do: [Integer.to_string(line), ": "], else: [] + line = if line > 0, do: [Integer.to_string(line), ": "], else: [] mfa = Exception.format_mfa(module, fun, arity) [" ", file, ?:, line, mfa, ?\n] end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/lib/task/supervised.ex new/elixir-1.10.4/lib/elixir/lib/task/supervised.ex --- old/elixir-1.10.3/lib/elixir/lib/task/supervised.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/lib/task/supervised.ex 2020-07-04 13:10:47.000000000 +0200 @@ -102,8 +102,8 @@ %{ label: {Task.Supervisor, :terminating}, report: %{ - name: get_from(owner), - starter: self(), + name: self(), + starter: get_from(owner), function: fun, args: args, reason: {log_value(kind, value), __STACKTRACE__} @@ -112,7 +112,8 @@ %{ domain: [:otp, :elixir], error_logger: %{tag: :error_msg}, - report_cb: &__MODULE__.format_report/1 + report_cb: &__MODULE__.format_report/1, + callers: Process.get(:"$callers") } ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' "old/elixir-1.10.3/lib/elixir/pages/Compatibility and Deprecations.md" "new/elixir-1.10.4/lib/elixir/pages/Compatibility and Deprecations.md" --- "old/elixir-1.10.3/lib/elixir/pages/Compatibility and Deprecations.md" 2020-04-25 10:30:20.000000000 +0200 +++ "new/elixir-1.10.4/lib/elixir/pages/Compatibility and Deprecations.md" 2020-07-04 13:10:47.000000000 +0200 @@ -52,7 +52,7 @@ 1.7 | 19 - 22 1.8 | 20 - 22 1.9 | 20 - 22 -1.10 | 21 - 22 +1.10 | 21 - 22 (and Erlang/OTP 23 from v1.10.3) While Elixir often adds compatibility to new Erlang/OTP versions on released branches, such as support for Erlang/OTP 20 in v1.4.5, those releases usually contain the minimum changes for Elixir to run without errors. Only the next minor release, in this case v1.5.0, does effectively leverage the new features provided by the latest Erlang/OTP release. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/src/elixir_dispatch.erl new/elixir-1.10.4/lib/elixir/src/elixir_dispatch.erl --- old/elixir-1.10.3/lib/elixir/src/elixir_dispatch.erl 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/src/elixir_dispatch.erl 2020-07-04 13:10:47.000000000 +0200 @@ -92,7 +92,7 @@ {ok, Receiver, Quoted} -> expand_quoted(Meta, Receiver, Name, Arity, Quoted, E); {ok, Receiver, NewName, NewArgs} -> - elixir_expand:expand({{'.', [], [Receiver, NewName]}, Meta, NewArgs}, E); + elixir_expand:expand({{'.', Meta, [Receiver, NewName]}, Meta, NewArgs}, E); error -> Callback() end. @@ -328,9 +328,10 @@ end; get_macros(Receiver, true) -> - case is_ensure_loaded(Receiver) of - true -> get_info(Receiver, macros); - false -> [] + try + Receiver:'__info__'(macros) + catch + error:_ -> [] end. %% Kernel deprecations are inlined. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/test/elixir/config/provider_test.exs new/elixir-1.10.4/lib/elixir/test/elixir/config/provider_test.exs --- old/elixir-1.10.3/lib/elixir/test/elixir/config/provider_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/test/elixir/config/provider_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -83,11 +83,11 @@ assert config[@config_app] == [config_providers_booted: {:booted, nil}] end - @tag sys_config: [my_app: [encoding: {:"£", "£", '£'}]] + @tag sys_config: [my_app: [encoding: {:time_μs, :"£", "£", '£'}]] test "writes sys_config with encoding" do init_and_assert_boot() config = consult(@sys_config) - assert config[:my_app][:encoding] == {:"£", "£", '£'} + assert config[:my_app][:encoding] == {:time_μs, :"£", "£", '£'} end @tag sys_config: [my_app: [key: :old_value, sys_key: :sys_value, extra_config: :old_value]] @@ -185,6 +185,6 @@ end defp write_sys_config!(data) do - File.write!(@sys_config, :io_lib.format("~tw.~n", [data]), [:utf8]) + File.write!(@sys_config, IO.chardata_to_string(:io_lib.format("~tw.~n", [data]))) end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/test/elixir/kernel/expansion_test.exs new/elixir-1.10.4/lib/elixir/test/elixir/kernel/expansion_test.exs --- old/elixir-1.10.3/lib/elixir/test/elixir/kernel/expansion_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/test/elixir/kernel/expansion_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -654,8 +654,8 @@ expand(quote(do: [1] ++ 2 ++ [3] = [1, 2, 3])) end - assert {:=, _, [-1, {{:., [], [:erlang, :-]}, _, [1]}]} = expand(quote(do: -1 = -1)) - assert {:=, _, [1, {{:., [], [:erlang, :+]}, _, [1]}]} = expand(quote(do: +1 = +1)) + assert {:=, _, [-1, {{:., _, [:erlang, :-]}, _, [1]}]} = expand(quote(do: -1 = -1)) + assert {:=, _, [1, {{:., _, [:erlang, :+]}, _, [1]}]} = expand(quote(do: +1 = +1)) assert {:=, _, [[{:|, _, [1, [{:|, _, [2, 3]}]]}], [1, 2, 3]]} = expand(quote(do: [1] ++ [2] ++ 3 = [1, 2, 3])) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/test/elixir/kernel/lexical_tracker_test.exs new/elixir-1.10.4/lib/elixir/test/elixir/kernel/lexical_tracker_test.exs --- old/elixir-1.10.3/lib/elixir/test/elixir/kernel/lexical_tracker_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/test/elixir/kernel/lexical_tracker_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -126,4 +126,21 @@ refute Foo.Bar in runtime refute Foo.Bar in compile end + + test "defdelegate with literal does not add compile dependency" do + {{compile, _structs, _runtime, _}, _binding} = + Code.eval_string(""" + defmodule Kernel.LexicalTrackerTest.Defdelegate do + defdelegate a, to: A + + opts = [to: B] + defdelegate b, opts + + Kernel.LexicalTracker.references(__ENV__.lexical_tracker) + end |> elem(3) + """) + + refute A in compile + assert B in compile + end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/test/elixir/module/checker_test.exs new/elixir-1.10.4/lib/elixir/test/elixir/module/checker_test.exs --- old/elixir-1.10.3/lib/elixir/test/elixir/module/checker_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/test/elixir/module/checker_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -639,6 +639,31 @@ assert_warnings(files, warning) end + test "reports imported functions" do + files = %{ + "a.ex" => """ + defmodule A do + @deprecated "oops" + def a, do: :ok + end + """, + "b.ex" => """ + defmodule B do + import A + def b, do: a() + end + """ + } + + warning = """ + warning: A.a/0 is deprecated. oops + b.ex:3: B.b/0 + + """ + + assert_warnings(files, warning) + end + test "reports structs" do files = %{ "a.ex" => """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/elixir/test/elixir/typespec_test.exs new/elixir-1.10.4/lib/elixir/test/elixir/typespec_test.exs --- old/elixir-1.10.3/lib/elixir/test/elixir/typespec_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/elixir/test/elixir/typespec_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -1183,6 +1183,12 @@ quoted = Enum.sort([ quote(do: @spec(foo() :: integer())), + quote(do: @spec(foo() :: union())), + quote(do: @spec(foo() :: union(integer()))), + quote(do: @spec(foo() :: truly_union())), + quote(do: @spec(foo(union()) :: union())), + quote(do: @spec(foo(union(integer())) :: union(integer()))), + quote(do: @spec(foo(truly_union()) :: truly_union())), quote(do: @spec(foo(atom()) :: integer() | [{}])), quote(do: @spec(foo(arg) :: integer() when [arg: integer()])), quote(do: @spec(foo(arg) :: arg when [arg: var])), @@ -1191,6 +1197,10 @@ bytecode = test_module do + @type union :: any() + @type union(t) :: t + @type truly_union :: list | map | union + def foo(), do: 1 def foo(arg), do: arg Module.eval_quoted(__MODULE__, quote(do: (unquote_splicing(quoted)))) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/ex_unit/examples/one_of_each.exs new/elixir-1.10.4/lib/ex_unit/examples/one_of_each.exs --- old/elixir-1.10.3/lib/ex_unit/examples/one_of_each.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/ex_unit/examples/one_of_each.exs 2020-07-04 13:10:47.000000000 +0200 @@ -1,4 +1,4 @@ -ExUnit.start [seed: 0] +ExUnit.start(seed: 0) defmodule TestOneOfEach do @moduledoc """ @@ -10,8 +10,8 @@ @one 1 @two 2 - @long_data_1 [field1: "one", field2: {:two1, :two2}, field3: 'three', field4: [1, 2, 3, 4]] - @long_data_2 [field1: "one", field2: {:two1, :two3}, field3: 'three', field4: [1, 2, 3, 4]] + @long_data_1 [field1: "one", field2: {:two1, :two2}, field3: 'three', field4: [1, 2, 3, 4]] + @long_data_2 [field1: "one", field2: {:two1, :two3}, field3: 'three', field4: [1, 2, 3, 4]] setup do {:ok, user_id: 1, post_id: 2, many_ids: Enum.to_list(1..50)} @@ -62,10 +62,10 @@ end test "12. assert that a message is received within a timeout" do - send self(), {:ok, 1} - send self(), :message_in_my_inbox - send self(), {:ok, 2} - send self(), :another_message + send(self(), {:ok, 1}) + send(self(), :message_in_my_inbox) + send(self(), {:ok, 2}) + send(self(), :another_message) assert_receive :no_message_after_timeout end @@ -75,14 +75,14 @@ test "14. assert an exception with a given message is raised" do assert_raise(SomeException, "some message", fn -> - raise "other exception" - end) + raise "other exception" + end) end test "15. assert an exception with a given message is raised, but the message is wrong" do assert_raise(RuntimeError, "some message", fn -> - raise "other error" - end) + raise "other error" + end) end test "16. assert an exception is raised" do @@ -90,7 +90,7 @@ end test "17. assert two values are within some delta" do - assert_in_delta 3.1415926, 22.0/7, 0.001 + assert_in_delta 3.1415926, 22.0 / 7, 0.001 end test "18. refute a value with a message" do @@ -98,12 +98,12 @@ end test "19. refute a message is received within a timeout" do - send self(), {:hello, "Dave"} + send(self(), {:hello, "Dave"}) refute_receive {:hello, _}, 1000 end test "20. refute a message is ready to be received" do - send self(), :hello_again + send(self(), :hello_again) refute_received :hello_again end @@ -116,7 +116,7 @@ end test "23. flunk" do - flunk "we failed. totally" + flunk("we failed. totally") end test "24. exception raised while running test" do @@ -124,7 +124,8 @@ end test "25. error due to exit" do - spawn_link fn -> raise "oops" end + spawn_link(fn -> raise "oops" end) + receive do end end @@ -133,15 +134,17 @@ error1 = try do assert [@one] = [@two] - rescue e in ExUnit.AssertionError -> - {:error, e, System.stacktrace} + rescue + e in ExUnit.AssertionError -> + {:error, e, __STACKTRACE__} end error2 = try do assert @one * 4 > @two * 3 - rescue e in ExUnit.AssertionError -> - {:error, e, System.stacktrace} + rescue + e in ExUnit.AssertionError -> + {:error, e, __STACKTRACE__} end raise ExUnit.MultiError, errors: [error1, error2] @@ -150,8 +153,8 @@ @tag capture_log: true test "27. log capturing" do require Logger - Logger.debug "this will be logged" - flunk "oops" + Logger.debug("this will be logged") + flunk("oops") end test "28. function clause error" do @@ -162,6 +165,28 @@ assert some_vars(1 + 2, 3 + 4) end + @tag :capture_log + test "30. linked assertion error" do + Task.async(fn -> assert 1 == 2 end) |> Task.await() + end + + @tag :capture_log + test "31. linked function clause error" do + Task.async(fn -> Access.fetch(:foo, :bar) end) |> Task.await() + end + + @tag :capture_log + test "32. trapped assertion error" do + Process.flag(:trap_exit, true) + Task.async(fn -> assert 1 == 2 end) |> Task.await() + end + + @tag :capture_log + test "33. trapped function clause error" do + Process.flag(:trap_exit, true) + Task.async(fn -> Access.fetch(:foo, :bar) end) |> Task.await() + end + defp some_vars(_a, _b) do false end @@ -171,6 +196,6 @@ end defp ignite(val) do - 1/val + 1 / val end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/ex_unit/lib/ex_unit/diff.ex new/elixir-1.10.4/lib/ex_unit/lib/ex_unit/diff.ex --- old/elixir-1.10.3/lib/ex_unit/lib/ex_unit/diff.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/ex_unit/lib/ex_unit/diff.ex 2020-07-04 13:10:47.000000000 +0200 @@ -180,13 +180,18 @@ defp diff_guard({:when, _, [expression, clause]}, right, env) do {diff_expression, post_env} = diff_quoted(expression, right, env) - bindings = Map.merge(post_env.pins, post_env.current_vars) - {diff_clause, clause_equivalent?} = diff_guard_clause(clause, Map.to_list(bindings)) + {guard_clause, guard_equivalent?} = + if diff_expression.equivalent? do + bindings = Map.merge(post_env.pins, post_env.current_vars) + diff_guard_clause(clause, Map.to_list(bindings)) + else + {clause, false} + end diff = %__MODULE__{ diff_expression - | left: {:when, [], [diff_expression.left, diff_clause]}, - equivalent?: diff_expression.equivalent? and clause_equivalent? + | left: {:when, [], [diff_expression.left, guard_clause]}, + equivalent?: guard_equivalent? } {diff, post_env} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/ex_unit/lib/ex_unit/formatter.ex new/elixir-1.10.4/lib/ex_unit/lib/ex_unit/formatter.ex --- old/elixir-1.10.3/lib/ex_unit/lib/ex_unit/formatter.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/ex_unit/lib/ex_unit/formatter.ex 2020-07-04 13:10:47.000000000 +0200 @@ -54,6 +54,7 @@ @counter_padding " " @mailbox_label_padding @counter_padding <> " " + @formatter_exceptions [ExUnit.AssertionError, FunctionClauseError] @no_value ExUnit.AssertionError.no_value() @doc """ @@ -136,10 +137,10 @@ @doc false def format_assertion_error(%ExUnit.AssertionError{} = struct) do - format_assertion_error(%{}, struct, [], :infinity, fn _, msg -> msg end, "") + format_exception(%{}, struct, [], :infinity, fn _, msg -> msg end, "") |> elem(0) end - defp format_assertion_error(test, struct, stack, width, formatter, counter_padding) do + defp format_exception(test, %ExUnit.AssertionError{} = struct, stack, width, formatter, pad) do label_padding_size = if has_value?(struct.right), do: 7, else: 6 padding_size = label_padding_size + byte_size(@counter_padding) @@ -148,16 +149,27 @@ do: &pad_multiline(&1, padding_size), else: &code_multiline(&1, padding_size) - [ - note: if_value(struct.message, &format_message(&1, formatter)), - doctest: if_value(struct.doctest, &pad_multiline(&1, 2 + byte_size(@counter_padding))), - code: if_value(struct.expr, code_multiline), - code: unless_value(struct.expr, fn -> get_code(test, stack) || @no_value end), - arguments: if_value(struct.args, &format_args(&1, width)) - ] - |> Kernel.++(format_context(struct, formatter, padding_size, width)) - |> format_meta(formatter, counter_padding, label_padding_size) - |> IO.iodata_to_binary() + formatted = + [ + note: if_value(struct.message, &format_message(&1, formatter)), + doctest: if_value(struct.doctest, &pad_multiline(&1, 2 + byte_size(@counter_padding))), + code: if_value(struct.expr, code_multiline), + code: unless_value(struct.expr, fn -> get_code(test, stack) || @no_value end), + arguments: if_value(struct.args, &format_args(&1, width)) + ] + |> Kernel.++(format_context(struct, formatter, padding_size, width)) + |> format_meta(formatter, pad, label_padding_size) + |> IO.iodata_to_binary() + + {formatted, stack} + end + + defp format_exception(test, %FunctionClauseError{} = struct, stack, _width, formatter, _pad) do + {blamed, stack} = Exception.blame(:error, struct, stack) + banner = Exception.format_banner(:error, struct) + blamed = FunctionClauseError.blame(blamed, &inspect/1, &blame_match(&1, &2, formatter)) + message = error_info(banner, formatter) <> "\n" <> pad(String.trim_leading(blamed, "\n")) + {message <> format_code(test, stack, formatter), stack} end @doc false @@ -179,29 +191,47 @@ end) end - defp format_kind_reason( - test, - :error, - %ExUnit.AssertionError{} = struct, - stack, - width, - formatter - ) do - {format_assertion_error(test, struct, stack, width, formatter, @counter_padding), stack} + defp format_kind_reason(test, :error, %mod{} = struct, stack, width, formatter) + when mod in @formatter_exceptions do + format_exception(test, struct, stack, width, formatter, @counter_padding) + end + + defp format_kind_reason(test, kind, reason, stack, width, formatter) do + case linked_or_trapped_exit(kind, reason) do + {header, wrapped_reason, wrapped_stack} -> + struct = Exception.normalize(:error, wrapped_reason, wrapped_stack) + + {formatted_reason, _} = + format_exception(test, struct, wrapped_stack, width, formatter, @counter_padding) + + formatted_stack = format_stacktrace(wrapped_stack, test.module, test.name, formatter) + {error_info(header, formatter) <> pad(formatted_reason <> formatted_stack), stack} + + :error -> + {reason, stack} = Exception.blame(kind, reason, stack) + message = error_info(Exception.format_banner(kind, reason), formatter) + {message <> format_code(test, stack, formatter), stack} + end end - defp format_kind_reason(test, :error, %FunctionClauseError{} = struct, stack, _width, formatter) do - {blamed, stack} = Exception.blame(:error, struct, stack) - banner = Exception.format_banner(:error, struct) - blamed = FunctionClauseError.blame(blamed, &inspect/1, &blame_match(&1, &2, formatter)) - message = error_info(banner, formatter) <> "\n" <> pad(String.trim_leading(blamed, "\n")) - {message <> format_code(test, stack, formatter), stack} + defp linked_or_trapped_exit({:EXIT, pid}, {reason, [_ | _] = stack}) + when :erlang.map_get(:__struct__, reason) in @formatter_exceptions + when reason == :function_clause do + {"** (EXIT from #{inspect(pid)}) an exception was raised:\n", reason, stack} + end + + defp linked_or_trapped_exit(:exit, {{reason, [_ | _] = stack}, {mod, fun, args}}) + when is_atom(mod) and is_atom(fun) and is_list(args) and + :erlang.map_get(:__struct__, reason) in @formatter_exceptions + when is_atom(mod) and is_atom(fun) and is_list(args) and reason == :function_clause do + { + "** (exit) exited in: #{Exception.format_mfa(mod, fun, args)}\n ** (EXIT) an exception was raised:", + reason, + stack + } end - defp format_kind_reason(test, kind, reason, stack, _width, formatter) do - message = error_info(Exception.format_banner(kind, reason), formatter) - {message <> format_code(test, stack, formatter), stack} - end + defp linked_or_trapped_exit(_kind, _reason), do: :error defp format_code(test, stack, formatter) do if snippet = get_code(test, stack) do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/ex_unit/test/ex_unit/diff_test.exs new/elixir-1.10.4/lib/ex_unit/test/ex_unit/diff_test.exs --- old/elixir-1.10.3/lib/ex_unit/test/ex_unit/diff_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/ex_unit/test/ex_unit/diff_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -914,6 +914,7 @@ refute_diff((x when x == 1 or x == 2) = 0, "x when -x == 1- or -x == 2-", "0") refute_diff((x when x == 1 when x == 2) = 0, "x when -x == 1- when -x == 2-", "0") refute_diff((x when x in [1, 2]) = 0, "x when -x in [1, 2]-", "0") + refute_diff(({:ok, x} when x == 1) = :error, "-{:ok, x}- when x == 1", "+:error+") end test "charlists" do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/ex_unit/test/ex_unit/formatter_test.exs new/elixir-1.10.4/lib/ex_unit/test/ex_unit/formatter_test.exs --- old/elixir-1.10.3/lib/ex_unit/test/ex_unit/formatter_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/ex_unit/test/ex_unit/formatter_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -69,6 +69,61 @@ """ end + test "formats test exits with function clause mfa" do + {error, stack} = + try do + Access.fetch(:foo, :bar) + catch + :error, error -> {error, __STACKTRACE__} + end + + failure = [{:exit, {{error, stack}, {:mod, :fun, []}}, []}] + + assert trim_multiline_whitespace(format_test_failure(test(), failure, 1, 80, &formatter/2)) =~ + """ + 1) world (Hello) + test/ex_unit/formatter_test.exs:1 + ** (exit) exited in: :mod.fun() + ** (EXIT) an exception was raised: + ** (FunctionClauseError) no function clause matching in Access.fetch/2 + + The following arguments were given to Access.fetch/2: + + # 1 + :foo + + # 2 + :bar + + Attempted function clauses (showing 5 out of 5): + + def fetch(%module{} = container, key) + """ + end + + test "formats test exits with assertion mfa" do + {error, stack} = + try do + assert 1 == 2 + rescue + error -> {error, __STACKTRACE__} + end + + failure = [{:exit, {{error, stack}, {:mod, :fun, []}}, []}] + + assert trim_multiline_whitespace(format_test_failure(test(), failure, 1, 80, &formatter/2)) =~ + """ + 1) world (Hello) + test/ex_unit/formatter_test.exs:1 + ** (exit) exited in: :mod.fun() + ** (EXIT) an exception was raised: + Assertion with == failed + code: assert 1 == 2 + left: 1 + right: 2 + """ + end + test "formats test throws" do failure = [{:throw, 1, []}] @@ -89,6 +144,61 @@ """ end + test "formats test EXITs with function clause errors" do + {error, stack} = + try do + Access.fetch(:foo, :bar) + catch + :error, error -> {error, __STACKTRACE__} + end + + failure = [{{:EXIT, self()}, {error, stack}, []}] + + assert trim_multiline_whitespace(format_test_failure(test(), failure, 1, 80, &formatter/2)) =~ + """ + 1) world (Hello) + test/ex_unit/formatter_test.exs:1 + ** (EXIT from #{inspect(self())}) an exception was raised: + + ** (FunctionClauseError) no function clause matching in Access.fetch/2 + + The following arguments were given to Access.fetch/2: + + # 1 + :foo + + # 2 + :bar + + Attempted function clauses (showing 5 out of 5): + + def fetch(%module{} = container, key) + """ + end + + test "formats test EXITs with assertion errors" do + {error, stack} = + try do + assert 1 == 2 + rescue + error -> {error, __STACKTRACE__} + end + + failure = [{{:EXIT, self()}, {error, stack}, []}] + + assert trim_multiline_whitespace(format_test_failure(test(), failure, 1, 80, &formatter/2)) =~ + """ + 1) world (Hello) + test/ex_unit/formatter_test.exs:1 + ** (EXIT from #{inspect(self())}) an exception was raised: + + Assertion with == failed + code: assert 1 == 2 + left: 1 + right: 2 + """ + end + test "formats test errors with test_location_relative_path" do Application.put_env(:ex_unit, :test_location_relative_path, "apps/sample") failure = [{:error, catch_error(raise "oops"), []}] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/iex/lib/iex/introspection.ex new/elixir-1.10.4/lib/iex/lib/iex/introspection.ex --- old/elixir-1.10.3/lib/iex/lib/iex/introspection.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/iex/lib/iex/introspection.ex 2020-07-04 13:10:47.000000000 +0200 @@ -735,9 +735,15 @@ ## Helpers defp format_typespec(definition, kind, nesting) do - "@#{kind} #{Macro.to_string(definition)}" - |> Code.format_string!(line_length: IEx.width() - 2 * nesting) - |> IO.iodata_to_binary() + string = "@#{kind} #{Macro.to_string(definition)}" + + try do + string + |> Code.format_string!(line_length: IEx.width() - 2 * nesting) + |> IO.iodata_to_binary() + rescue + _ -> string + end |> color_prefix_with_line() |> indent(nesting) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/iex/test/iex/helpers_test.exs new/elixir-1.10.4/lib/iex/test/iex/helpers_test.exs --- old/elixir-1.10.3/lib/iex/test/iex/helpers_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/iex/test/iex/helpers_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -853,6 +853,7 @@ defmodule TypeSample do @typedoc "An ID with description." @type id_with_desc :: {number, String.t} + @type unquote(:"?")() :: :question_mark end """ @@ -872,6 +873,11 @@ An ID with description. """ + + assert capture_io(fn -> t(TypeSample."?"()) end) == """ + @type ?() :: :question_mark + + """ end) after cleanup_modules([TypeSample]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/logger/test/logger/translator_test.exs new/elixir-1.10.4/lib/logger/test/logger/translator_test.exs --- old/elixir-1.10.3/lib/logger/test/logger/translator_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/logger/test/logger/translator_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -306,13 +306,14 @@ test "translates Task crashes" do {:ok, pid} = Task.start_link(__MODULE__, :task, [self()]) + parent = self() assert capture_log(fn -> ref = Process.monitor(pid) send(pid, :go) receive do: ({:DOWN, ^ref, _, _, _} -> :ok) end) =~ ~r""" - \[error\] Task #PID<\d+\.\d+\.\d+> started from #PID<\d+\.\d+\.\d+> terminating + \[error\] Task #{inspect(pid)} started from #{inspect(self())} terminating \*\* \(RuntimeError\) oops .* Function: &Logger.TranslatorTest.task\/1 @@ -324,6 +325,7 @@ assert {%RuntimeError{message: "oops"}, [_ | _]} = task_metadata[:crash_reason] assert {%RuntimeError{message: "oops"}, [_ | _]} = process_metadata[:crash_reason] + assert [parent] == task_metadata[:callers] refute Keyword.has_key?(task_metadata, :initial_call) assert process_metadata[:initial_call] == {Logger.TranslatorTest, :task, 1} @@ -332,6 +334,7 @@ test "translates Task async_stream crashes with neighbour" do fun = fn -> Task.async_stream([:oops], :erlang, :error, []) |> Enum.to_list() end {:ok, pid} = Task.start(__MODULE__, :task, [self(), fun]) + parent = self() assert capture_log(:debug, fn -> ref = Process.monitor(pid) @@ -345,12 +348,15 @@ assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} + assert [pid, parent] == task_metadata[:callers] assert {:oops, [_ | _]} = task_metadata[:crash_reason] assert {%ErlangError{original: :oops}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task undef module crash" do + parent = self() + assert capture_log(fn -> {:ok, pid} = Task.start(:module_does_not_exist, :undef, []) ref = Process.monitor(pid) @@ -365,12 +371,15 @@ assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} + assert [parent] == task_metadata[:callers] assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = task_metadata[:crash_reason] assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task undef function crash" do + parent = self() + assert capture_log(fn -> {:ok, pid} = Task.start(__MODULE__, :undef, []) ref = Process.monitor(pid) @@ -385,12 +394,15 @@ assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} + assert [parent] == task_metadata[:callers] assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = task_metadata[:crash_reason] assert {%UndefinedFunctionError{function: :undef}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task raising ErlangError" do + parent = self() + assert capture_log(fn -> exception = try do @@ -413,12 +425,15 @@ assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} + assert [parent] == task_metadata[:callers] 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 + parent = self() + assert capture_log(fn -> {:ok, pid} = Task.start(:erlang, :error, [:badarg]) ref = Process.monitor(pid) @@ -433,12 +448,15 @@ assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} + assert [parent] == task_metadata[:callers] assert {%ArgumentError{message: "argument error"}, [_ | _]} = task_metadata[:crash_reason] assert {%ArgumentError{message: "argument error"}, [_ | _]} = process_metadata[:crash_reason] end test "translates Task exiting abnormally" do + parent = self() + assert capture_log(fn -> {:ok, pid} = Task.start(:erlang, :exit, [:abnormal]) ref = Process.monitor(pid) @@ -453,6 +471,7 @@ assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} assert_receive {:error, _pid, {Logger, ["Process " | _], _ts, process_metadata}} + assert [parent] == task_metadata[:callers] assert {:abnormal, [_ | _]} = task_metadata[:crash_reason] assert {:abnormal, [_ | _]} = process_metadata[:crash_reason] @@ -625,8 +644,9 @@ """ end - test "translates :proc_lib crashes on debug" do + test "translates :proc_lib+Task crashes on debug" do {:ok, pid} = Task.start_link(__MODULE__, :task, [self()]) + parent = self() assert capture_log(:debug, fn -> ref = Process.monitor(pid) @@ -652,13 +672,15 @@ assert process_metadata[:pid] == task_metadata[:pid] assert is_list(process_metadata[:callers]) assert is_list(process_metadata[:ancestors]) + assert [parent] == task_metadata[:callers] 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 + test "translates :proc_lib+Task crashes with neighbour on debug" do {:ok, pid} = Task.start_link(__MODULE__, :sub_task, [self()]) + parent = self() assert capture_log(:debug, fn -> ref = Process.monitor(pid) @@ -677,6 +699,9 @@ Current Stacktrace: (lib/logger/)?test/logger/translator_test.exs:\d+: Logger.TranslatorTest.sleep/1 """ + + assert_receive {:error, _pid, {Logger, ["Task " <> _ | _], _ts, task_metadata}} + assert [parent] == task_metadata[:callers] end test "translates Supervisor progress" do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/lib/mix/local.ex new/elixir-1.10.4/lib/mix/lib/mix/local.ex --- old/elixir-1.10.3/lib/mix/lib/mix/local.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/lib/mix/local.ex 2020-07-04 13:10:47.000000000 +0200 @@ -161,14 +161,29 @@ contents {:remote, message} -> - Mix.raise(""" - #{message} + Mix.raise( + """ + #{message} - Could not install #{name} because Mix could not download metadata at #{path}. - """) + Could not install #{name} because Mix could not download metadata at #{path}. + """ <> suggestions(name) + ) end end + defp suggestions("Hex") do + """ + + Alternatively, you can compile and install Hex directly with this command: + + mix archive.install github hexpm/hex branch latest + """ + end + + defp suggestions(_) do + "" + end + defp parse_csv(body) do body |> :binary.split("\n", [:global, :trim]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/lib/mix/release.ex new/elixir-1.10.4/lib/mix/lib/mix/release.ex --- old/elixir-1.10.3/lib/mix/lib/mix/release.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/lib/mix/release.ex 2020-07-04 13:10:47.000000000 +0200 @@ -402,7 +402,7 @@ args = [runtime_config?, sys_config] format = "%% coding: utf-8~n%% RUNTIME_CONFIG=~s~n~tw.~n" File.mkdir_p!(Path.dirname(path)) - File.write!(path, :io_lib.format(format, args), [:utf8]) + File.write!(path, IO.chardata_to_string(:io_lib.format(format, args))) case :file.consult(path) do {:ok, _} -> @@ -512,7 +512,7 @@ :ok | {:error, String.t()} def make_boot_script(release, path, modes, prepend_paths \\ []) do with {:ok, rel_spec} <- build_release_spec(release, modes) do - File.write!(path <> ".rel", consultable(rel_spec), [:utf8]) + File.write!(path <> ".rel", consultable(rel_spec)) sys_path = String.to_charlist(path) @@ -535,7 +535,7 @@ |> prepend_paths_to_script(prepend_paths) script = {:script, rel_info, instructions} - File.write!(script_path, consultable(script), [:utf8]) + File.write!(script_path, consultable(script)) :ok = :systools.script2boot(sys_path) {:error, module, info} -> @@ -657,7 +657,7 @@ end defp consultable(term) do - :io_lib.format("%% coding: utf-8~n~tp.~n", [term]) + IO.chardata_to_string(:io_lib.format("%% coding: utf-8~n~tp.~n", [term])) end @doc """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/lib/mix/shell.ex new/elixir-1.10.4/lib/mix/lib/mix/shell.ex --- old/elixir-1.10.3/lib/mix/lib/mix/shell.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/lib/mix/shell.ex 2020-07-04 13:10:47.000000000 +0200 @@ -76,8 +76,12 @@ ## Options + * `:cd` - (since v1.11.0) the directory to run the command in + * `:stderr_to_stdout` - redirects stderr to stdout, defaults to true + * `:env` - a list of environment variables, defaults to `[]` + * `:quiet` - overrides the callback to no-op """ @@ -98,7 +102,10 @@ [] end - opts = [:stream, :binary, :exit_status, :hide, :use_stdio, {:env, env} | args] + opts = + [:stream, :binary, :exit_status, :hide, :use_stdio, {:env, env}] ++ + args ++ Keyword.take(options, [:cd]) + port = Port.open({:spawn, shell_command(command)}, opts) port_read(port, callback) end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/lib/mix/tasks/app.start.ex new/elixir-1.10.4/lib/mix/lib/mix/tasks/app.start.ex --- old/elixir-1.10.3/lib/mix/lib/mix/tasks/app.start.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/lib/mix/tasks/app.start.ex 2020-07-04 13:10:47.000000000 +0200 @@ -162,8 +162,8 @@ {:error, {:file.format_error(:enoent), name}} path -> - case :file.consult(path) do - {:ok, [{:application, _, properties} = application_data]} -> + case consult_app_file_maybe_in_archive(path) do + {:ok, {:application, _, properties} = application_data} -> with :ok <- :application.load(application_data) do if compile_env = validate_compile_env? && properties[:compile_env] do # Unfortunately we can only check the current app here, @@ -182,6 +182,19 @@ end end + defp consult_app_file_maybe_in_archive(path) do + # The path could be located in .ez archive + case :erl_prim_loader.get_file(path) do + {:ok, bin, _full_name} -> + with {:ok, tokens, _} <- :erl_scan.string(String.to_charlist(bin)) do + :erl_parse.parse_term(tokens) + end + + :error -> + {:error, :enoent} + end + end + @doc false def type(config, opts) do cond do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/lib/mix/tasks/cmd.ex new/elixir-1.10.4/lib/mix/lib/mix/tasks/cmd.ex --- old/elixir-1.10.3/lib/mix/lib/mix/tasks/cmd.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/lib/mix/tasks/cmd.ex 2020-07-04 13:10:47.000000000 +0200 @@ -22,6 +22,13 @@ This task is automatically reenabled, so it can be called multiple times with different arguments. + ## Command line options + + * `--app` - limit running the command to the given app. This option + may be given multiple times + + * `--cd` - (since v1.11.0) the directory to run the command in + ## Zombie operating system processes Beware that the Erlang VM does not terminate child processes @@ -35,12 +42,20 @@ of the `Port` module documentation. """ + @switches [ + app: :keep, + cd: :string + ] + @impl true def run(args) do - {args, apps} = parse_apps(args, []) + {opts, args} = OptionParser.parse_head!(args, strict: @switches) + apps = Enum.map(List.wrap(opts[:app]), &String.to_atom/1) if apps == [] or Mix.Project.config()[:app] in apps do - case Mix.shell().cmd(Enum.join(args, " ")) do + cmd_opts = Keyword.take(opts, [:cd]) + + case Mix.shell().cmd(Enum.join(args, " "), cmd_opts) do 0 -> :ok status -> exit(status) end @@ -48,14 +63,4 @@ Mix.Task.reenable("cmd") end - - defp parse_apps(args, apps) do - case args do - ["--app", app | tail] -> - parse_apps(tail, [String.to_atom(app) | apps]) - - args -> - {args, apps} - end - end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/lib/mix/tasks/local.hex.ex new/elixir-1.10.4/lib/mix/lib/mix/tasks/local.hex.ex --- old/elixir-1.10.3/lib/mix/lib/mix/tasks/local.hex.ex 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/lib/mix/tasks/local.hex.ex 2020-07-04 13:10:47.000000000 +0200 @@ -11,6 +11,11 @@ mix local.hex + If installing a precompiled Hex does not work, you can compile and install + Hex directly with this command: + + mix archive.install github hexpm/hex branch latest + ## Command line options * `--force` - forces installation without a shell prompt; primarily diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/test/fixtures/release_test/config/config.exs new/elixir-1.10.4/lib/mix/test/fixtures/release_test/config/config.exs --- old/elixir-1.10.3/lib/mix/test/fixtures/release_test/config/config.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/test/fixtures/release_test/config/config.exs 2020-07-04 13:10:47.000000000 +0200 @@ -1,4 +1,4 @@ import Config config :release_test, :static, :was_set -config :release_test, :encoding, {:"£", "£", '£'} +config :release_test, :encoding, {:time_μs, :"£", "£", '£'} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/test/mix/release_test.exs new/elixir-1.10.4/lib/mix/test/mix/release_test.exs --- old/elixir-1.10.3/lib/mix/test/mix/release_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/test/mix/release_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -488,11 +488,15 @@ end test "writes sys_config with encoding" do - assert make_sys_config(release([]), [encoding: {:"£", "£", '£'}], "unused/runtime/path") == + assert make_sys_config( + release([]), + [encoding: {:time_μs, :"£", "£", '£'}], + "unused/runtime/path" + ) == :ok {:ok, contents} = :file.consult(@sys_config) - assert contents == [[encoding: {:"£", "£", '£'}]] + assert contents == [[encoding: {:time_μs, :"£", "£", '£'}]] end test "writes the given sys_config with config providers" do diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/test/mix/shell_test.exs new/elixir-1.10.4/lib/mix/test/mix/shell_test.exs --- old/elixir-1.10.3/lib/mix/test/mix/shell_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/test/mix/shell_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -17,4 +17,16 @@ after Mix.shell(Mix.Shell.Process) end + + test "with :cd" do + Mix.shell(Mix.Shell.IO) + tmp_dir = System.tmp_dir() + {pwd, 0} = System.cmd("pwd", [], cd: tmp_dir) + + assert ExUnit.CaptureIO.capture_io(fn -> + Mix.shell().cmd("pwd", cd: tmp_dir) + end) == pwd + after + Mix.shell(Mix.Shell.Process) + end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/test/mix/tasks/cmd_test.exs new/elixir-1.10.4/lib/mix/test/mix/tasks/cmd_test.exs --- old/elixir-1.10.3/lib/mix/test/mix/tasks/cmd_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/test/mix/tasks/cmd_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -36,4 +36,16 @@ 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"]} + end) + end) + end end diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/test/mix/tasks/compile_test.exs new/elixir-1.10.4/lib/mix/test/mix/tasks/compile_test.exs --- old/elixir-1.10.3/lib/mix/test/mix/tasks/compile_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/test/mix/tasks/compile_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -146,6 +146,39 @@ end) end + test "calling raise inside a macro returns a diagnostic with a position" do + in_fixture("no_mixfile", fn -> + File.write!("lib/a.ex", """ + defmodule A do + defmacro custom_macro do + raise "error" + end + end + """) + + File.write!("lib/b.ex", """ + defmodule B do + require A + A.custom_macro() + end + """) + + file = Path.absname("lib/b.ex") + + ExUnit.CaptureIO.capture_io(fn -> + assert {:error, [diagnostic]} = Mix.Task.run("compile", ["--return-errors"]) + + assert %Mix.Task.Compiler.Diagnostic{ + file: ^file, + severity: :error, + position: 3, + message: "** (RuntimeError) error\n expanding macro: A.custom_macro/0" <> _, + compiler_name: "Elixir" + } = diagnostic + end) + end) + end + test "returns syntax error from an Erlang file when --return-errors is set" do in_fixture("no_mixfile", fn -> import ExUnit.CaptureIO diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/elixir-1.10.3/lib/mix/test/mix/tasks/release_test.exs new/elixir-1.10.4/lib/mix/test/mix/tasks/release_test.exs --- old/elixir-1.10.3/lib/mix/test/mix/tasks/release_test.exs 2020-04-25 10:30:20.000000000 +0200 +++ new/elixir-1.10.4/lib/mix/test/mix/tasks/release_test.exs 2020-07-04 13:10:47.000000000 +0200 @@ -281,7 +281,7 @@ assert %{ app_dir: app_dir, cookie_env: ^cookie, - encoding: {:"£", "£", '£'}, + encoding: {:time_μs, :"£", "£", '£'}, mode: :embedded, node: release_node("release_test"), protocols_consolidated?: true, @@ -322,7 +322,7 @@ File.write!("config/releases.exs", """ import Config config :release_test, :runtime, :was_set - config :release_test, :encoding, {:runtime, :"£", "£", '£'} + config :release_test, :encoding, {:runtime, :time_μs, :"£", "£", '£'} """) root = Path.absname("_build/dev/rel/runtime_config") @@ -348,7 +348,7 @@ open_port(Path.join(root, "bin/runtime_config"), ['start']) assert %{ - encoding: {:runtime, :"£", "£", '£'}, + encoding: {:runtime, :time_μs, :"£", "£", '£'}, mode: :embedded, node: release_node("runtime_config"), protocols_consolidated?: true,
