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.14.1
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.14.2
Choose a head ref
  • 18 commits
  • 40 files changed
  • 2 contributors

Commits on Oct 11, 2022

  1. Copy the full SHA
    32c2a2d View commit details

Commits on Oct 14, 2022

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    fcb7b69 View commit details

Commits on Oct 16, 2022

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    eb43a6a View commit details

Commits on Oct 29, 2022

  1. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    29a2bb4 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    804d034 View commit details
  3. Fix backported test

    josevalim committed Oct 29, 2022
    Copy the full SHA
    6c5d7bf View commit details

Commits on Oct 31, 2022

  1. Improvements to ExUnit

    * Do not run duplicate cases on ExUnit.run/1
    * Allow test cases to not be registered on use
    * Expose ExUnit.Formatter.format_assertion_diff/4
    josevalim committed Oct 31, 2022
    Copy the full SHA
    e65dae9 View commit details

Commits on Nov 1, 2022

  1. Copy the full SHA
    c80f8b6 View commit details

Commits on Nov 3, 2022

  1. Copy the full SHA
    fdf96ad View commit details
  2. Copy the full SHA
    c01658b View commit details
  3. mix format

    josevalim committed Nov 3, 2022
    Copy the full SHA
    7996c0a View commit details

Commits on Nov 7, 2022

  1. Copy the full SHA
    2acd094 View commit details

Commits on Nov 8, 2022

  1. Copy the full SHA
    7f70f64 View commit details
  2. Copy the full SHA
    bdea27c View commit details

Commits on Nov 9, 2022

  1. Copy the full SHA
    1230345 View commit details
  2. Document register

    josevalim committed Nov 9, 2022
    Copy the full SHA
    d1f7457 View commit details
  3. Copy the full SHA
    d061cb9 View commit details

Commits on Nov 11, 2022

  1. Release v1.14.2

    josevalim committed Nov 11, 2022
    Copy the full SHA
    0909940 View commit details
Showing with 791 additions and 421 deletions.
  1. +34 −0 CHANGELOG.md
  2. +1 −1 VERSION
  3. 0 _build/dev/lib/git_app/.mix/compile.lock
  4. +1 −1 bin/elixir
  5. +1 −1 bin/elixir.bat
  6. +34 −19 lib/elixir/lib/code.ex
  7. +11 −8 lib/elixir/lib/code/formatter.ex
  8. +20 −4 lib/elixir/lib/kernel.ex
  9. +1 −1 lib/elixir/lib/kernel/utils.ex
  10. +1 −1 lib/elixir/lib/module.ex
  11. +2 −1 lib/elixir/lib/protocol.ex
  12. +33 −21 lib/elixir/src/elixir.erl
  13. +1 −1 lib/elixir/src/elixir_bootstrap.erl
  14. +4 −3 lib/elixir/src/elixir_compiler.erl
  15. +5 −7 lib/elixir/src/elixir_env.erl
  16. +24 −11 lib/elixir/src/elixir_erl_var.erl
  17. +12 −9 lib/elixir/src/elixir_expand.erl
  18. +27 −13 lib/elixir/src/elixir_module.erl
  19. +2 −0 lib/elixir/test/elixir/code_formatter/containers_test.exs
  20. +76 −0 lib/elixir/test/elixir/code_test.exs
  21. +98 −82 lib/elixir/test/elixir/kernel/expansion_test.exs
  22. +9 −0 lib/elixir/test/elixir/protocol_test.exs
  23. +2 −2 lib/elixir/test/erlang/atom_test.erl
  24. +2 −2 lib/elixir/test/erlang/function_test.erl
  25. +2 −2 lib/elixir/test/erlang/string_test.erl
  26. +3 −3 lib/ex_unit/lib/ex_unit.ex
  27. +6 −4 lib/ex_unit/lib/ex_unit/assertions.ex
  28. +29 −15 lib/ex_unit/lib/ex_unit/case.ex
  29. +23 −24 lib/ex_unit/lib/ex_unit/doc_test.ex
  30. +173 −155 lib/ex_unit/lib/ex_unit/formatter.ex
  31. +1 −1 lib/ex_unit/lib/ex_unit/runner.ex
  32. +16 −4 lib/ex_unit/lib/ex_unit/server.ex
  33. +8 −9 lib/ex_unit/test/ex_unit/doc_test_test.exs
  34. +29 −4 lib/ex_unit/test/ex_unit/formatter_test.exs
  35. +19 −8 lib/ex_unit/test/ex_unit_test.exs
  36. +1 −1 lib/iex/lib/iex/pry.ex
  37. +31 −2 lib/mix/lib/mix.ex
  38. +2 −0 lib/mix/lib/mix/compilers/test.ex
  39. +2 −1 lib/mix/test/mix/tasks/test_test.exs
  40. +45 −0 lib/mix/test/mix_test.exs
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -241,6 +241,40 @@ protocol, giving developers more control over the struct representation.
See the updated documentation for `Inspect` for a general rundown on
the approaches and options available.

## v1.14.2 (2022-11-11)

### 1. Enhancements

#### Elixir

* [Code] Add `Code.eval_quoted_with_env/4` with support for the `:prune_binding` option

#### ExUnit

* [ExUnit.Case] Allow test cases to not be registered on use
* [ExUnit.DocTest] Include `:doctest` and `:doctest_line` as meta tags
* [ExUnit.Formatter] Expose `ExUnit.Formatter.format_assertion_diff/4`

#### Mix

* [Mix] `Mix.install/2` accepts atoms as paths

### 2. Bug fixes

#### Elixir

* [Code.Formatter] Fix `size*unit` shortcut in bitstring
* [Kernel] Generate unique variables for macro expansion of `defguard`
* [Protocol] Expand `:for` in protocols with the appropriate env

#### ExUnit

* [ExUnit] Do not run duplicate cases on `ExUnit.run/1`

#### Mix

* [mix test] Ensure proper error message when there is no test directory

## v1.14.1 (2022-10-10)

### 1. Enhancements
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.14.1
1.14.2
Empty file.
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.14.1
ELIXIR_VERSION=1.14.2

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.14.1
set ELIXIR_VERSION=1.14.2

setlocal enabledelayedexpansion
if ""%1""=="""" if ""%2""=="""" goto documentation
53 changes: 34 additions & 19 deletions lib/elixir/lib/code.ex
Original file line number Diff line number Diff line change
@@ -100,10 +100,15 @@ defmodule Code do
following events are available to tracers:
* `:start` - (since v1.11.0) invoked whenever the compiler starts to trace
a new lexical context, such as a new file. Keep in mind the compiler runs
in parallel, so multiple files may invoke `:start` and run at the same
time. The value of the `lexical_tracker` of the macro environment, albeit
opaque, can be used to uniquely identify the environment.
a new lexical context. A lexical context is started when compiling a new
file or when defining a module within a function. Note evaluated code
does not start a new lexical context (because they don't track unused
aliases, imports, etc) but defining a module inside evaluated code will.
Note this event may be emitted in parallel, where multiple files/modules
invoke `:start` and run at the same time. The value of the `lexical_tracker`
of the macro environment, albeit opaque, can be used to uniquely identify
the environment.
* `:stop` - (since v1.11.0) invoked whenever the compiler stops tracing a
new lexical context, such as a new file.
@@ -181,7 +186,7 @@ defmodule Code do
"""

@typedoc """
A list with all variable bindings.
A list with all variables and their values.
The binding keys are usually atoms, but they may be a tuple for variables
defined in a different context.
@@ -344,7 +349,7 @@ defmodule Code do
@doc """
Evaluates the contents given by `string`.
The `binding` argument is a list of variable bindings.
The `binding` argument is a list of all variables and their values.
The `opts` argument is a keyword list of environment options.
**Warning**: `string` can be any Elixir code and will be executed with
@@ -362,17 +367,15 @@ defmodule Code do
* `:line` - the line on which the script starts
Additionally, you may also pass an environment as second argument,
so the evaluation happens within that environment. However, if the evaluated
code requires or compiles another file, the environment given to this function
will not apply to said files.
so the evaluation happens within that environment.
Returns a tuple of the form `{value, binding}`, where `value` is the value
returned from evaluating `string`. If an error occurs while evaluating
`string` an exception will be raised.
`string`, an exception will be raised.
`binding` is a list with all variable bindings after evaluating `string`.
The binding keys are usually atoms, but they may be a tuple for variables
defined in a different context.
`binding` is a list with all variable names and their values after evaluating
`string`. The binding keys are usually atoms, but they may be a tuple for variables
defined in a different context. The names are in no particular order.
## Examples
@@ -419,13 +422,13 @@ defmodule Code do
defp validated_eval_string(string, binding, opts_or_env) do
%{line: line, file: file} = env = env_for_eval(opts_or_env)
forms = :elixir.string_to_quoted!(to_charlist(string), line, 1, file, [])
{value, binding, _env} = eval_verify(:eval_forms, forms, binding, env)
{value, binding, _env} = eval_verify(:eval_forms, [forms, binding, env])
{value, binding}
end

defp eval_verify(fun, forms, binding, env) do
defp eval_verify(fun, args) do
Module.ParallelChecker.verify(fn ->
apply(:elixir, fun, [forms, binding, env])
apply(:elixir, fun, args)
end)
end

@@ -822,7 +825,9 @@ defmodule Code do
"""
@spec eval_quoted(Macro.t(), binding, Macro.Env.t() | keyword) :: {term, binding}
def eval_quoted(quoted, binding \\ [], env_or_opts \\ []) do
{value, binding, _env} = eval_verify(:eval_quoted, quoted, binding, env_for_eval(env_or_opts))
{value, binding, _env} =
eval_verify(:eval_quoted, [quoted, binding, env_for_eval(env_or_opts)])

{value, binding}
end

@@ -859,10 +864,20 @@ defmodule Code do
Therefore, the first time you call this function, you must compute
the initial environment with `env_for_eval/1`. The remaining calls
must pass the environment that was returned by this function.
## Options
* `:prune_binding` - (since v1.14.2) prune binding to keep only
variables read or written by the evaluated code. Note that
variables used by modules are always pruned, even if later used
by the modules. You can submit to the `:on_module` tracer event
and access the variables used by the module from its environment.
"""
@doc since: "1.14.0"
def eval_quoted_with_env(quoted, binding, %Macro.Env{} = env) when is_list(binding) do
eval_verify(:eval_forms, quoted, binding, env)
def eval_quoted_with_env(quoted, binding, %Macro.Env{} = env, opts \\ [])
when is_list(binding) do
eval_verify(:eval_quoted, [quoted, binding, env, opts])
end

@doc ~S"""
19 changes: 11 additions & 8 deletions lib/elixir/lib/code/formatter.ex
Original file line number Diff line number Diff line change
@@ -1411,7 +1411,7 @@ defmodule Code.Formatter do

defp bitstring_segment_to_algebra({{:"::", _, [segment, spec]}, i}, state, last) do
{doc, state} = quoted_to_algebra(segment, :parens_arg, state)
{spec, state} = bitstring_spec_to_algebra(spec, state)
{spec, state} = bitstring_spec_to_algebra(spec, state, state.normalize_bitstring_modifiers)

spec = wrap_in_parens_if_inspected_atom(spec)
spec = if i == last, do: bitstring_wrap_parens(spec, i, last), else: spec
@@ -1430,26 +1430,29 @@ defmodule Code.Formatter do
{bitstring_wrap_parens(doc, i, last), state}
end

defp bitstring_spec_to_algebra({op, _, [left, right]}, state) when op in [:-, :*] do
{left, state} = bitstring_spec_to_algebra(left, state)
{right, state} = bitstring_spec_element_to_algebra(right, state)
defp bitstring_spec_to_algebra({op, _, [left, right]}, state, normalize_modifiers)
when op in [:-, :*] do
normalize_modifiers = normalize_modifiers && op != :*
{left, state} = bitstring_spec_to_algebra(left, state, normalize_modifiers)
{right, state} = bitstring_spec_element_to_algebra(right, state, normalize_modifiers)
{concat(concat(left, Atom.to_string(op)), right), state}
end

defp bitstring_spec_to_algebra(spec, state) do
bitstring_spec_element_to_algebra(spec, state)
defp bitstring_spec_to_algebra(spec, state, normalize_modifiers) do
bitstring_spec_element_to_algebra(spec, state, normalize_modifiers)
end

defp bitstring_spec_element_to_algebra(
{atom, meta, empty_args},
state = %{normalize_bitstring_modifiers: true}
state,
_normalize_modifiers = true
)
when is_atom(atom) and empty_args in [nil, []] do
empty_args = bitstring_spec_normalize_empty_args(atom)
quoted_to_algebra_with_parens_if_operator({atom, meta, empty_args}, :parens_arg, state)
end

defp bitstring_spec_element_to_algebra(spec_element, state) do
defp bitstring_spec_element_to_algebra(spec_element, state, _normalize_modifiers) do
quoted_to_algebra_with_parens_if_operator(spec_element, :parens_arg, state)
end

24 changes: 20 additions & 4 deletions lib/elixir/lib/kernel.ex
Original file line number Diff line number Diff line change
@@ -4800,11 +4800,27 @@ defmodule Kernel do
:elixir_quote.escape(block, :none, false)
end

module_vars = :lists.map(&module_var/1, :maps.keys(env.versioned_vars))
versioned_vars = env.versioned_vars
prune = :erlang.is_map_key({:elixir, :prune_binding}, versioned_vars)

var_meta =
case prune do
true -> [generated: true, keep_unused: true]
false -> [generated: false]
end

module_vars = :lists.map(&module_var(&1, var_meta), :maps.keys(versioned_vars))

quote do
unquote(with_alias)
:elixir_module.compile(unquote(expanded), unquote(escaped), unquote(module_vars), __ENV__)

:elixir_module.compile(
unquote(expanded),
unquote(escaped),
unquote(module_vars),
unquote(prune),
__ENV__
)
end
end

@@ -4853,8 +4869,8 @@ defmodule Kernel do
{module, module, nil}
end

defp module_var({name, kind}) when is_atom(kind), do: {name, [generated: true], kind}
defp module_var({name, kind}), do: {name, [counter: kind, generated: true], nil}
defp module_var({name, kind}, meta) when is_atom(kind), do: {name, meta, kind}
defp module_var({name, kind}, meta), do: {name, [counter: kind] ++ meta, nil}

@doc ~S"""
Defines a public function with the given name and body.
2 changes: 1 addition & 1 deletion lib/elixir/lib/kernel/utils.ex
Original file line number Diff line number Diff line change
@@ -375,7 +375,7 @@ defmodule Kernel.Utils do

%{} ->
generated = String.to_atom("arg" <> Integer.to_string(map_size(acc) + 1))
new_var = Macro.var(generated, Elixir)
new_var = Macro.unique_var(generated, Elixir)
{new_var, Map.put(acc, pair, {new_var, var})}
end

2 changes: 1 addition & 1 deletion lib/elixir/lib/module.ex
Original file line number Diff line number Diff line change
@@ -822,7 +822,7 @@ defmodule Module do
next = :elixir_module.next_counter(nil)
meta = Keyword.take(opts, [:line, :generated])
quoted = :elixir_quote.linify_with_context_counter(meta, {module, next}, quoted)
:elixir_module.compile(module, quoted, [], :elixir.env_for_eval(opts))
:elixir_module.compile(module, quoted, [], false, :elixir.env_for_eval(opts))
end

@doc """
3 changes: 2 additions & 1 deletion lib/elixir/lib/protocol.ex
Original file line number Diff line number Diff line change
@@ -931,7 +931,8 @@ defmodule Protocol do
raise ArgumentError, "defimpl/3 expects a :for option when declared outside a module"
end)

for = Macro.expand_literals(for, %{env | module: Kernel, function: {:defimpl, 3}})
for =
Macro.expand_literals(for, %{env | module: env.module || Elixir, function: {:__impl__, 1}})

case opts do
[] -> raise ArgumentError, "defimpl expects a do-end block"
54 changes: 33 additions & 21 deletions lib/elixir/src/elixir.erl
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@
-behaviour(application).
-export([start_cli/0,
string_to_tokens/5, tokens_to_quoted/3, 'string_to_quoted!'/5,
env_for_eval/1, quoted_to_erl/2, eval_forms/3, eval_quoted/3]).
env_for_eval/1, quoted_to_erl/2, eval_forms/3, eval_quoted/3,
eval_quoted/4]).
-include("elixir.hrl").
-define(system, 'Elixir.System').

@@ -260,22 +261,27 @@ env_for_eval(Opts) when is_list(Opts) ->

%% Quoted evaluation

eval_quoted(Tree, Binding, Opts) when is_list(Opts) ->
eval_quoted(Tree, Binding, env_for_eval(Opts));
eval_quoted(Tree, Binding, #{line := Line} = E) ->
eval_forms(elixir_quote:linify(Line, line, Tree), Binding, E).
eval_quoted(Tree, Binding, E) ->
eval_quoted(Tree, Binding, E, []).
eval_quoted(Tree, Binding, #{line := Line} = E, Opts) ->
eval_forms(elixir_quote:linify(Line, line, Tree), Binding, E, Opts).

eval_forms(Tree, Binding, Opts) when is_list(Opts) ->
eval_forms(Tree, Binding, env_for_eval(Opts));
eval_forms(Tree, Binding, OrigE) ->
{ExVars, ErlVars, ErlBinding} = elixir_erl_var:load_binding(Binding),
eval_forms(Tree, Binding, OrigE, []).
eval_forms(Tree, Binding, OrigE, Opts) ->
Prune = proplists:get_value(prune_binding, Opts, false),
{ExVars, ErlVars, ErlBinding} = elixir_erl_var:load_binding(Binding, Prune),
E = elixir_env:with_vars(OrigE, ExVars),
S = elixir_erl_var:from_env(E, ErlVars),
{Erl, NewErlS, NewExS, NewE} = quoted_to_erl(Tree, E, S),
ExS = elixir_env:env_to_ex(E),
ErlS = elixir_erl_var:from_env(E, ErlVars),
{Erl, NewErlS, NewExS, NewE} = quoted_to_erl(Tree, ErlS, ExS, E),

case Erl of
{atom, _, Atom} ->
{Atom, Binding, NewE};
{Literal, _, Value} when Literal == atom; Literal == float; Literal == integer ->
if
Prune -> {Value, [], NewE#{versioned_vars := #{}}};
true -> {Value, Binding, NewE}
end;

_ ->
Exprs =
@@ -286,7 +292,12 @@ eval_forms(Tree, Binding, OrigE) ->

ExternalHandler = eval_external_handler(NewE),
{value, Value, NewBinding} = erl_eval:exprs(Exprs, ErlBinding, none, ExternalHandler),
{Value, elixir_erl_var:dump_binding(NewBinding, NewExS, NewErlS), NewE}
PruneBefore = if Prune -> length(Binding); true -> -1 end,

{DumpedBinding, DumpedVars} =
elixir_erl_var:dump_binding(NewBinding, NewErlS, NewExS, PruneBefore),

{Value, DumpedBinding, NewE#{versioned_vars := DumpedVars}}
end.

%% TODO: Remove conditional once we require Erlang/OTP 25+.
@@ -356,14 +367,15 @@ eval_external_handler(_Env) ->
%% Converts a quoted expression to Erlang abstract format

quoted_to_erl(Quoted, E) ->
{_, S} = elixir_erl_var:from_env(E),
quoted_to_erl(Quoted, E, S).

quoted_to_erl(Quoted, Env, Scope) ->
{Expanded, #elixir_ex{vars={ReadVars, _}} = NewExS, NewEnv} =
elixir_expand:expand(Quoted, elixir_env:env_to_ex(Env), Env),
{Erl, NewErlS} = elixir_erl_pass:translate(Expanded, erl_anno:new(?key(Env, line)), Scope),
{Erl, NewErlS, NewExS, NewEnv#{versioned_vars := ReadVars}}.
{_, ErlS} = elixir_erl_var:from_env(E),
ExS = elixir_env:env_to_ex(E),
quoted_to_erl(Quoted, ErlS, ExS, E).

quoted_to_erl(Quoted, ErlS, ExS, Env) ->
{Expanded, NewExS, NewEnv} =
elixir_expand:expand(Quoted, ExS, Env),
{Erl, NewErlS} = elixir_erl_pass:translate(Expanded, erl_anno:new(?key(Env, line)), ErlS),
{Erl, NewErlS, NewExS, NewEnv}.

%% Converts a given string (charlist) into quote expression

2 changes: 1 addition & 1 deletion lib/elixir/src/elixir_bootstrap.erl
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@

'MACRO-defmodule'(_Caller, Alias, [{do, Block}]) ->
Escaped = elixir_quote:escape(Block, none, false),
Args = [Alias, Escaped, [], env()],
Args = [Alias, Escaped, [], false, env()],
{{'.', [], [elixir_module, compile]}, [], Args}.

'__info__'(functions) ->
Loading