Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bbf5e2bc7b | ||
|
|
ceafd0cdab | ||
|
|
87710cd495 | ||
|
|
6e2c29c9d4 | ||
|
|
f7b32487c0 | ||
|
|
eedc9bc757 | ||
|
|
f053df848f |
+19
-1
@@ -23,7 +23,7 @@ def format(message, opts) do
|
||||
end
|
||||
```
|
||||
|
||||
The `if` block above is implicitly changing the value in `message`. Now imagine we want to move the `if` block to its own function to clean up the implementation:
|
||||
The `if` block above is implicitly rebinding the outer scope variable `message` to a new value. Now imagine we want to move the `if` block to its own function to clean up the implementation:
|
||||
|
||||
```elixir
|
||||
def format(message, opts) do
|
||||
@@ -268,6 +268,20 @@ end
|
||||
|
||||
By restricting hierarchies in favor of named setups, it is straight-forward for the developer to glance at each describe block and know exactly the setup steps involved.
|
||||
|
||||
## v1.3.4 (2016-10-09)
|
||||
|
||||
### 1. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Kernel] Ensure the compiler does not generate unecessary variable bindings inside case statements. This improves the code emitted and make sure "unused variable warnings" are not mistakenly silenced
|
||||
* [Kernel] Move `raise` checks to runtime to avoid crashing cover on Erlang 19.1
|
||||
* [Protocol] Do not emit warnings when using protocols on opaque types
|
||||
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit.CaptureLog] Flush Erlang's `:error_logger` before capturing to avoid mixed messages
|
||||
|
||||
## v1.3.3 (2016-09-17)
|
||||
|
||||
### 1. Enhancements
|
||||
@@ -503,3 +517,7 @@ This release deprecates many APIs that have been soft-deprecated in previous Eli
|
||||
* [String] `String.valid_character?/1` is deprecated in favor of `String.valid?/1` with pattern matching
|
||||
* [Task] `Task.find/2` is deprecated in favor of explicit message matching
|
||||
* [URI] Passing a non-map to `URI.decode_query/2` is deprecated
|
||||
|
||||
## v1.2
|
||||
|
||||
The CHANGELOG for v1.2 releases can be found [in the v1.2 branch](https://github.com/elixir-lang/elixir/blob/v1.2/CHANGELOG.md).
|
||||
|
||||
@@ -1350,31 +1350,9 @@ defmodule Kernel do
|
||||
:erlang.error unquote(alias).exception([])
|
||||
end
|
||||
_ ->
|
||||
generated = fn fun, var ->
|
||||
{fun, [generated: true, line: -1], [{var, [], __MODULE__}]}
|
||||
quote do
|
||||
:erlang.error Kernel.Utils.raise(unquote(msg))
|
||||
end
|
||||
|
||||
{fun, meta, [arg, [do: clauses]]} =
|
||||
quote do
|
||||
case unquote(msg) do
|
||||
msg when unquote(generated.(:is_binary, :msg)) ->
|
||||
:erlang.error RuntimeError.exception(msg)
|
||||
atom when unquote(generated.(:is_atom, :atom)) ->
|
||||
:erlang.error atom.exception([])
|
||||
%{__struct__: struct, __exception__: true} = other when is_atom(struct) ->
|
||||
:erlang.error other
|
||||
other ->
|
||||
message = "raise/1 expects an alias, string or exception as the first argument, got: #{inspect other}"
|
||||
:erlang.error ArgumentError.exception(message)
|
||||
end
|
||||
end
|
||||
|
||||
clauses =
|
||||
:lists.map(fn {:->, meta, args} ->
|
||||
{:->, [generated: true] ++ Keyword.put(meta, :line, -1), args}
|
||||
end, clauses)
|
||||
|
||||
{fun, meta, [arg, [do: clauses]]}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,6 +3,9 @@ import Kernel, except: [destructure: 2, defdelegate: 2, defstruct: 2]
|
||||
defmodule Kernel.Utils do
|
||||
@moduledoc false
|
||||
|
||||
@doc """
|
||||
Callback for destructure.
|
||||
"""
|
||||
def destructure(list, count) when is_list(list), do: destructure_list(list, count)
|
||||
def destructure(nil, count), do: destructure_nil(count)
|
||||
|
||||
@@ -13,6 +16,9 @@ defmodule Kernel.Utils do
|
||||
defp destructure_nil(0), do: []
|
||||
defp destructure_nil(count), do: [nil | destructure_nil(count - 1)]
|
||||
|
||||
@doc """
|
||||
Callback for defdelegate.
|
||||
"""
|
||||
def defdelegate(fun, opts) do
|
||||
append_first = Keyword.get(opts, :append_first, false)
|
||||
|
||||
@@ -56,6 +62,9 @@ defmodule Kernel.Utils do
|
||||
"defdelegate/2 only accepts function parameters, got: #{Macro.to_string(code)}"
|
||||
end
|
||||
|
||||
@doc """
|
||||
Callback for defstruct.
|
||||
"""
|
||||
def defstruct(module, fields) do
|
||||
case fields do
|
||||
fs when is_list(fs) ->
|
||||
@@ -85,10 +94,30 @@ defmodule Kernel.Utils do
|
||||
Module.get_attribute(module, :derive)}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Announcing callback for defstruct.
|
||||
"""
|
||||
def announce_struct(module) do
|
||||
case :erlang.get(:elixir_compiler_pid) do
|
||||
:undefined -> :ok
|
||||
pid -> send(pid, {:struct_available, module})
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Callback for raise.
|
||||
"""
|
||||
def raise(msg) when is_binary(msg) do
|
||||
RuntimeError.exception(msg)
|
||||
end
|
||||
def raise(atom) when is_atom(atom) do
|
||||
atom.exception([])
|
||||
end
|
||||
def raise(%{__struct__: struct, __exception__: true} = exception) when is_atom(struct) do
|
||||
exception
|
||||
end
|
||||
def raise(other) do
|
||||
ArgumentError.exception("raise/1 expects an alias, string or exception as " <>
|
||||
"the first argument, got: #{inspect other}")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -451,6 +451,16 @@ defmodule Protocol do
|
||||
end
|
||||
end, builtin)
|
||||
|
||||
# Define a catch-all impl_for/1 clause to pacify Dialyzer (since
|
||||
# destructuring opaque types is illegal, Dialyzer will think none of the
|
||||
# previous clauses matches opaque types, and without this clause, will
|
||||
# conclude that impl_for can't handle an opaque argument). This is a hack
|
||||
# since it relies on Dialyzer not being smart enough to conclude that all
|
||||
# opaque types will get the any_impl_for/0 implementation.
|
||||
Kernel.def impl_for(_) do
|
||||
any_impl_for()
|
||||
end
|
||||
|
||||
@doc false
|
||||
@spec impl_for!(term) :: atom | no_return
|
||||
Kernel.def impl_for!(data) do
|
||||
|
||||
@@ -198,17 +198,17 @@ normalize_vars(Key, {Ref, Counter, _Safe},
|
||||
% Generate match vars by checking if they were updated
|
||||
% or not and assigning the previous value.
|
||||
|
||||
generate_match_vars([{Key, Value, Expr} | T], ClauseVars, Left, Right) ->
|
||||
generate_match_vars([{Key, {Value, _, _}, Expr} | T], ClauseVars, Left, Right) ->
|
||||
case maps:find(Key, ClauseVars) of
|
||||
{ok, Value} ->
|
||||
{ok, {Value, _, _}} ->
|
||||
generate_match_vars(T, ClauseVars, Left, Right);
|
||||
{ok, Clause} ->
|
||||
{ok, {Clause, _, _}} ->
|
||||
generate_match_vars(T, ClauseVars,
|
||||
[{var, 0, element(1, Value)} | Left],
|
||||
[{var, 0, element(1, Clause)} | Right]);
|
||||
[{var, 0, Value} | Left],
|
||||
[{var, 0, Clause} | Right]);
|
||||
error ->
|
||||
generate_match_vars(T, ClauseVars,
|
||||
[{var, 0, element(1, Value)} | Left], [Expr | Right])
|
||||
[{var, 0, Value} | Left], [Expr | Right])
|
||||
end;
|
||||
|
||||
generate_match_vars([], _ClauseVars, Left, Right) ->
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
defmodule Dialyzer.ProtocolOpaque do
|
||||
def circus() do
|
||||
Dialyzer.ProtocolOpaque.Entity.speak(Dialyzer.ProtocolOpaque.Duck.new)
|
||||
end
|
||||
end
|
||||
|
||||
defprotocol Dialyzer.ProtocolOpaque.Entity do
|
||||
def speak(entity)
|
||||
end
|
||||
|
||||
defmodule Dialyzer.ProtocolOpaque.Duck do
|
||||
@opaque t :: %__MODULE__{}
|
||||
defstruct feathers: :white_and_grey
|
||||
|
||||
@spec new :: t
|
||||
def new(), do: %__MODULE__{}
|
||||
|
||||
defimpl Dialyzer.ProtocolOpaque.Entity do
|
||||
def speak(%Dialyzer.ProtocolOpaque.Duck{}), do: "Quack!"
|
||||
end
|
||||
end
|
||||
@@ -81,6 +81,13 @@ defmodule Kernel.DialyzerTest do
|
||||
assert_dialyze_no_warnings! context
|
||||
end
|
||||
|
||||
test "no warnings on protocol calls with opaque types", context do
|
||||
copy_beam! context, Dialyzer.ProtocolOpaque
|
||||
copy_beam! context, Dialyzer.ProtocolOpaque.Entity
|
||||
copy_beam! context, Dialyzer.ProtocolOpaque.Duck
|
||||
assert_dialyze_no_warnings! context
|
||||
end
|
||||
|
||||
defp copy_beam!(context, module) do
|
||||
name = "#{module}.beam"
|
||||
File.cp! Path.join(context[:base_dir], name),
|
||||
|
||||
@@ -37,7 +37,15 @@ defmodule ExUnit.CaptureLog do
|
||||
Returns the binary which is the captured output.
|
||||
|
||||
This function mutes the `:console` backend
|
||||
and captures any log messages sent to Logger.
|
||||
and captures any log messages sent to Logger from the calling
|
||||
processes. It is possible to ensure explicit log messages from other
|
||||
processes are captured by waiting for their exit or monitor
|
||||
signal. This does not guarantee to capture VM generated log messages
|
||||
when a process that is spawned using a low level `Kernel` spawn function
|
||||
(e.g. `Kernel.spawn/1`) raises an exception that isn't rescued or a throw
|
||||
that isn't caught. A `Task`, or other OTP process, will send explicit logs
|
||||
that are sent before its exit or monitor signal and will not cause VM
|
||||
generated log messages.
|
||||
|
||||
Note that when the `async` is set to `true`,
|
||||
the messages from another test might be captured.
|
||||
@@ -59,6 +67,7 @@ defmodule ExUnit.CaptureLog do
|
||||
{:ok, string_io} = StringIO.open("")
|
||||
|
||||
try do
|
||||
_ = :gen_event.which_handlers(:error_logger)
|
||||
:ok = add_capture(string_io, opts)
|
||||
ref = ExUnit.CaptureServer.log_capture_on(self())
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{application, elixir,
|
||||
[{description, "elixir"},
|
||||
{vsn, "1.3.3"},
|
||||
{vsn, "1.3.4"},
|
||||
{modules, [
|
||||
elixir
|
||||
]},
|
||||
|
||||
Reference in New Issue
Block a user