Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: elixir-lang/elixir
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.15.4
Choose a base ref
...
head repository: elixir-lang/elixir
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.15.5
Choose a head ref
  • 13 commits
  • 17 files changed
  • 3 contributors

Commits on Jul 18, 2023

  1. Fix type in changelog

    josevalim committed Jul 18, 2023
    Copy the full SHA
    f40aa10 View commit details
  2. Do not assume blake is always available

    Closes #12809.
    josevalim committed Jul 18, 2023
    Copy the full SHA
    9b7b7d6 View commit details

Commits on Jul 19, 2023

  1. Copy the full SHA
    92eee10 View commit details

Commits on Jul 28, 2023

  1. Also handle charlist returns from stdio

    Closes #12687.
    josevalim committed Jul 28, 2023
    Copy the full SHA
    14cddf3 View commit details

Commits on Jul 31, 2023

  1. Copy the full SHA
    5d79b34 View commit details

Commits on Aug 2, 2023

  1. Copy the full SHA
    bdaffc3 View commit details

Commits on Aug 3, 2023

  1. Copy the full SHA
    36c3f75 View commit details

Commits on Aug 23, 2023

  1. Always pass stracktrace when necessary

    sabiwara authored and josevalim committed Aug 23, 2023
    Copy the full SHA
    84c8d23 View commit details
  2. Copy the full SHA
    dc8cfcd View commit details
  3. Copy the full SHA
    def65ab View commit details

Commits on Aug 24, 2023

  1. Copy the full SHA
    fb03792 View commit details

Commits on Aug 27, 2023

  1. Copy the full SHA
    78cee0e View commit details

Commits on Aug 28, 2023

  1. Release v1.15.5

    josevalim committed Aug 28, 2023
    Copy the full SHA
    9fd97c4 View commit details
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -117,6 +117,35 @@ in the long term.
See the new `Logger` documentation for more information on the
new features and on compatibility.

## v1.15.5 (2023-08-28)

### 1. Enhancements

#### IEx

* [IEx.Autocomplete] Speed up loading of struct suggestions

### 2. Bug fixes

#### Elixir

* [Code.Fragment] Fix `Code.Fragment.surround_context/2` for aliases and submodules of non-aliases
* [Kernel] Ensure stacktrace is included when necessary when rescuing multiple exceptions in the same branch
* [Kernel] Fix index in error message for unused optional arguments

#### ExUnit

* [ExUnit.Diff] Fix scenario where diff would not show up due to a timed-out loop

#### IEx

* [IEx] Force group leader to run as a binary and unicode in IEx

#### Mix

* [mix compile] Do not assume `blake` is always available
* [mix format] Load and compile plugins if specified in subdirectories

## v1.15.4 (2023-07-18)

### 1. Bug fixes
@@ -133,7 +162,7 @@ new features and on compatibility.

#### Elixir

* [Kernel] Improve better stacktraces when executing unnested Elixir code in a file
* [Kernel] Improve stacktraces when executing unnested Elixir code in a file

#### Mix

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.15.4
1.15.5
2 changes: 1 addition & 1 deletion bin/elixir
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/sh
set -e

ELIXIR_VERSION=1.15.4
ELIXIR_VERSION=1.15.5

if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
cat <<USAGE >&2
2 changes: 1 addition & 1 deletion bin/elixir.bat
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@if defined ELIXIR_CLI_ECHO (@echo on) else (@echo off)

set ELIXIR_VERSION=1.15.4
set ELIXIR_VERSION=1.15.5

setlocal enabledelayedexpansion
if ""%1""=="""" if ""%2""=="""" goto documentation
5 changes: 4 additions & 1 deletion lib/elixir/lib/code/fragment.ex
Original file line number Diff line number Diff line change
@@ -677,6 +677,9 @@ defmodule Code.Fragment do
{{:alias, acc}, offset} ->
build_surround({:alias, acc}, reversed, line, offset)

{{:alias, parent, acc}, offset} ->
build_surround({:alias, parent, acc}, reversed, line, offset)

{{:struct, acc}, offset} ->
build_surround({:struct, acc}, reversed, line, offset)

@@ -753,7 +756,7 @@ defmodule Code.Fragment do
end
end

defp take_alias([h | t], acc) when h in ?A..?Z or h in ?a..?z or h in ?0..9 or h == ?_,
defp take_alias([h | t], acc) when h in ?A..?Z or h in ?a..?z or h in ?0..?9 or h == ?_,
do: take_alias(t, [h | acc])

defp take_alias(rest, acc) do
2 changes: 1 addition & 1 deletion lib/elixir/src/elixir_erl_try.erl
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@ normalize_rescue(Meta, Var, Pattern, Expr, ErlangAliases) ->
dynamic_normalize(Meta, Var, ?REQUIRES_STACKTRACE);

false ->
case lists:splitwith(fun is_normalized_with_stacktrace/1, ErlangAliases) of
case lists:partition(fun is_normalized_with_stacktrace/1, ErlangAliases) of
{[], _} -> [];
{_, []} -> {'__STACKTRACE__', Meta, nil};
{Some, _} -> dynamic_normalize(Meta, Var, Some)
6 changes: 3 additions & 3 deletions lib/elixir/src/elixir_locals.erl
Original file line number Diff line number Diff line change
@@ -118,11 +118,11 @@ format_error({function_conflict, {Receiver, {Name, Arity}}}) ->
format_error({unused_args, {Name, Arity}}) ->
io_lib:format("default values for the optional arguments in ~ts/~B are never used", [Name, Arity]);

format_error({unused_args, {Name, Arity}, 1}) ->
io_lib:format("the default value for the first optional argument in ~ts/~B is never used", [Name, Arity]);
format_error({unused_args, {Name, Arity}, Count}) when Arity - Count == 1 ->
io_lib:format("the default value for the last optional argument in ~ts/~B is never used", [Name, Arity]);

format_error({unused_args, {Name, Arity}, Count}) ->
io_lib:format("the default values for the first ~B optional arguments in ~ts/~B are never used", [Count, Name, Arity]);
io_lib:format("the default values for the last ~B optional arguments in ~ts/~B are never used", [Arity - Count, Name, Arity]);

format_error({unused_def, {Name, Arity}, defp}) ->
io_lib:format("function ~ts/~B is unused", [Name, Arity]);
56 changes: 36 additions & 20 deletions lib/elixir/test/elixir/code_fragment_test.exs
Original file line number Diff line number Diff line change
@@ -738,6 +738,14 @@ defmodule CodeFragmentTest do
end: {3, 5}
}
end

for i <- 1..11 do
assert CF.surround_context("Foo.Bar.Baz.foo(bar)", {1, i}) == %{
context: {:alias, ~c"Foo.Bar.Baz"},
begin: {1, 1},
end: {1, 12}
}
end
end

test "underscored special forms" do
@@ -747,17 +755,21 @@ defmodule CodeFragmentTest do
end: {1, 11}
}

assert CF.surround_context("__MODULE__.Foo", {1, 12}) == %{
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo"},
begin: {1, 1},
end: {1, 15}
}
for i <- 1..15 do
assert CF.surround_context("__MODULE__.Foo", {1, i}) == %{
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo"},
begin: {1, 1},
end: {1, 15}
}
end

assert CF.surround_context("__MODULE__.Foo.Sub", {1, 16}) == %{
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo.Sub"},
begin: {1, 1},
end: {1, 19}
}
for i <- 1..19 do
assert CF.surround_context("__MODULE__.Foo.Sub", {1, i}) == %{
context: {:alias, {:local_or_var, ~c"__MODULE__"}, ~c"Foo.Sub"},
begin: {1, 1},
end: {1, 19}
}
end

assert CF.surround_context("%__MODULE__{}", {1, 5}) == %{
context: {:struct, {:local_or_var, ~c"__MODULE__"}},
@@ -803,17 +815,21 @@ defmodule CodeFragmentTest do
end

test "attribute submodules" do
assert CF.surround_context("@some.Foo", {1, 8}) == %{
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo"},
begin: {1, 1},
end: {1, 10}
}
for i <- 1..10 do
assert CF.surround_context("@some.Foo", {1, i}) == %{
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo"},
begin: {1, 1},
end: {1, 10}
}
end

assert CF.surround_context("@some.Foo.Sub", {1, 12}) == %{
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo.Sub"},
begin: {1, 1},
end: {1, 14}
}
for i <- 1..14 do
assert CF.surround_context("@some.Foo.Sub", {1, i}) == %{
context: {:alias, {:module_attribute, ~c"some"}, ~c"Foo.Sub"},
begin: {1, 1},
end: {1, 14}
}
end

assert CF.surround_context("%@some{}", {1, 5}) == %{
context: {:struct, {:module_attribute, ~c"some"}},
11 changes: 11 additions & 0 deletions lib/elixir/test/elixir/kernel/raise_test.exs
Original file line number Diff line number Diff line change
@@ -224,6 +224,17 @@ defmodule Kernel.RaiseTest do
assert result == "an exception"
end

test "named function clause (stacktrace) or runtime (no stacktrace) error" do
result =
try do
Access.get("foo", 0)
rescue
x in [FunctionClauseError, CaseClauseError] -> Exception.message(x)
end

assert result == "no function clause matching in Access.get/3"
end

test "with higher precedence than catch" do
result =
try do
23 changes: 6 additions & 17 deletions lib/elixir/test/elixir/kernel/warning_test.exs
Original file line number Diff line number Diff line change
@@ -563,17 +563,17 @@ defmodule Kernel.WarningTest do
end
""")
end) =~
"the default values for the first 2 optional arguments in b/3 are never used\n nofile:3"
"the default value for the last optional argument in b/3 is never used\n nofile:3"

assert capture_err(fn ->
Code.eval_string(~S"""
defmodule Sample3 do
def a, do: b(1)
defp b(arg1 \\ 1, arg2 \\ 2, arg3 \\ 3), do: [arg1, arg2, arg3]
def a, do: b(1, 2)
defp b(arg1, arg2 \\ 2, arg3 \\ 3, arg4 \\ 4), do: [arg1, arg2, arg3, arg4]
end
""")
end) =~
"the default value for the first optional argument in b/3 is never used\n nofile:3"
"the default values for the last 2 optional arguments in b/4 are never used"

assert capture_err(fn ->
Code.eval_string(~S"""
@@ -587,27 +587,16 @@ defmodule Kernel.WarningTest do
assert capture_err(fn ->
Code.eval_string(~S"""
defmodule Sample5 do
def a, do: b(1, 2, 3)
defp b(arg1 \\ 1, arg2 \\ 2, arg3 \\ 3)
defp b(arg1, arg2, arg3), do: [arg1, arg2, arg3]
end
""")
end) =~ "default values for the optional arguments in b/3 are never used\n nofile:3"

assert capture_err(fn ->
Code.eval_string(~S"""
defmodule Sample6 do
def a, do: b(1, 2)
defp b(arg1 \\ 1, arg2 \\ 2, arg3 \\ 3)
defp b(arg1, arg2, arg3), do: [arg1, arg2, arg3]
end
""")
end) =~
"the default values for the first 2 optional arguments in b/3 are never used\n nofile:3"
"the default value for the last optional argument in b/3 is never used\n nofile:3"
after
purge([Sample1, Sample2, Sample3, Sample4, Sample5, Sample6])
purge([Sample1, Sample2, Sample3, Sample4, Sample5])
end

test "unused import" do
6 changes: 5 additions & 1 deletion lib/ex_unit/lib/ex_unit/diff.ex
Original file line number Diff line number Diff line change
@@ -200,11 +200,15 @@ defmodule ExUnit.Diff do

{diff, env}
else
diff_value(left, right, env)
non_recursive_diff_value(left, right, env)
end
end

defp diff_value(left, right, env) do
non_recursive_diff_value(left, right, env)
end

defp non_recursive_diff_value(left, right, env) do
diff_left = escape(left) |> update_diff_meta(true)
diff_right = escape(right) |> update_diff_meta(true)
diff = %__MODULE__{equivalent?: false, left: diff_left, right: diff_right}
10 changes: 8 additions & 2 deletions lib/ex_unit/test/ex_unit/diff_test.exs
Original file line number Diff line number Diff line change
@@ -1121,6 +1121,8 @@ defmodule ExUnit.DiffTest do
)
end

@compile {:no_warn_undefined, String}

test "functions" do
identity = & &1
inspect = inspect(identity)
@@ -1130,11 +1132,15 @@ defmodule ExUnit.DiffTest do

refute_diff(identity == :a, "-#{inspect}-", "+:a+")
refute_diff({identity, identity} == :a, "-{#{inspect}, #{inspect}}", "+:a+")

refute_diff({identity, :a} == {:a, identity}, "{-#{inspect}-, -:a-}", "{+:a+, +#{inspect}+}")

refute_diff(%{identity => identity} == :a, "-%{#{inspect} => #{inspect}}", "+:a+")

refute_diff(
(&String.to_charlist/1) == (&String.unknown/1),
"-&String.to_charlist/1-",
"+&String.unknown/1"
)

refute_diff(
%Opaque{data: identity} == :a,
"-#Opaque<???>-",
1 change: 1 addition & 0 deletions lib/iex/lib/iex.ex
Original file line number Diff line number Diff line change
@@ -871,6 +871,7 @@ defmodule IEx do
if Code.ensure_loaded?(:prim_tty) do
spawn(fn ->
{:ok, _} = Application.ensure_all_started(:iex)
:ok = :io.setopts(binary: true, encoding: :unicode)
_ = for fun <- Enum.reverse(after_spawn()), do: fun.()
IEx.Server.run([register: false] ++ opts)
end)
24 changes: 14 additions & 10 deletions lib/iex/lib/iex/autocomplete.ex
Original file line number Diff line number Diff line change
@@ -146,7 +146,7 @@ defmodule IEx.Autocomplete do

@doc false
def exports(mod) do
if Code.ensure_loaded?(mod) and function_exported?(mod, :__info__, 1) do
if ensure_loaded?(mod) and function_exported?(mod, :__info__, 1) do
mod.__info__(:macros) ++ (mod.__info__(:functions) -- [__info__: 1])
else
mod.module_info(:exports) -- [module_info: 0, module_info: 1]
@@ -313,21 +313,23 @@ defmodule IEx.Autocomplete do
for {alias, mod} <- aliases_from_env(shell),
[name] = Module.split(alias),
String.starts_with?(name, hint),
struct?(mod) and not function_exported?(mod, :exception, 1),
do: %{kind: :struct, name: name}
do: {mod, name}

modules =
for "Elixir." <> name = full_name <- match_modules("Elixir." <> hint, true),
String.starts_with?(name, hint),
mod = String.to_atom(full_name),
struct?(mod) and not function_exported?(mod, :exception, 1),
do: %{kind: :struct, name: name}
do: {mod, name}

format_expansion(aliases ++ modules, hint)
end
all = aliases ++ modules
Code.ensure_all_loaded(Enum.map(all, &elem(&1, 0)))

defp struct?(mod) do
Code.ensure_loaded?(mod) and function_exported?(mod, :__struct__, 1)
refs =
for {mod, name} <- all,
function_exported?(mod, :__struct__, 1) and not function_exported?(mod, :exception, 1),
do: %{kind: :struct, name: name}

format_expansion(refs, hint)
end

defp expand_container_context(code, context, hint, shell) do
@@ -420,7 +422,9 @@ defmodule IEx.Autocomplete do
defp container_context_struct(cursor, pairs, aliases, shell) do
with {pairs, [^cursor]} <- Enum.split(pairs, -1),
alias = value_from_alias(aliases, shell),
true <- Keyword.keyword?(pairs) and struct?(alias) do
true <-
Keyword.keyword?(pairs) and ensure_loaded?(alias) and
function_exported?(alias, :__struct__, 1) do
{:struct, alias, pairs}
else
_ -> nil
10 changes: 7 additions & 3 deletions lib/iex/lib/iex/broker.ex
Original file line number Diff line number Diff line change
@@ -85,9 +85,13 @@ defmodule IEx.Broker do
yes?(IO.gets(:stdio, interrupt))
end

defp yes?(string) do
is_binary(string) and String.trim(string) in ["", "y", "Y", "yes", "YES", "Yes"]
end
defp yes?(string) when is_binary(string),
do: String.trim(string) in ["", "y", "Y", "yes", "YES", "Yes"]

defp yes?(charlist) when is_list(charlist),
do: yes?(List.to_string(charlist))

defp yes?(_), do: false

@doc """
Client requests a takeover.
3 changes: 3 additions & 0 deletions lib/mix/lib/mix/compilers/elixir.ex
Original file line number Diff line number Diff line change
@@ -454,6 +454,9 @@ defmodule Mix.Compilers.Elixir do
8 -> :crypto.hash(:blake2b, contents)
_ -> :crypto.hash(:blake2s, contents)
end
rescue
# Blake may not be available on all OpenSSL distribution
_ -> :erlang.md5(contents)
end

defp set_compiler_opts(opts) do
Loading