Compare commits
26
Commits
v1.6.0-rc.1
...
v1.6.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2b588dfb3e | ||
|
|
2ba98f247a | ||
|
|
bc9b5ea700 | ||
|
|
992c240b14 | ||
|
|
79ae08f962 | ||
|
|
5d7ee90cba | ||
|
|
66cf08ea89 | ||
|
|
ad62bb970d | ||
|
|
18eef65839 | ||
|
|
0611069c74 | ||
|
|
f2b31a6626 | ||
|
|
3b41c9f396 | ||
|
|
0eb95c9c75 | ||
|
|
b898f09870 | ||
|
|
e89139bcf0 | ||
|
|
ecad849319 | ||
|
|
2658f3666a | ||
|
|
3907a3062f | ||
|
|
bd9751d669 | ||
|
|
e4ebe291f2 | ||
|
|
6b6bcea76c | ||
|
|
96260316ca | ||
|
|
47f2696820 | ||
|
|
740cc1ff0b | ||
|
|
63b8d0ba34 | ||
|
|
12102d8015 |
+31
-34
@@ -29,8 +29,8 @@ This release also introduces two new attributes associated to function definitio
|
||||
@doc "Breaks a collection into chunks"
|
||||
@since "1.0.0"
|
||||
@deprecated "Use chunk_every/2 instead"
|
||||
def chunk_every(collection, chunk_size) do
|
||||
...
|
||||
def chunk(collection, chunk_size) do
|
||||
chunk_every(collection, chunk_size)
|
||||
end
|
||||
|
||||
The `mix xref` task was also updated to warn if your project calls deprecated code. So if a definition is marked as `@deprecated` and a module invokes it, a warning will be emitted during compilation. This effectively provides libraries and frameworks a mechanism to deprecate code without causing multiple warnings to be printed in runtime and without impacting performance.
|
||||
@@ -123,49 +123,33 @@ Those improvements will help developers better understand the relationship betwe
|
||||
|
||||
Other improvements in Mix include better compiler diagnostics for editor integration, support for the `--slowest N` flag in `mix test` that shows the slowest tests in your suite, and a new `mix profile.eprof` task that provides time based profiling, complementing the existing `mix profile.cprof` (count based) and `mix profile.fprof` (flame based).
|
||||
|
||||
## v1.6.0-rc.1 (2018-01-11)
|
||||
## v1.6.1 (2018-01-29)
|
||||
|
||||
### 1. Enhancements
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Code] Render tuples and bitstrings as "flex" in the formatter so they attempt to fit the maximum amount of entries in the same line
|
||||
* [Exception] Improve the printing of guards on blamed exceptions
|
||||
* [List] Rearrange equals and inserts for shorter diff scripts in `List.myers_difference/2`
|
||||
* [Kernel] Warn if heredoc is outdented compared to its closing quotes
|
||||
* [Record] Allow `:macros` and `:includes` to be given to `Record.extract/2`
|
||||
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit.Assertions] Perform inclusive checks in `assert_in_delta`
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx.CLI] Provide hints for developers when a bad host name is given to `--remsh`
|
||||
|
||||
#### Logger
|
||||
|
||||
* [Logger] Add `:discard_threshold` to Logger to help with buffer overflow
|
||||
* [DynamicSupervisor] Implement `child_spec/1` for DynamicSupervisor
|
||||
* [Kernel] Raise better error messages on invalid map syntax
|
||||
|
||||
### 2. Bug fixes
|
||||
|
||||
#### Elixir
|
||||
|
||||
* [Inspect] Properly handle minus signal for non-decimal negative integers
|
||||
* [Kernel] Allow `defguard` to call another `defguard` (previous RC only)
|
||||
* [Kernel] Return right side expression value on variable struct matching (previous RC only)
|
||||
* [Kernel] Do not leak variables during optimizations (previous RC only)
|
||||
* [Kernel] Avoid badarg/badfun on long-lived functions defined during compilation (previous RC only)
|
||||
* [Kernel] Fix precedence of `&` in regards to `=` (previous RC only)
|
||||
* [String] Consider case ignorable characters when downcasing greek characters (previous RC only)
|
||||
* [Code.Formatter] Only rearrange `not in` operator if explicitly opted-in
|
||||
* [Code.Formatter] Ensure do blocks do not exceed line length on calls with a single argument
|
||||
* [Collectable] Support bitstrings in Collectable and for-comprehensions (regression in v1.6.0)
|
||||
* [GenServer] Do not override user own `@opts` attribute
|
||||
* [Enum] Reintroduce zipping of any enumerable of enumerables in `Enum.zip/1` (regression in v1.6.0)
|
||||
* [Macro] Reorder kw blocks in `Macro.to_string/1` to avoid warnings
|
||||
* [Protocol] Fix protocol consolidation when some chunks may be missing
|
||||
* [Stream] Reintroduce zipping of any enumerable of enumerables in `Enum.zip/1` (regression in v1.6.0)
|
||||
* [Supervisor] Do not override user own `@opts` attribute
|
||||
* [Supervisor] Add `@spec` to second clause of `start_link/2`
|
||||
|
||||
#### Mix
|
||||
#### ExUnit
|
||||
|
||||
* [mix clean] Fix error when cleaning compiled Erlang code (previous RC only)
|
||||
* [mix deps.update] Ensure transitive new non-Hex dependencies are also fetched when a repo is updated
|
||||
* [mix format] Raise if imported dependencies have not been checked out on `mix format` (previous RC only)
|
||||
* [ExUnit.Case] Reintroduce `:case` in ExUnit setup/setup_all/test context
|
||||
|
||||
## v1.6.0-rc.0 (2017-12-24)
|
||||
## v1.6.0 (2018-01-17)
|
||||
|
||||
### 1. Enhancements
|
||||
|
||||
@@ -180,6 +164,7 @@ Other improvements in Mix include better compiler diagnostics for editor integra
|
||||
* [Code] Support column annotations in quoted expressions with `columns: true` in `Code.string_to_quoted/2`
|
||||
* [DynamicSupervisor] Add `DynamicSupervisor` designed to manage children that are added and removed dynamically
|
||||
* [Exception] Make `Exception.blame/3` extensible by adding an optional `blame/2` callback to exceptions
|
||||
* [Exception] Improve the printing of guards on blamed exceptions
|
||||
* [Enumerable] Add `Enumerable.slice/1` and optimize many `Enum` operations with the new protocol. This allows data-structures with index-based random access to provide a non-linear implementation
|
||||
* [Inspect] Show UTF-8 BOM on inspected strings
|
||||
* [Inspect.Algebra] Add `:strict` and `:flex` breaks - this gives more control over the document fitting
|
||||
@@ -190,10 +175,14 @@ Other improvements in Mix include better compiler diagnostics for editor integra
|
||||
* [Kernel] Prefix variables with V when emitting Erlang code. This improves the integration with tools such as Erlang code formatters and the GUI debugger
|
||||
* [Kernel] Warn on the use of `length(x) == 0` in guards
|
||||
* [Kernel] Warn if `catch` comes before `rescue` in try
|
||||
* [Kernel] Warn if heredoc is outdented compared to its closing quotes
|
||||
* [Kernel] Add `defguard/1` and `defguardp/1` to make it easier to build guard-safe macros
|
||||
* [Kernel.ParallelCompiler] Add `compile/2`, `compile_to_path/3` and `require/2` which provide detailed information about warnings and errors
|
||||
* [Kernel.SpecialForms] Support the `uniq: true` flag in `for` comprehensions
|
||||
* [Module] Introduce `@deprecated` and `@since` attributes
|
||||
* [Module] Emit conflicting behaviour warnings if the same behaviour is given more than once
|
||||
* [List] Rearrange equals and inserts for shorter diff scripts in `List.myers_difference/2`
|
||||
* [Record] Allow `:macros` and `:includes` to be given to `Record.extract/2`
|
||||
* [Stream] Add `Stream.intersperse/2`
|
||||
* [String] Update to Unicode 10
|
||||
* [String] Allow passing empty string `match` to `String.replace/4`
|
||||
@@ -203,16 +192,22 @@ Other improvements in Mix include better compiler diagnostics for editor integra
|
||||
|
||||
#### ExUnit
|
||||
|
||||
* [ExUnit.Assertions] Perform inclusive checks in `assert_in_delta`
|
||||
* [ExUnit.Callbacks] Add `ExUnit.Callbacks.start_supervised!/2`
|
||||
* [ExUnit.Case] Generate a random seed per test based on the test suite seed
|
||||
|
||||
#### IEx
|
||||
|
||||
* [IEx.Autocomplete] Provide contextual autocompletion: `t Enum.` will autocomplete types, `b Enum` will autocomplete callbacks
|
||||
* [IEx.CLI] Provide hints for developers when a bad host name is given to `--remsh`
|
||||
* [IEx.Helpers] Automatically include specs when showing documentation for functions/macros
|
||||
* [IEx.Helpers] Improve formatting of behaviours and typespecs by using the formatter
|
||||
* [IEx.Helpers] Allow pattern matching and guard expressions when on `IEx.break!`
|
||||
|
||||
#### Logger
|
||||
|
||||
* [Logger] Add `:discard_threshold` to Logger to help with message queue overflow
|
||||
|
||||
#### Mix
|
||||
|
||||
* [mix app.start] Add `--preload-modules` to `mix app.start`
|
||||
@@ -235,6 +230,7 @@ Other improvements in Mix include better compiler diagnostics for editor integra
|
||||
#### Elixir
|
||||
|
||||
* [CLI] Support path with spaces as argument to elixir.bat
|
||||
* [Inspect] Properly handle minus signal for non-decimal negative integers
|
||||
* [Integer] Do not raise on non-integer values in `is_odd`/`is_even`
|
||||
* [Kernel] Solve a precedence issue between `&` and `|`, such as `[&Foo.bar/1 | &Baz.bat/2]`
|
||||
* [Kernel] Do not load dynamic Elixir modules as `:in_memory` as this value is not officially supported by the code server. Instead, use an empty list, which is the same value used by Erlang.
|
||||
@@ -251,6 +247,7 @@ Other improvements in Mix include better compiler diagnostics for editor integra
|
||||
* [mix app.start] Improve the quality of reports if app fails to boot
|
||||
* [mix cmd] Allow `mix cmd` to be invoked multiple times without marking it as executed
|
||||
* [mix deps] Ensure optional dependencies in umbrella applications are loaded
|
||||
* [mix deps.update] Ensure transitive new non-Hex dependencies are also fetched when a repo is updated
|
||||
* [mix xref] Take compile dependencies with higher priority than runtime ones when building a graph
|
||||
* [mix xref] Handle external files for xref callers and warnings
|
||||
|
||||
|
||||
@@ -243,13 +243,13 @@ build_man: man/iex.1 man/elixir.1
|
||||
|
||||
man/iex.1:
|
||||
$(Q) cp man/iex.1.in man/iex.1
|
||||
$(Q) sed -i.bak "/{COMMON}/r common" man/iex.1
|
||||
$(Q) sed -i.bak "/{COMMON}/r man/common" man/iex.1
|
||||
$(Q) sed -i.bak "/{COMMON}/d" man/iex.1
|
||||
$(Q) rm -f man/iex.1.bak
|
||||
|
||||
man/elixir.1:
|
||||
$(Q) cp man/elixir.1.in man/elixir.1
|
||||
$(Q) sed -i.bak "/{COMMON}/r common" man/elixir.1
|
||||
$(Q) sed -i.bak "/{COMMON}/r man/common" man/elixir.1
|
||||
$(Q) sed -i.bak "/{COMMON}/d" man/elixir.1
|
||||
$(Q) rm -f man/elixir.1.bak
|
||||
|
||||
|
||||
@@ -149,9 +149,7 @@ defmodule Agent do
|
||||
|
||||
@doc false
|
||||
defmacro __using__(opts) do
|
||||
quote location: :keep do
|
||||
@opts unquote(opts)
|
||||
|
||||
quote location: :keep, bind_quoted: [opts: opts] do
|
||||
@doc false
|
||||
def child_spec(arg) do
|
||||
default = %{
|
||||
@@ -159,7 +157,7 @@ defmodule Agent do
|
||||
start: {__MODULE__, :start_link, [arg]}
|
||||
}
|
||||
|
||||
Supervisor.child_spec(default, @opts)
|
||||
Supervisor.child_spec(default, unquote(Macro.escape(opts)))
|
||||
end
|
||||
|
||||
defoverridable child_spec: 1
|
||||
|
||||
@@ -92,7 +92,9 @@ defmodule Code.Formatter do
|
||||
|
||||
# Stdlib,
|
||||
defrecord: 2,
|
||||
defrecord: 3,
|
||||
defrecordp: 2,
|
||||
defrecordp: 3,
|
||||
|
||||
# Testing
|
||||
all: :*,
|
||||
@@ -274,7 +276,8 @@ defmodule Code.Formatter do
|
||||
defp next_eol('\r\n' ++ rest, count), do: next_eol(rest, count + 1)
|
||||
defp next_eol(_, count), do: count
|
||||
|
||||
defp previous_eol([{token, {_, _, count}} | _]) when token in [:eol, :",", :";"] and count > 0 do
|
||||
defp previous_eol([{token, {_, _, count}} | _])
|
||||
when token in [:eol, :",", :";"] and count > 0 do
|
||||
count
|
||||
end
|
||||
|
||||
@@ -451,7 +454,8 @@ defmodule Code.Formatter do
|
||||
{atom_to_algebra(atom), state}
|
||||
end
|
||||
|
||||
defp quoted_to_algebra({:__block__, meta, [integer]}, _context, state) when is_integer(integer) do
|
||||
defp quoted_to_algebra({:__block__, meta, [integer]}, _context, state)
|
||||
when is_integer(integer) do
|
||||
{integer_to_algebra(Keyword.fetch!(meta, :original)), state}
|
||||
end
|
||||
|
||||
@@ -504,8 +508,15 @@ defmodule Code.Formatter do
|
||||
|
||||
# not(left in right)
|
||||
# left not in right
|
||||
defp quoted_to_algebra({:not, meta, [{:in, _, [left, right]}]}, context, state) do
|
||||
binary_op_to_algebra(:in, "not in", meta, left, right, context, state)
|
||||
defp quoted_to_algebra({:not, meta, [{:in, _, [left, right]} = arg]}, context, state) do
|
||||
%{rename_deprecated_at: since} = state
|
||||
|
||||
# TODO: Remove since check on Elixir v2.0 and the OP arrengement is removed.
|
||||
if meta[:operator] == :"not in" || (since && Version.match?(since, "~> 1.5")) do
|
||||
binary_op_to_algebra(:in, "not in", meta, left, right, context, state)
|
||||
else
|
||||
unary_op_to_algebra(:not, meta, arg, context, state)
|
||||
end
|
||||
end
|
||||
|
||||
defp quoted_to_algebra({:fn, meta, [_ | _] = clauses}, _context, state) do
|
||||
@@ -906,7 +917,8 @@ defmodule Code.Formatter do
|
||||
# Mod.function()
|
||||
# var.function
|
||||
# expression.function(arguments)
|
||||
defp remote_to_algebra({{:., _, [target, fun]}, meta, args}, context, state) when is_atom(fun) do
|
||||
defp remote_to_algebra({{:., _, [target, fun]}, meta, args}, context, state)
|
||||
when is_atom(fun) do
|
||||
{target_doc, state} = remote_target_to_algebra(target, state)
|
||||
fun = remote_fun_to_algebra(target, fun, length(args), state)
|
||||
remote_doc = target_doc |> concat(".") |> concat(string(fun))
|
||||
@@ -1073,6 +1085,10 @@ defmodule Code.Formatter do
|
||||
"ed_to_algebra(&1, context, &2)
|
||||
)
|
||||
|
||||
# If we have a single argument, then we won't have an option to break
|
||||
# before the "extra" part, so we ungroup it and build it later.
|
||||
args_doc = ungroup_if_group(args_doc)
|
||||
|
||||
doc =
|
||||
if skip_parens? do
|
||||
" "
|
||||
@@ -1187,7 +1203,8 @@ defmodule Code.Formatter do
|
||||
["\n" | entries]
|
||||
end
|
||||
|
||||
defp interpolation_to_algebra([entry | entries], escape, state, acc, last) when is_binary(entry) do
|
||||
defp interpolation_to_algebra([entry | entries], escape, state, acc, last)
|
||||
when is_binary(entry) do
|
||||
acc = concat(acc, escape_string(entry, escape))
|
||||
interpolation_to_algebra(entries, escape, state, acc, last)
|
||||
end
|
||||
@@ -2032,6 +2049,9 @@ defmodule Code.Formatter do
|
||||
|
||||
## Algebra helpers
|
||||
|
||||
defp ungroup_if_group({:doc_group, group, _mode}), do: group
|
||||
defp ungroup_if_group(other), do: other
|
||||
|
||||
defp format_to_string(doc) do
|
||||
doc |> Inspect.Algebra.format(:infinity) |> IO.iodata_to_binary()
|
||||
end
|
||||
|
||||
@@ -90,11 +90,37 @@ defimpl Collectable, for: List do
|
||||
end
|
||||
|
||||
defimpl Collectable, for: BitString do
|
||||
def into(original) do
|
||||
def into(original) when is_binary(original) do
|
||||
fun = fn
|
||||
acc, {:cont, x} when is_bitstring(x) -> [acc | x]
|
||||
acc, :done -> IO.iodata_to_binary(acc)
|
||||
_, :halt -> :ok
|
||||
acc, {:cont, x} when is_binary(x) and is_list(acc) ->
|
||||
[acc | x]
|
||||
|
||||
acc, {:cont, x} when is_bitstring(x) and is_bitstring(acc) ->
|
||||
<<acc::bitstring, x::bitstring>>
|
||||
|
||||
acc, {:cont, x} when is_bitstring(x) ->
|
||||
<<IO.iodata_to_binary(acc)::bitstring, x::bitstring>>
|
||||
|
||||
acc, :done ->
|
||||
IO.iodata_to_binary(acc)
|
||||
|
||||
_, :halt ->
|
||||
:ok
|
||||
end
|
||||
|
||||
{[original], fun}
|
||||
end
|
||||
|
||||
def into(original) when is_bitstring(original) do
|
||||
fun = fn
|
||||
acc, {:cont, x} when is_bitstring(x) ->
|
||||
<<acc::bitstring, x::bitstring>>
|
||||
|
||||
acc, :done ->
|
||||
acc
|
||||
|
||||
_, :halt ->
|
||||
:ok
|
||||
end
|
||||
|
||||
{original, fun}
|
||||
|
||||
@@ -57,6 +57,63 @@ defmodule DynamicSupervisor do
|
||||
|
||||
A supervisor is bound to the same name registration rules as a `GenServer`.
|
||||
Read more about these rules in the documentation for `GenServer`.
|
||||
|
||||
## Migrating from Supervisor's :simple_one_for_one
|
||||
|
||||
In case you were using the deprecated `:simple_one_for_one` strategy from
|
||||
the `Supervisor` module, you can migrate to the `DynamicSupervisor` in
|
||||
few steps.
|
||||
|
||||
Imagine the given "old" code:
|
||||
|
||||
defmodule MySupervisor do
|
||||
use Supervisor
|
||||
|
||||
def start_link(arg) do
|
||||
Supervisor.start_link(__MODULE__, arg, name: __MODULE__)
|
||||
end
|
||||
|
||||
def start_child(foo, bar, baz) do
|
||||
# This will start child by calling MyWorker.start_link(implicit_arg, foo, bar, baz)
|
||||
Supervisor.start_child(__MODULE__, [foo, bar, baz])
|
||||
end
|
||||
|
||||
def init(implicit_arg) do
|
||||
children = [
|
||||
worker(MyWorker, [implicit_arg])
|
||||
]
|
||||
|
||||
supervise(children, strategy: :simple_one_for_one)
|
||||
end
|
||||
end
|
||||
|
||||
It can be upgraded to the DynamicSupervisor like this:
|
||||
|
||||
defmodule MySupervisor do
|
||||
use DynamicSupervisor
|
||||
|
||||
def start_link(arg) do
|
||||
DynamicSupervisor.start_link(__MODULE__, arg, name: __MODULE__)
|
||||
end
|
||||
|
||||
def start_child(foo, bar, baz) do
|
||||
# This will start child by calling MyWorker.start_link(implicit_arg, foo, bar, baz)
|
||||
spec = Supervisor.Spec.worker(MyWorker, [foo, bar, baz])
|
||||
DynamicSupervisor.start_child(__MODULE__, spec)
|
||||
end
|
||||
|
||||
def init(implicit_arg) do
|
||||
DynamicSupervisor.init(
|
||||
strategy: :one_for_one,
|
||||
extra_arguments: [implicit_arg]
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
The difference is that the `DynamicSupervisor` expects the child specification
|
||||
at the moment `start_child/2` is called, and no longer on the init callback.
|
||||
If there are any implicit arguments given on initialization, such as `[implicit_arg]`,
|
||||
it can be given in the `:extra_arguments` flag on `DynamicSupervisor.init/1`.
|
||||
"""
|
||||
|
||||
@behaviour GenServer
|
||||
@@ -108,11 +165,19 @@ defmodule DynamicSupervisor do
|
||||
restarts: []
|
||||
]
|
||||
|
||||
@doc false
|
||||
def child_spec(arg) do
|
||||
%{
|
||||
id: DynamicSupervisor,
|
||||
start: {DynamicSupervisor, :start_link, [arg]},
|
||||
type: :supervisor
|
||||
}
|
||||
end
|
||||
|
||||
@doc false
|
||||
defmacro __using__(opts) do
|
||||
quote location: :keep do
|
||||
quote location: :keep, bind_quoted: [opts: opts] do
|
||||
@behaviour DynamicSupervisor
|
||||
@opts unquote(opts)
|
||||
|
||||
@doc false
|
||||
def child_spec(arg) do
|
||||
@@ -122,7 +187,7 @@ defmodule DynamicSupervisor do
|
||||
type: :supervisor
|
||||
}
|
||||
|
||||
Supervisor.child_spec(default, @opts)
|
||||
Supervisor.child_spec(default, unquote(Macro.escape(opts)))
|
||||
end
|
||||
|
||||
defoverridable child_spec: 1
|
||||
|
||||
@@ -2718,10 +2718,10 @@ defmodule Enum do
|
||||
end
|
||||
|
||||
@doc """
|
||||
Zips corresponding elements from a list of enumerables
|
||||
Zips corresponding elements from a finite collection of enumerables
|
||||
into one list of tuples.
|
||||
|
||||
The zipping finishes as soon as any enumerable in the given list completes.
|
||||
The zipping finishes as soon as any enumerable in the given collection completes.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -2733,10 +2733,11 @@ defmodule Enum do
|
||||
|
||||
"""
|
||||
@spec zip([t]) :: t
|
||||
@spec zip(t) :: t
|
||||
|
||||
def zip([]), do: []
|
||||
|
||||
def zip(enumerables) when is_list(enumerables) do
|
||||
def zip(enumerables) do
|
||||
Stream.zip(enumerables).({:cont, []}, &{:cont, [&1 | &2]})
|
||||
|> elem(1)
|
||||
|> :lists.reverse()
|
||||
|
||||
+35
-4
@@ -3,6 +3,33 @@ import Kernel, except: [round: 1]
|
||||
defmodule Float do
|
||||
@moduledoc """
|
||||
Functions for working with floating-point numbers.
|
||||
|
||||
## Kernel functions
|
||||
|
||||
There are functions related to floating-point numbers on the `Kernel` module
|
||||
too. Here is a list of them:
|
||||
|
||||
* `Kernel.round/1`: rounds a number to the nearest integer.
|
||||
* `Kernel.trunc/1`: returns the integer part of a number.
|
||||
|
||||
## Known issues
|
||||
|
||||
There are some very well known problems with floating-point numbers
|
||||
and arithmetics due to the fact most decimal fractions cannot be
|
||||
represented by a floating-point binary.
|
||||
|
||||
For example, the numbers 0.1 and 0.01 are two of them, what means the result
|
||||
of squaring 0.1 does not give 0.01 neither the closest representable. Here is
|
||||
what happens in this case:
|
||||
|
||||
* The closest representable number to 0.1 is 0.1000000014
|
||||
* The closest representable number to 0.01 is 0.0099999997
|
||||
* Doing 0.1 * 0.1 should return 0.01, but because 0.1 is actually 0.1000000014,
|
||||
the result is 0.010000000000000002, and because this is not the closest
|
||||
representable number to 0.01, you'll get the wrong result for this operation
|
||||
|
||||
There are also other known problems like flooring or rounding numbers. See
|
||||
`round/2` and `floor/2` for more details about them.
|
||||
"""
|
||||
|
||||
import Bitwise
|
||||
@@ -79,13 +106,18 @@ defmodule Float do
|
||||
defp add_dot(acc, false), do: acc <> ".0"
|
||||
|
||||
@doc """
|
||||
Rounds a float to the largest integer less than or equal to `num`.
|
||||
Rounds a float to the largest number less than or equal to `num`.
|
||||
|
||||
`floor/2` also accepts a precision to round a floating-point value down
|
||||
to an arbitrary number of fractional digits (between 0 and 15).
|
||||
The operation is performed on the binary floating point, without a
|
||||
conversion to decimal.
|
||||
|
||||
This function always returns a float. `Kernel.trunc/1` may be used instead to
|
||||
truncate the result to an integer afterwards.
|
||||
|
||||
## Known issues
|
||||
|
||||
The behaviour of `floor/2` for floats can be surprising. For example:
|
||||
|
||||
iex> Float.floor(12.52, 2)
|
||||
@@ -96,9 +128,6 @@ defmodule Float do
|
||||
and therefore the number above is internally represented as 12.51999999,
|
||||
which explains the behaviour above.
|
||||
|
||||
This function always returns a float. `Kernel.trunc/1` may be used instead to
|
||||
truncate the result to an integer afterwards.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> Float.floor(34.25)
|
||||
@@ -174,6 +203,8 @@ defmodule Float do
|
||||
`Kernel.round/1` if you want a function that accepts both floats
|
||||
and integers and always returns an integer.
|
||||
|
||||
## Known issues
|
||||
|
||||
The behaviour of `round/2` for floats can be surprising. For example:
|
||||
|
||||
iex> Float.round(5.5675, 3)
|
||||
|
||||
@@ -567,9 +567,8 @@ defmodule GenServer do
|
||||
|
||||
@doc false
|
||||
defmacro __using__(opts) do
|
||||
quote location: :keep do
|
||||
quote location: :keep, bind_quoted: [opts: opts] do
|
||||
@behaviour GenServer
|
||||
@opts unquote(opts)
|
||||
|
||||
@doc false
|
||||
def child_spec(arg) do
|
||||
@@ -578,7 +577,7 @@ defmodule GenServer do
|
||||
start: {__MODULE__, :start_link, [arg]}
|
||||
}
|
||||
|
||||
Supervisor.child_spec(default, @opts)
|
||||
Supervisor.child_spec(default, unquote(Macro.escape(opts)))
|
||||
end
|
||||
|
||||
defoverridable child_spec: 1
|
||||
@@ -886,7 +885,8 @@ defmodule GenServer do
|
||||
See `multi_call/4` for more information.
|
||||
"""
|
||||
@spec abcast([node], name :: atom, term) :: :abcast
|
||||
def abcast(nodes \\ [node() | Node.list()], name, request) when is_list(nodes) and is_atom(name) do
|
||||
def abcast(nodes \\ [node() | Node.list()], name, request)
|
||||
when is_list(nodes) and is_atom(name) do
|
||||
msg = cast_msg(request)
|
||||
_ = for node <- nodes, do: do_send({name, node}, msg)
|
||||
:abcast
|
||||
|
||||
@@ -934,7 +934,7 @@ defmodule Inspect.Algebra do
|
||||
do: fits?(w, k, b?, [{apply_nesting(i, k, j), m, x} | t])
|
||||
|
||||
defp fits?(w, k, b?, [{i, m, doc_cons(x, y)} | t]),
|
||||
do: fits?(w, k, b?, [{i, m, x} | [{i, m, y} | t]])
|
||||
do: fits?(w, k, b?, [{i, m, x}, {i, m, y} | t])
|
||||
|
||||
defp fits?(w, k, b?, [{i, m, doc_group(x, _)} | t]),
|
||||
do: fits?(w, k, b?, [{i, m, x} | {:tail, b?, t}])
|
||||
@@ -943,7 +943,7 @@ defmodule Inspect.Algebra do
|
||||
defp format(_, _, []), do: []
|
||||
defp format(w, k, [{_, _, :doc_nil} | t]), do: format(w, k, t)
|
||||
defp format(w, _, [{i, _, :doc_line} | t]), do: [indent(i) | format(w, i, t)]
|
||||
defp format(w, k, [{i, m, doc_cons(x, y)} | t]), do: format(w, k, [{i, m, x} | [{i, m, y} | t]])
|
||||
defp format(w, k, [{i, m, doc_cons(x, y)} | t]), do: format(w, k, [{i, m, x}, {i, m, y} | t])
|
||||
defp format(w, k, [{i, m, doc_color(x, c)} | t]), do: [ansi(c) | format(w, k, [{i, m, x} | t])]
|
||||
defp format(w, k, [{_, _, doc_string(s, l)} | t]), do: [s | format(w, k + l, t)]
|
||||
defp format(w, k, [{_, _, s} | t]) when is_binary(s), do: [s | format(w, k + byte_size(s), t)]
|
||||
|
||||
@@ -3330,7 +3330,8 @@ defmodule Kernel do
|
||||
:lists.mapfoldl(fun, acc, list)
|
||||
end
|
||||
|
||||
defp ensure_evaled_element(elem, acc) when is_number(elem) or is_atom(elem) or is_binary(elem) do
|
||||
defp ensure_evaled_element(elem, acc)
|
||||
when is_number(elem) or is_atom(elem) or is_binary(elem) do
|
||||
{elem, acc}
|
||||
end
|
||||
|
||||
|
||||
@@ -1776,19 +1776,19 @@ defmodule Kernel.SpecialForms do
|
||||
allows matching on both the *kind* of the caught value as well as the value
|
||||
itself:
|
||||
|
||||
try do
|
||||
exit(:shutdown)
|
||||
catch
|
||||
:exit, value
|
||||
IO.puts "Exited with value #{inspect(value)}"
|
||||
end
|
||||
try do
|
||||
exit(:shutdown)
|
||||
catch
|
||||
:exit, value
|
||||
IO.puts "Exited with value #{inspect(value)}"
|
||||
end
|
||||
|
||||
try do
|
||||
exit(:shutdown)
|
||||
catch
|
||||
kind, value when kind in [:exit, :throw] ->
|
||||
IO.puts "Caught exit or throw with value #{inspect(value)}"
|
||||
end
|
||||
try do
|
||||
exit(:shutdown)
|
||||
catch
|
||||
kind, value when kind in [:exit, :throw] ->
|
||||
IO.puts "Caught exit or throw with value #{inspect(value)}"
|
||||
end
|
||||
|
||||
The `catch` clause also supports `:error` alongside `:exit` and `:throw` as
|
||||
in Erlang, although this is commonly avoided in favor of `raise`/`rescue` control
|
||||
|
||||
@@ -745,7 +745,7 @@ defmodule Macro do
|
||||
end
|
||||
|
||||
# Block keywords
|
||||
kw_keywords = [:do, :catch, :rescue, :after, :else]
|
||||
kw_keywords = [:do, :rescue, :catch, :else, :after]
|
||||
|
||||
defp kw_blocks?([{:do, _} | _] = kw) do
|
||||
Enum.all?(kw, &match?({x, _} when x in unquote(kw_keywords), &1))
|
||||
|
||||
@@ -150,7 +150,8 @@ defmodule MapSet do
|
||||
# If the first set is less than twice the size of the second map,
|
||||
# it is fastest to re-accumulate items in the first set that are not
|
||||
# present in the second set.
|
||||
def difference(%MapSet{map: map1}, %MapSet{map: map2}) when map_size(map1) < map_size(map2) * 2 do
|
||||
def difference(%MapSet{map: map1}, %MapSet{map: map2})
|
||||
when map_size(map1) < map_size(map2) * 2 do
|
||||
map =
|
||||
map1
|
||||
|> Map.keys()
|
||||
|
||||
@@ -1308,7 +1308,7 @@ defmodule Module do
|
||||
|
||||
not Code.ensure_compiled?(behaviour) ->
|
||||
message =
|
||||
"module #{inspect(behaviour)} does not exist (in module #{inspect(env.module)})"
|
||||
"@behaviour #{inspect(behaviour)} does not exist (in module #{inspect(env.module)})"
|
||||
|
||||
unless standard_behaviour?(behaviour) do
|
||||
:elixir_errors.warn(env.line, env.file, message)
|
||||
|
||||
@@ -61,7 +61,8 @@ defmodule Module.LocalsTracker do
|
||||
@doc """
|
||||
Adds an import dispatch to the given target.
|
||||
"""
|
||||
def add_import(d, function, module, {name, arity}) when is_tuple(function) and is_atom(module) do
|
||||
def add_import(d, function, module, {name, arity})
|
||||
when is_tuple(function) and is_atom(module) do
|
||||
tuple = {:import, name, arity}
|
||||
put_edge(d, tuple, module)
|
||||
put_edge(d, function, tuple)
|
||||
|
||||
@@ -317,14 +317,16 @@ defmodule Protocol do
|
||||
[
|
||||
{:abstract_code, {_raw, abstract_code}},
|
||||
{:attributes, attributes},
|
||||
{:compile_info, compile_info},
|
||||
{'ExDc', docs},
|
||||
{'ExDp', deprecated}
|
||||
{:compile_info, compile_info} | extra_chunks
|
||||
] = entries
|
||||
|
||||
extra_chunks =
|
||||
for {name, contents} when is_binary(contents) <- extra_chunks,
|
||||
do: {List.to_string(name), contents}
|
||||
|
||||
case attributes[:protocol] do
|
||||
[fallback_to_any: any] ->
|
||||
{:ok, {protocol, any, abstract_code}, {compile_info, docs, deprecated}}
|
||||
{:ok, {protocol, any, abstract_code}, {compile_info, extra_chunks}}
|
||||
|
||||
_ ->
|
||||
{:error, :not_a_protocol}
|
||||
@@ -493,15 +495,11 @@ defmodule Protocol do
|
||||
end
|
||||
|
||||
# Finally compile the module and emit its bytecode.
|
||||
defp compile(protocol, code, {compile_info, docs, deprecated}) do
|
||||
defp compile(protocol, code, {compile_info, extra_chunks}) do
|
||||
opts = Keyword.take(compile_info, [:source])
|
||||
opts = if Code.compiler_options()[:debug_info], do: [:debug_info | opts], else: opts
|
||||
{:ok, ^protocol, binary, _warnings} = :compile.forms(code, [:return | opts])
|
||||
|
||||
case docs do
|
||||
:missing_chunk -> {:ok, binary}
|
||||
_ -> {:ok, :elixir_erl.add_beam_chunks(binary, [{"ExDc", docs}, {"ExDp", deprecated}])}
|
||||
end
|
||||
{:ok, :elixir_erl.add_beam_chunks(binary, extra_chunks)}
|
||||
end
|
||||
|
||||
## Definition callbacks
|
||||
|
||||
@@ -465,7 +465,8 @@ defmodule Regex do
|
||||
end
|
||||
end
|
||||
|
||||
def split(%Regex{re_pattern: compiled}, string, opts) when is_binary(string) and is_list(opts) do
|
||||
def split(%Regex{re_pattern: compiled}, string, opts)
|
||||
when is_binary(string) and is_list(opts) do
|
||||
on = Keyword.get(opts, :on, :first)
|
||||
|
||||
case :re.run(string, compiled, [:global, capture: on]) do
|
||||
@@ -653,7 +654,8 @@ defmodule Regex do
|
||||
string
|
||||
end
|
||||
|
||||
defp apply_list(whole, string, pos, replacement, [[{mpos, _} | _] | _] = list) when mpos > pos do
|
||||
defp apply_list(whole, string, pos, replacement, [[{mpos, _} | _] | _] = list)
|
||||
when mpos > pos do
|
||||
length = mpos - pos
|
||||
<<untouched::binary-size(length), rest::binary>> = string
|
||||
[untouched | apply_list(whole, rest, mpos, replacement, list)]
|
||||
|
||||
@@ -1091,10 +1091,10 @@ defmodule Stream do
|
||||
def zip(left, right), do: zip([left, right])
|
||||
|
||||
@doc """
|
||||
Zips corresponding elements from a list of enumerables
|
||||
Zips corresponding elements from a finite collection of enumerables
|
||||
into one stream of tuples.
|
||||
|
||||
The zipping finishes as soon as any enumerable in the given list completes.
|
||||
The zipping finishes as soon as any enumerable in the given collection completes.
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -1105,7 +1105,12 @@ defmodule Stream do
|
||||
|
||||
"""
|
||||
@spec zip([Enumerable.t()]) :: Enumerable.t()
|
||||
def zip(enumerables) when is_list(enumerables) do
|
||||
@spec zip(Enumerable.t()) :: Enumerable.t()
|
||||
def zip(enumerables) do
|
||||
&prepare_zip(enumerables, &1, &2)
|
||||
end
|
||||
|
||||
defp prepare_zip(enumerables, acc, fun) do
|
||||
step = &do_zip_step(&1, &2)
|
||||
|
||||
enum_funs =
|
||||
@@ -1113,7 +1118,7 @@ defmodule Stream do
|
||||
{&Enumerable.reduce(enum, &1, step), :cont}
|
||||
end)
|
||||
|
||||
&do_zip(enum_funs, &1, &2)
|
||||
do_zip(enum_funs, acc, fun)
|
||||
end
|
||||
|
||||
# This implementation of do_zip/3 works for any number of
|
||||
@@ -1128,6 +1133,10 @@ defmodule Stream do
|
||||
{:suspended, acc, &do_zip(zips, &1, fun)}
|
||||
end
|
||||
|
||||
defp do_zip([], {:cont, acc}, _callback) do
|
||||
{:done, acc}
|
||||
end
|
||||
|
||||
defp do_zip(zips, {:cont, acc}, callback) do
|
||||
try do
|
||||
do_zip_next_tuple(zips, acc, callback, [], [])
|
||||
|
||||
@@ -446,7 +446,8 @@ defmodule Supervisor do
|
||||
has been replaced by the `DynamicSupervisor`. The `:simple_one_for_one`
|
||||
supervisor was similar to `:one_for_one` but suits better when dynamically
|
||||
attaching children. Many functions in this module behaved slightly
|
||||
differently when this strategy is used.
|
||||
differently when this strategy is used. See the `DynamicSupervisor` module
|
||||
for more information and migration strategies.
|
||||
|
||||
## Name registration
|
||||
|
||||
@@ -456,10 +457,8 @@ defmodule Supervisor do
|
||||
|
||||
@doc false
|
||||
defmacro __using__(opts) do
|
||||
quote location: :keep do
|
||||
quote location: :keep, bind_quoted: [opts: opts] do
|
||||
import Supervisor.Spec
|
||||
@behaviour Supervisor
|
||||
@opts unquote(opts)
|
||||
|
||||
@doc false
|
||||
def child_spec(arg) do
|
||||
@@ -469,7 +468,7 @@ defmodule Supervisor do
|
||||
type: :supervisor
|
||||
}
|
||||
|
||||
Supervisor.child_spec(default, @opts)
|
||||
Supervisor.child_spec(default, unquote(Macro.escape(opts)))
|
||||
end
|
||||
|
||||
defoverridable child_spec: 1
|
||||
@@ -760,6 +759,10 @@ defmodule Supervisor do
|
||||
name, the supported values are described in the "Name registration"
|
||||
section in the `GenServer` module docs.
|
||||
"""
|
||||
|
||||
# It is important to keep the 2-arity spec because it is a catch
|
||||
# all to start_link(children, options).
|
||||
@spec start_link(module, term) :: on_start
|
||||
@spec start_link(module, term, GenServer.options()) :: on_start
|
||||
def start_link(module, arg, options \\ []) when is_list(options) do
|
||||
case Keyword.get(options, :name) do
|
||||
|
||||
@@ -184,9 +184,7 @@ defmodule Task do
|
||||
|
||||
@doc false
|
||||
defmacro __using__(opts) do
|
||||
quote location: :keep do
|
||||
@opts unquote(opts)
|
||||
|
||||
quote location: :keep, bind_quoted: [opts: opts] do
|
||||
@doc false
|
||||
def child_spec(arg) do
|
||||
default = %{
|
||||
@@ -195,7 +193,7 @@ defmodule Task do
|
||||
restart: :temporary
|
||||
}
|
||||
|
||||
Supervisor.child_spec(default, @opts)
|
||||
Supervisor.child_spec(default, unquote(Macro.escape(opts)))
|
||||
end
|
||||
|
||||
defoverridable child_spec: 1
|
||||
|
||||
@@ -2,9 +2,9 @@ defmodule Tuple do
|
||||
@moduledoc """
|
||||
Functions for working with tuples.
|
||||
|
||||
Tuples are ordered collections of elements. Tuples can contain elements
|
||||
of any type, and a tuple can contain elements of different types. Curly
|
||||
braces can be used to create tuples:
|
||||
Tuples are composite data types with a fixed number of elements. Tuples
|
||||
can contain elements of any type, and a tuple can contain elements of
|
||||
different types. Curly braces can be used to create tuples:
|
||||
|
||||
iex> {}
|
||||
{}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Guards
|
||||
|
||||
Guards are a way to augment pattern matching with more complex checks; they are allowed in a predefined set of constructs where pattern matching is allowed.
|
||||
Guards are a way to augment pattern matching with more complex checks. They are allowed in a predefined set of constructs where pattern matching is allowed.
|
||||
|
||||
Not all expressions are allowed in guard clauses, but only a handful of them. This is a deliberate choice. This way, Elixir (and Erlang) can make sure that nothing bad happens while executing guards and no mutations happen anywhere. It also allows the compiler to optimize the code related to guards efficiently.
|
||||
|
||||
## List of allowed expressions
|
||||
|
||||
@@ -100,6 +102,9 @@ In the example above, we show how guards can be used in function clauses. There
|
||||
end
|
||||
```
|
||||
|
||||
* custom guards can also be defined with `Kernel.defguard/1` and `Kernel.defguardp/1`.
|
||||
A custom guard is always defined based on existing guards.
|
||||
|
||||
Other constructs are `for`, `with`, `try`/`rescue`/`catch`/`else`/, and the `match?/2` macro in the `Kernel` module.
|
||||
|
||||
## Failing guards
|
||||
@@ -125,10 +130,6 @@ iex> case "hello" do
|
||||
|
||||
In many cases, we can take advantage of this. In the code above, we used `length/1` to both check that the given thing is a list *and* check some properties of its length (instead of using `is_list(something) and length(something) > 0`).
|
||||
|
||||
## Expressions in guard clauses
|
||||
|
||||
Not all expressions are allowed in guard clauses, but only a handful of them. This is a deliberate choice: only a predefined set of side-effect-free functions are allowed. This way, Elixir (and Erlang) can make sure that nothing bad happens while executing guards and no mutations happen anywhere. This behaviour is also coherent with pattern match, which is a naturally a side-effect-free operation. Finally, keeping expressions allowed in clauses to a close set of predefined ones allows the compiler to optimize the code related to choosing the right clause.
|
||||
|
||||
## Defining custom guard expressions
|
||||
|
||||
As mentioned before, only the expressions listed in this page are allowed in guards. However, we can take advantage of macros to write custom guards that can simplify our programs or make them more domain-specific. At the end of the day, what matters is that the *output* of the macros (which is what will be compiled) boils down to a combinations of the allowed expressions.
|
||||
@@ -165,6 +166,14 @@ def my_function(number) when is_even(number) do
|
||||
end
|
||||
```
|
||||
|
||||
While it's possible to create custom guards with macros, it's recommended to define them using `defguard` and `defguardp` which perform additional compile-time checks. Here's an example:
|
||||
|
||||
```elixir
|
||||
defmodule MyInteger do
|
||||
defguard is_even(value) when is_integer(value) and rem(value, 2) == 0
|
||||
end
|
||||
```
|
||||
|
||||
## Multiple guards in the same clause
|
||||
|
||||
There exists an additional way to simplify a chain of `or`s in guards: Elixir supports writing "multiple guards" in the same clause. This:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{application, elixir,
|
||||
[{description, "elixir"},
|
||||
{vsn, "1.6.0-rc.1"},
|
||||
{vsn, "1.6.1"},
|
||||
{modules, [
|
||||
elixir
|
||||
]},
|
||||
|
||||
@@ -3,20 +3,14 @@
|
||||
-include("elixir.hrl").
|
||||
|
||||
translate(Meta, Args, Return, S) ->
|
||||
{AccName, _, SA} = elixir_erl_var:build('_', S),
|
||||
{VarName, _, SV} = elixir_erl_var:build('_', SA),
|
||||
|
||||
Ann = ?ann(Meta),
|
||||
Acc = {var, Ann, AccName},
|
||||
Var = {var, Ann, VarName},
|
||||
|
||||
{Cases, [{do, Expr} | Opts]} = elixir_utils:split_last(Args),
|
||||
|
||||
{TInto, SI} =
|
||||
case lists:keyfind(into, 1, Opts) of
|
||||
{into, Into} -> elixir_erl_pass:translate(Into, SV);
|
||||
false when Return -> {{nil, Ann}, SV};
|
||||
false -> {false, SV}
|
||||
{into, Into} -> elixir_erl_pass:translate(Into, S);
|
||||
false when Return -> {{nil, Ann}, S};
|
||||
false -> {false, S}
|
||||
end,
|
||||
|
||||
TUniq = lists:keyfind(uniq, 1, Opts) == {uniq, true},
|
||||
@@ -27,9 +21,9 @@ translate(Meta, Args, Return, S) ->
|
||||
|
||||
case comprehension_expr(TInto, TExpr) of
|
||||
{inline, TIntoExpr} ->
|
||||
{build_inline(Ann, TCases, TIntoExpr, TInto, TUniq, Var, Acc, SF), SF};
|
||||
{build_inline(Ann, TCases, TIntoExpr, TInto, TUniq, SF), SF};
|
||||
{into, TIntoExpr} ->
|
||||
build_into(Ann, TCases, TIntoExpr, TInto, TUniq, Var, Acc, SF)
|
||||
build_into(Ann, TCases, TIntoExpr, TInto, TUniq, SF)
|
||||
end.
|
||||
|
||||
%% In case we have no return, we wrap the expression
|
||||
@@ -95,30 +89,61 @@ collect_filters([], Acc) ->
|
||||
{Acc, []}.
|
||||
|
||||
|
||||
build_inline(Ann, Clauses, Expr, Into, Uniq, _Var, Acc, S) ->
|
||||
build_inline(Ann, Clauses, Expr, Into, Uniq, S) ->
|
||||
case not Uniq and lists:all(fun(Clause) -> element(1, Clause) == bin end, Clauses) of
|
||||
true -> build_comprehension(Ann, Clauses, Expr, Into);
|
||||
false -> build_inline(Ann, Clauses, Expr, Into, Uniq, Acc, S)
|
||||
false -> build_inline_each(Ann, Clauses, Expr, Into, Uniq, S)
|
||||
end.
|
||||
|
||||
build_inline(Ann, Clauses, Expr, false, Uniq, Acc, S) ->
|
||||
build_inline_each(Ann, Clauses, Expr, false, Uniq, S) ->
|
||||
InnerFun = fun(InnerExpr, _InnerAcc) -> InnerExpr end,
|
||||
build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, Acc, S);
|
||||
build_inline(Ann, Clauses, Expr, {nil, _} = Into, Uniq, Acc, S) ->
|
||||
build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, S);
|
||||
build_inline_each(Ann, Clauses, Expr, {nil, _} = Into, Uniq, S) ->
|
||||
InnerFun = fun(InnerExpr, InnerAcc) -> {cons, Ann, InnerExpr, InnerAcc} end,
|
||||
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Into, Uniq, Acc, S),
|
||||
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Into, Uniq, S),
|
||||
elixir_erl:remote(Ann, lists, reverse, [ReduceExpr]);
|
||||
build_inline(Ann, Clauses, Expr, {bin, _, _} = Into, Uniq, Acc, S) ->
|
||||
InnerFun = fun(InnerExpr, InnerAcc) -> {cons, Ann, InnerAcc, InnerExpr} end,
|
||||
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Into, Uniq, Acc, S),
|
||||
build_inline_each(Ann, Clauses, Expr, {bin, _, []}, Uniq, S) ->
|
||||
{InnerValue, SV} = build_var(Ann, S),
|
||||
|
||||
InnerFun = fun(InnerExpr, InnerAcc) ->
|
||||
{'case', Ann, InnerExpr, [
|
||||
{clause, Ann,
|
||||
[InnerValue],
|
||||
[[elixir_erl:remote(Ann, erlang, is_binary, [InnerValue]),
|
||||
elixir_erl:remote(Ann, erlang, is_list, [InnerAcc])]],
|
||||
[{cons, Ann, InnerAcc, InnerValue}]},
|
||||
{clause, Ann,
|
||||
[InnerValue],
|
||||
[[elixir_erl:remote(Ann, erlang, is_bitstring, [InnerValue]),
|
||||
elixir_erl:remote(Ann, erlang, is_bitstring, [InnerAcc])]],
|
||||
[{bin, Ann, [
|
||||
{bin_element, Ann, InnerAcc, default, [bitstring]},
|
||||
{bin_element, Ann, InnerValue, default, [bitstring]}
|
||||
]}]},
|
||||
{clause, Ann,
|
||||
[InnerValue],
|
||||
[[elixir_erl:remote(Ann, erlang, is_bitstring, [InnerValue])]],
|
||||
[{bin, Ann, [
|
||||
{bin_element, Ann, elixir_erl:remote(Ann, erlang, iolist_to_binary, [InnerAcc]), default, [bitstring]},
|
||||
{bin_element, Ann, InnerValue, default, [bitstring]}
|
||||
]}]},
|
||||
{clause, Ann,
|
||||
[InnerValue],
|
||||
[],
|
||||
[elixir_erl:remote(Ann, erlang, error, [{tuple, Ann, [{atom, Ann, badarg}, InnerValue]}])]}
|
||||
]}
|
||||
end,
|
||||
|
||||
ReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, {nil, Ann}, Uniq, SV),
|
||||
elixir_erl:remote(Ann, erlang, iolist_to_binary, [ReduceExpr]).
|
||||
|
||||
build_into(Ann, Clauses, Expr, {map, _, []}, Uniq, _Var, Acc, S) ->
|
||||
ReduceExpr = build_inline(Ann, Clauses, Expr, {nil, Ann}, Uniq, Acc, S),
|
||||
build_into(Ann, Clauses, Expr, {map, _, []}, Uniq, S) ->
|
||||
ReduceExpr = build_inline_each(Ann, Clauses, Expr, {nil, Ann}, Uniq, S),
|
||||
{elixir_erl:remote(Ann, maps, from_list, [ReduceExpr]), S};
|
||||
|
||||
build_into(Ann, Clauses, Expr, Into, Uniq, Fun, Acc, S) ->
|
||||
{Kind, SK} = build_var(Ann, S),
|
||||
build_into(Ann, Clauses, Expr, Into, Uniq, S) ->
|
||||
{Fun, SF} = build_var(Ann, S),
|
||||
{Acc, SA} = build_var(Ann, SF),
|
||||
{Kind, SK} = build_var(Ann, SA),
|
||||
{Reason, SR} = build_var(Ann, SK),
|
||||
{Stack, ST} = build_var(Ann, SR),
|
||||
{Done, SD} = build_var(Ann, ST),
|
||||
@@ -132,7 +157,7 @@ build_into(Ann, Clauses, Expr, Into, Uniq, Fun, Acc, S) ->
|
||||
elixir_erl:remote(Ann, 'Elixir.Collectable', into, [Into])
|
||||
},
|
||||
|
||||
IntoReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Acc, Uniq, Acc, SD),
|
||||
IntoReduceExpr = build_reduce(Ann, Clauses, InnerFun, Expr, Acc, Uniq, SD),
|
||||
|
||||
TryExpr =
|
||||
{'try', Ann,
|
||||
@@ -153,12 +178,14 @@ build_into(Ann, Clauses, Expr, Into, Uniq, Fun, Acc, S) ->
|
||||
|
||||
%% Helpers
|
||||
|
||||
build_reduce(_Ann, Clauses, InnerFun, Expr, Into, false, Acc, S) ->
|
||||
build_reduce_each(Clauses, InnerFun(Expr, Acc), Into, Acc, S);
|
||||
build_reduce(Ann, Clauses, InnerFun, Expr, Into, true, Acc, S) ->
|
||||
build_reduce(Ann, Clauses, InnerFun, Expr, Into, false, S) ->
|
||||
{Acc, SA} = build_var(Ann, S),
|
||||
build_reduce_each(Clauses, InnerFun(Expr, Acc), Into, Acc, SA);
|
||||
build_reduce(Ann, Clauses, InnerFun, Expr, Into, true, S) ->
|
||||
%% Those variables are used only inside the anonymous function
|
||||
%% so we don't need to worry about returning the scope.
|
||||
{Value, SV} = build_var(Ann, S),
|
||||
{Acc, SA} = build_var(Ann, S),
|
||||
{Value, SV} = build_var(Ann, SA),
|
||||
{IntoAcc, SI} = build_var(Ann, SV),
|
||||
{UniqAcc, SU} = build_var(Ann, SI),
|
||||
|
||||
@@ -302,12 +329,12 @@ join_filter(Ann, {nil, Filter}, True, False) ->
|
||||
{clause, Ann, [{atom, Ann, false}], [], [False]}
|
||||
]};
|
||||
join_filter(Ann, {Var, Filter}, True, False) ->
|
||||
Guard =
|
||||
{op, Ann, 'orelse',
|
||||
{op, Ann, '==', Var, {atom, Ann, false}},
|
||||
{op, Ann, '==', Var, {atom, Ann, nil}}},
|
||||
Guards = [
|
||||
[{op, Ann, '==', Var, {atom, Ann, false}}],
|
||||
[{op, Ann, '==', Var, {atom, Ann, nil}}]
|
||||
],
|
||||
|
||||
{'case', Ann, Filter, [
|
||||
{clause, Ann, [Var], [[Guard]], [False]},
|
||||
{clause, Ann, [Var], Guards, [False]},
|
||||
{clause, Ann, [{var, Ann, '_'}], [], [True]}
|
||||
]}.
|
||||
|
||||
@@ -688,7 +688,13 @@ build_op({_Kind, Location, 'in'}, {UOp, _, [Left]}, Right) when ?rearrange_uop(U
|
||||
{UOp, meta_from_location(Location), [{'in', meta_from_location(Location), [Left, Right]}]};
|
||||
|
||||
build_op({_Kind, Location, 'not in'}, Left, Right) ->
|
||||
{'not', meta_from_location(Location), [{'in', meta_from_location(Location), [Left, Right]}]};
|
||||
InMeta = meta_from_location(Location),
|
||||
NotMeta =
|
||||
case ?formatter_metadata() of
|
||||
true -> [{operator, 'not in'} | InMeta];
|
||||
false -> InMeta
|
||||
end,
|
||||
{'not', NotMeta, [{'in', InMeta, [Left, Right]}]};
|
||||
build_op({_Kind, Location, Op}, Left, Right) ->
|
||||
{Op, eol_op(Location) ++ meta_from_location(Location), [Left, Right]}.
|
||||
|
||||
|
||||
@@ -473,6 +473,10 @@ tokenize("\r\n" ++ Rest, Line, Column, Scope, Tokens) ->
|
||||
|
||||
% Others
|
||||
|
||||
tokenize([$%, $[ | Rest], Line, _Column, _Scope, Tokens) ->
|
||||
Reason = {Line, "expected %{ to define a map, got: ", [$%, $[]},
|
||||
{error, Reason, Rest, Tokens};
|
||||
|
||||
tokenize([$%, ${ | T], Line, Column, Scope, Tokens) ->
|
||||
tokenize([${ | T], Line, Column + 1, Scope, [{'%{}', {Line, Column, nil}} | Tokens]);
|
||||
|
||||
|
||||
@@ -345,7 +345,7 @@ defmodule Code.Formatter.IntegrationTest do
|
||||
"""
|
||||
end
|
||||
|
||||
test "no parens keywords right on line limit" do
|
||||
test "no parens keywords at the end of the line" do
|
||||
bad = """
|
||||
defmodule Mod do
|
||||
defp token_list_downcase(<<char, rest::binary>>, acc) when is_whitespace(char) or is_comma(char), do: token_list_downcase(rest, acc)
|
||||
@@ -382,6 +382,25 @@ defmodule Code.Formatter.IntegrationTest do
|
||||
assert_format bad, good, line_length: 18
|
||||
end
|
||||
|
||||
test "do at the end of the line with single argument" do
|
||||
bad = """
|
||||
defmodule Location do
|
||||
def new(line, column) when is_integer(line) and line >= 0 and is_integer(column) and column >= 0 do
|
||||
%{column: column, line: line}
|
||||
end
|
||||
end
|
||||
"""
|
||||
|
||||
assert_format bad, """
|
||||
defmodule Location do
|
||||
def new(line, column)
|
||||
when is_integer(line) and line >= 0 and is_integer(column) and column >= 0 do
|
||||
%{column: column, line: line}
|
||||
end
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
test "tuples as trees" do
|
||||
bad = """
|
||||
@document Parser.parse(
|
||||
|
||||
@@ -121,7 +121,13 @@ defmodule Code.Formatter.OperatorsTest do
|
||||
end
|
||||
|
||||
test "not in" do
|
||||
assert_format "not(foo in bar)", "foo not in bar"
|
||||
assert_format "not foo in bar", "not (foo in bar)"
|
||||
assert_format "not foo in bar", "foo not in bar", rename_deprecated_at: "1.5.0"
|
||||
|
||||
assert_format "not(foo in bar)", "not (foo in bar)"
|
||||
assert_format "not(foo in bar)", "foo not in bar", rename_deprecated_at: "1.5.0"
|
||||
|
||||
assert_same "foo not in bar"
|
||||
assert_same "(not foo) in bar"
|
||||
assert_same "(!foo) in bar"
|
||||
end
|
||||
|
||||
@@ -9,6 +9,12 @@ defmodule DynamicSupervisorTest do
|
||||
def init(args), do: args
|
||||
end
|
||||
|
||||
test "can be supervised directly" do
|
||||
children = [{DynamicSupervisor, strategy: :one_for_one, name: :dyn_sup_spec_test}]
|
||||
assert {:ok, _} = Supervisor.start_link(children, strategy: :one_for_one)
|
||||
assert DynamicSupervisor.which_children(:dyn_sup_spec_test) == []
|
||||
end
|
||||
|
||||
describe "use/2" do
|
||||
test "generates child_spec/1" do
|
||||
assert Simple.child_spec([:hello]) == %{
|
||||
|
||||
@@ -878,10 +878,7 @@ defmodule EnumTest do
|
||||
assert Enum.zip([[1]]) == [{1}]
|
||||
|
||||
assert Enum.zip([[], [], [], []]) == []
|
||||
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
Enum.zip(%{})
|
||||
end
|
||||
assert Enum.zip(%{}) == []
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -143,8 +143,38 @@ defmodule Kernel.ComprehensionTest do
|
||||
end
|
||||
|
||||
test "for comprehensions into binary" do
|
||||
enum = 1..3
|
||||
assert for(x <- enum, into: "", do: to_bin(x * 2)) == <<2, 4, 6>>
|
||||
enum = 0..3
|
||||
|
||||
assert (for x <- enum, into: "" do
|
||||
to_bin(x * 2)
|
||||
end) == <<0, 2, 4, 6>>
|
||||
|
||||
assert (for x <- enum, into: "" do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
end
|
||||
|
||||
test "for comprehensions into dynamic binary" do
|
||||
enum = 0..3
|
||||
into = ""
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
to_bin(x * 2)
|
||||
end) == <<0, 2, 4, 6>>
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
|
||||
into = <<7::size(1)>>
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
to_bin(x * 2)
|
||||
end) == <<7::size(1), 0, 2, 4, 6>>
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<7::size(1), 0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
end
|
||||
|
||||
test "for comprehensions where value is not used" do
|
||||
@@ -238,9 +268,39 @@ defmodule Kernel.ComprehensionTest do
|
||||
assert for(x <- enum, into: [], do: x * 2) == [2, 4, 6]
|
||||
end
|
||||
|
||||
test "list for comprehensions into binaries" do
|
||||
enum = [1, 2, 3]
|
||||
assert for(x <- enum, into: "", do: to_bin(x * 2)) == <<2, 4, 6>>
|
||||
test "list for comprehensions into binary" do
|
||||
enum = [0, 1, 2, 3]
|
||||
|
||||
assert (for x <- enum, into: "" do
|
||||
to_bin(x * 2)
|
||||
end) == <<0, 2, 4, 6>>
|
||||
|
||||
assert (for x <- enum, into: "" do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
end
|
||||
|
||||
test "list for comprehensions into dynamic binary" do
|
||||
enum = [0, 1, 2, 3]
|
||||
into = ""
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
to_bin(x * 2)
|
||||
end) == <<0, 2, 4, 6>>
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
|
||||
into = <<7::size(1)>>
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
to_bin(x * 2)
|
||||
end) == <<7::size(1), 0, 2, 4, 6>>
|
||||
|
||||
assert (for x <- enum, into: into do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<7::size(1), 0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
end
|
||||
|
||||
test "map for comprehensions into map" do
|
||||
@@ -278,9 +338,39 @@ defmodule Kernel.ComprehensionTest do
|
||||
assert for(<<x <- bin>>, into: [], do: x * 2) == [2, 4, 6]
|
||||
end
|
||||
|
||||
test "binary for comprehensions into binaries" do
|
||||
bin = <<1, 2, 3>>
|
||||
assert for(<<x <- bin>>, into: "", do: to_bin(x * 2)) == <<2, 4, 6>>
|
||||
test "binary for comprehensions into binary" do
|
||||
bin = <<0, 1, 2, 3>>
|
||||
|
||||
assert (for <<x <- bin>>, into: "" do
|
||||
to_bin(x * 2)
|
||||
end) == <<0, 2, 4, 6>>
|
||||
|
||||
assert (for <<x <- bin>>, into: "" do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
end
|
||||
|
||||
test "binary for comprehensions into dynamic binary" do
|
||||
bin = <<0, 1, 2, 3>>
|
||||
into = ""
|
||||
|
||||
assert (for <<x <- bin>>, into: into do
|
||||
to_bin(x * 2)
|
||||
end) == <<0, 2, 4, 6>>
|
||||
|
||||
assert (for <<x <- bin>>, into: into do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
|
||||
into = <<7::size(1)>>
|
||||
|
||||
assert (for <<x <- bin>>, into: into do
|
||||
to_bin(x * 2)
|
||||
end) == <<7::size(1), 0, 2, 4, 6>>
|
||||
|
||||
assert (for <<x <- bin>>, into: into do
|
||||
if Integer.is_even(x), do: <<x::size(2)>>, else: <<x::size(1)>>
|
||||
end) == <<7::size(1), 0::size(2), 1::size(1), 2::size(2), 3::size(1)>>
|
||||
end
|
||||
|
||||
test "binary for comprehensions with variable size" do
|
||||
|
||||
@@ -108,6 +108,12 @@ defmodule Kernel.ErrorsTest do
|
||||
assert_eval_raise SyntaxError, msg, "foo:+1"
|
||||
end
|
||||
|
||||
test "invalid map start" do
|
||||
assert_eval_raise SyntaxError,
|
||||
"nofile:1: expected %{ to define a map, got: %[",
|
||||
"{:ok, %[], %{}}"
|
||||
end
|
||||
|
||||
test "sigil terminator" do
|
||||
assert_eval_raise TokenMissingError,
|
||||
"nofile:3: missing terminator: \" (for sigil ~r\" starting at line 1)",
|
||||
|
||||
@@ -833,7 +833,7 @@ defmodule Kernel.WarningTest do
|
||||
@behaviour UndefinedBehaviour
|
||||
end
|
||||
""")
|
||||
end) =~ "module UndefinedBehaviour does not exist (in module Sample)"
|
||||
end) =~ "@behaviour UndefinedBehaviour does not exist (in module Sample)"
|
||||
after
|
||||
purge(Sample)
|
||||
end
|
||||
|
||||
@@ -412,6 +412,45 @@ defmodule MacroTest do
|
||||
assert Macro.to_string(quoted) <> "\n" == expected
|
||||
end
|
||||
|
||||
test "try" do
|
||||
quoted =
|
||||
quote do
|
||||
try do
|
||||
foo
|
||||
catch
|
||||
_, _ ->
|
||||
2
|
||||
rescue
|
||||
ArgumentError ->
|
||||
1
|
||||
after
|
||||
4
|
||||
else
|
||||
_ ->
|
||||
3
|
||||
end
|
||||
end
|
||||
|
||||
expected = """
|
||||
try() do
|
||||
foo
|
||||
rescue
|
||||
ArgumentError ->
|
||||
1
|
||||
catch
|
||||
_, _ ->
|
||||
2
|
||||
else
|
||||
_ ->
|
||||
3
|
||||
after
|
||||
4
|
||||
end
|
||||
"""
|
||||
|
||||
assert Macro.to_string(quoted) <> "\n" == expected
|
||||
end
|
||||
|
||||
test "fn" do
|
||||
assert Macro.to_string(quote(do: fn -> 1 + 2 end)) == "fn -> 1 + 2 end"
|
||||
assert Macro.to_string(quote(do: fn x -> x + 1 end)) == "fn x -> x + 1 end"
|
||||
|
||||
@@ -1099,10 +1099,8 @@ defmodule StreamTest do
|
||||
assert Stream.zip([concat, cycle]) |> Enum.to_list() ==
|
||||
[{1, :a}, {2, :b}, {3, :c}, {4, :a}, {5, :b}, {6, :c}]
|
||||
|
||||
assert_raise FunctionClauseError, fn ->
|
||||
enum_of_enums = Stream.cycle([[1, 2], [:a, :b]])
|
||||
Stream.zip(enum_of_enums)
|
||||
end
|
||||
assert Stream.chunk_every([0, 1, 2, 3], 2) |> Stream.zip() |> Enum.to_list() ==
|
||||
[{0, 2}, {1, 3}]
|
||||
end
|
||||
|
||||
test "zip/1 does not leave streams suspended" do
|
||||
|
||||
@@ -181,22 +181,22 @@ defmodule ExUnit.Runner do
|
||||
end
|
||||
|
||||
defp exec_module_setup(%ExUnit.TestModule{name: module} = test_module) do
|
||||
{:ok, test_module, module.__ex_unit__(:setup_all, %{module: module})}
|
||||
{:ok, test_module, module.__ex_unit__(:setup_all, %{module: module, case: module})}
|
||||
catch
|
||||
kind, error ->
|
||||
failed = failed(kind, error, pruned_stacktrace())
|
||||
{:error, %{test_module | state: failed}}
|
||||
end
|
||||
|
||||
defp run_test(true, config, test, context) do
|
||||
run_test([], config, test, context)
|
||||
defp run_test_with_capture_log(true, config, test, context) do
|
||||
run_test_with_capture_log([], config, test, context)
|
||||
end
|
||||
|
||||
defp run_test(false, config, test, context) do
|
||||
defp run_test_with_capture_log(false, config, test, context) do
|
||||
spawn_test(config, test, context)
|
||||
end
|
||||
|
||||
defp run_test(opts, config, test, context) do
|
||||
defp run_test_with_capture_log(opts, config, test, context) do
|
||||
ref = make_ref()
|
||||
|
||||
try do
|
||||
@@ -224,7 +224,7 @@ defmodule ExUnit.Runner do
|
||||
test =
|
||||
if is_nil(test.state) do
|
||||
capture_log? = Map.get(tags, :capture_log, config.capture_log)
|
||||
run_test(capture_log?, config, test, Map.merge(tags, context))
|
||||
run_test_with_capture_log(capture_log?, config, test, Map.merge(tags, context))
|
||||
else
|
||||
test
|
||||
end
|
||||
|
||||
@@ -21,6 +21,7 @@ defmodule ExUnit.CaseTest do
|
||||
test "tags", context do
|
||||
line = __ENV__.line - 1
|
||||
assert context[:module] == __MODULE__
|
||||
assert context[:case] == __MODULE__
|
||||
assert context[:test] == __ENV__.function |> elem(0)
|
||||
assert context[:line] == line
|
||||
assert context[:async] == true
|
||||
|
||||
@@ -14,8 +14,8 @@ defmodule IEx.Evaluator do
|
||||
old_leader = Process.group_leader()
|
||||
Process.group_leader(self(), leader)
|
||||
|
||||
evaluator? = !!Process.get(:iex_evaluator)
|
||||
Process.put(:iex_evaluator, true)
|
||||
evaluator = Process.get(:iex_evaluator)
|
||||
Process.put(:iex_evaluator, command)
|
||||
|
||||
state = loop_state(server, IEx.History.init(), opts)
|
||||
command == :ack && :proc_lib.init_ack(self())
|
||||
@@ -25,11 +25,16 @@ defmodule IEx.Evaluator do
|
||||
after
|
||||
Process.group_leader(self(), old_leader)
|
||||
|
||||
# If there was an evaluator, nest failures.
|
||||
if evaluator? do
|
||||
send(self(), {:done, server})
|
||||
else
|
||||
Process.delete(:iex_evaluator)
|
||||
cond do
|
||||
is_nil(evaluator) ->
|
||||
Process.delete(:iex_evaluator)
|
||||
|
||||
evaluator != :ack ->
|
||||
# Ensure propagation to non-root level evaluators
|
||||
send(self(), {:done, server})
|
||||
|
||||
true ->
|
||||
:ok
|
||||
end
|
||||
|
||||
:ok
|
||||
|
||||
@@ -605,7 +605,8 @@ defmodule Logger do
|
||||
"""
|
||||
@spec bare_log(level, message | (() -> message | {message, keyword}), keyword) ::
|
||||
:ok | {:error, :noproc} | {:error, term}
|
||||
def bare_log(level, chardata_or_fun, metadata \\ []) when level in @levels and is_list(metadata) do
|
||||
def bare_log(level, chardata_or_fun, metadata \\ [])
|
||||
when level in @levels and is_list(metadata) do
|
||||
case __metadata__() do
|
||||
{true, pdict} ->
|
||||
%{mode: mode, truncate: truncate, level: min_level, utc_log: utc_log?} =
|
||||
|
||||
Reference in New Issue
Block a user