Compare commits

...
14 Commits
Author SHA1 Message Date
José Valim 7340ca2d92 Release v1.5.3 2017-12-19 18:12:48 +01:00
José Valim 3beb72e6f4 Update CHANGELOG 2017-12-15 11:16:33 +01:00
José Valim 45f9c9c1ce Add upcoming attributes to no warn list 2017-12-15 10:27:56 +01:00
José Valim cde12f75fc Also consider when acc_count is more than count
Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2017-12-15 10:27:19 +01:00
Alberto Almagro Sotelo eb7e0984bd Fix Stream.chunk_every/4 odd results when step > count (#7114)
Stream.chunk_every/4 was giving odd results when step was greater than
count and the last element of the enumerable was the same as last
chunk's last element.

This commit fixes this issue and adds tests for different scenarios when
step > count. It also adds a doctest example to reflect this in the
documentation and test it simultaneously.

Fixes #7096

Signed-off-by: José Valim <jose.valim@plataformatec.com.br>
2017-12-15 10:26:58 +01:00
José Valim 57a52b9a05 Remove leftover space 2017-12-12 09:43:16 +01:00
José Valim c352766f43 Update CHANGELOG.md 2017-12-12 09:42:59 +01:00
Aleksei Magusev 15222304eb Consider microseconds in maximum possible Calendar.ISO datetime (#7055) 2017-11-23 09:13:13 +01:00
Glauber Campinho f6aced3072 Remove dialyzer warnings from else in with clauses (#7031)
Add generated: true to guard clauses from else in with clauses

Resolves #6426
2017-11-15 17:55:41 -02:00
Glauber Campinho 5e5eaca78f Warn duplicate definitions in macros (#7037)
Closes #6637
2017-11-15 17:55:16 -02:00
Aleksei Magusev ab61c09b3d Fix compiler warnings in ExUnit.assert_receive/3
Closes #6429.
2017-10-07 23:07:42 +02:00
Aleksei Magusev ccf939e9e7 Fix typo in MapSetTest 2017-10-03 17:39:21 -07:00
Shane Howley 331a70ade9 Return valid MapSet when union-ing a legacy MapSet (#6534) 2017-10-03 17:32:24 -07:00
Scott Riley 4329429823 Fix typo/reference in Supervisor @moduledoc (#6622)
All examples leading up to this snippet have used the module name
`Stack`, this updates the snippet to use the same.
2017-10-01 18:44:42 +02:00
18 changed files with 196 additions and 39 deletions
+28 -1
View File
@@ -14,7 +14,17 @@ Or:
saudação = "Bom dia!"
Elixir follows the recommendations in [Unicode Annex #31](http://unicode.org/reports/tr31/) to make the language more accessible to other languages and communities. Identifiers must still be a sequence of letters, followed by digits and combining marks. This means symbols, such as mathematical notations and emoji, are not allowed identifiers.
Elixir follows the recommendations in [Unicode Annex #31](http://unicode.org/reports/tr31/) to make the language more accessible to other languages and communities. Identifiers must start with a letter or underscore, optionally followed by letters, digits, and underscores. Here letter means any UTF-8 letter-character (optionally with a combining mark) and digit means a UTF-8 decimal-digit-character. If you're using ASCII, this does what you'd expect.
Examples of valid variables are:
name josé _age まつもと _42 адрес
Examples of invalid variables include:
name• a±2 42
Symbols, such as mathematical notations and emoji, are not allowed identifiers.
For a complete reference on Elixir syntax, see the [Syntax Reference](https://hexdocs.pm/elixir/syntax-reference.html). For technical details on Unicode support, see [Unicode Syntax](https://hexdocs.pm/elixir/unicode-syntax.html).
@@ -156,6 +166,23 @@ Overall, using `@impl` has the following advantages:
This release brings further improvements to Calendar types. It adds arithmetic and others functions to `Time`, `Date`, `NaiveDateTime` and `Datetime` as well as conversion between different calendars.
## v1.5.3 (2017-12-19)
### 1. Bug fixes
#### Elixir
* [Calendar] Consider microseconds in maximum possible Calendar.ISO datetime
* [Enum] Fix `chunk_every/4` when `step > count`
* [Kernel] Warn duplicate definitions in macros
* [Kernel] Remove dialyzer warnings from else in with clauses
* [Kernel] Do not warning on upcoming `@deprecated` and `@since` attributes for v1.6
* [MapSet] Return valid MapSet when union-ing a legacy MapSet
#### ExUnit
* [ExUnit] Fix compiler warnings in `assert_receive/3`
## v1.5.2 (2017-09-29)
### 1. Enhacements
+1 -1
View File
@@ -1 +1 @@
1.5.2
1.5.3
+1 -1
View File
@@ -17,7 +17,7 @@ defmodule Calendar.ISO do
@unix_epoch 62167219200
@unix_start 1_000_000 * -@unix_epoch
@unix_end 1_000_000 * (315569519999 - @unix_epoch)
@unix_end 315569519999999999 - @unix_epoch * 1_000_000
@unix_range_microseconds @unix_start..@unix_end
@type year :: 0..9999
+3
View File
@@ -362,6 +362,9 @@ defmodule Enum do
iex> Enum.chunk_every([1, 2, 3, 4], 10)
[[1, 2, 3, 4]]
iex> Enum.chunk_every([1, 2, 3, 4, 5], 2, 3, [])
[[1, 2], [4, 5]]
"""
@spec chunk_every(t, pos_integer, pos_integer, t | :discard) :: [list]
def chunk_every(enumerable, count, step, leftover \\ [])
+2 -1
View File
@@ -353,7 +353,8 @@ defmodule MapSet do
%{map_set | map: Map.merge(map1, map2)}
end
def union(%MapSet{map: map1}, %MapSet{map: map2}) do
new_from_list(Map.keys(map1) ++ Map.keys(map2), [])
map = new_from_list(Map.keys(map1) ++ Map.keys(map2), [])
%MapSet{map: map}
end
defp order_by_size(map1, map2) when map_size(map1) > map_size(map2), do: {map2, map1}
+1 -1
View File
@@ -22,7 +22,7 @@ defmodule Stream.Reducers do
{:cont, new_state}
end
end, fn {acc_buffer, acc_count} ->
if leftover == :discard or acc_count == 0 do
if leftover == :discard or acc_count == 0 or (step > count and acc_count >= count) do
{:cont, []}
else
{:cont, :lists.reverse(acc_buffer, Enum.take(leftover, count - acc_count)), []}
+1 -1
View File
@@ -82,7 +82,7 @@ defmodule Supervisor do
supervisor will automatically start a new one, with the initial stack
of `[:hello]`:
GenServer.call(MyStack, :pop)
GenServer.call(Stack, :pop)
#=> :hello
Supervisors support different strategies; in the example above, we
+4 -1
View File
@@ -96,6 +96,10 @@ store_definition(Kind, CheckClauses, Call, Body, Pos) ->
%% extract meta information like file and context.
{_, Meta, _} = Call,
DoCheckClauses = (not lists:keymember(context, 1, Meta)) andalso (CheckClauses),
Generated = case lists:keyfind(generated, 1, Meta) of
{generated, true} -> ?generated([]);
_ -> []
end,
%% Check if there is a file information in the definition.
%% If so, we assume this come from another source and
@@ -114,7 +118,6 @@ store_definition(Kind, CheckClauses, Call, Body, Pos) ->
LinifyArgs = elixir_quote:linify(Line, Key, Args),
LinifyGuards = elixir_quote:linify(Line, Key, Guards),
LinifyBody = elixir_quote:linify(Line, Key, Body),
Generated = case DoCheckClauses of true -> []; false -> ?generated([]) end,
{EL, MetaLocation} =
case retrieve_location(Location, ?key(E, module)) of
+21 -1
View File
@@ -371,11 +371,31 @@ translate_with_else(Meta, [{else, Else}], S) ->
RaiseExpr = {{'.', Generated, [erlang, error]}, Generated, [{with_clause, RaiseVar}]},
RaiseClause = {'->', Generated, [[RaiseVar], RaiseExpr]},
GeneratedElse = [{'->', ?generated(ElseMeta), ElseArgs} || {'->', ElseMeta, ElseArgs} <- Else],
GeneratedElse = [build_generated_clause(Generated, ElseClause) || ElseClause <- Else],
Case = {'case', [{export_vars, false} | Generated], [ElseVarEx, [{do, GeneratedElse ++ [RaiseClause]}]]},
{TranslatedCase, SC} = elixir_erl_pass:translate(Case, SV),
{{clause, ?ann(Generated), [ElseVarErl], [], [TranslatedCase]}, SC}.
build_generated_clause(Generated, {'->', _, [Args, Clause]}) ->
NewArgs = [build_generated_clause_arg(Generated, Arg) || Arg <- Args],
{'->', Generated, [NewArgs, Clause]}.
build_generated_clause_arg(Generated, Arg) ->
{Expr, Guards} = elixir_utils:extract_guards(Arg),
NewGuards = [build_generated_guard(Generated, Guard) || Guard <- Guards],
concat_guards(Generated, Expr, NewGuards).
build_generated_guard(Generated, {{'.', _, _} = Call, _, Args}) ->
{Call, Generated, [build_generated_guard(Generated, Arg) || Arg <- Args]};
build_generated_guard(_, Expr) ->
Expr.
concat_guards(_Meta, Expr, []) ->
Expr;
concat_guards(Meta, Expr, [Guard | Tail]) ->
{'when', Meta, [Expr, concat_guards(Meta, Guard, Tail)]}.
translate_with_do([{'<-', Meta, [Left, Expr]} | Rest], Do, Else, S) ->
{Args, Guards} = elixir_utils:extract_guards(Left),
{TExpr, SR} = elixir_erl_pass:translate(Expr, S),
+1 -1
View File
@@ -318,7 +318,7 @@ beam_location(#{lexical_tracker := Pid, module := Module}) ->
%% Handle unused attributes warnings and special cases.
warn_unused_attributes(File, Data, PersistedAttrs) ->
ReservedAttrs = [after_compile, before_compile, moduledoc, on_definition | PersistedAttrs],
ReservedAttrs = [after_compile, before_compile, deprecated, moduledoc, on_definition, since | PersistedAttrs],
Keys = ets:select(Data, [{{'$1', '_', '_', '$2'}, [{is_atom, '$1'}, {is_integer, '$2'}], [['$1', '$2']]}]),
[elixir_errors:form_warn([{line, Line}], File, ?MODULE, {unused_attribute, Key}) ||
[Key, Line] <- Keys, not lists:member(Key, ReservedAttrs)].
+2 -2
View File
@@ -230,12 +230,12 @@ defmodule DateTimeTest do
assert DateTime.from_unix(-62167219201) == {:error, :invalid_unix_time}
max_datetime = %DateTime{
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {0, 0},
calendar: Calendar.ISO, day: 31, hour: 23, microsecond: {999999, 6},
minute: 59, month: 12, second: 59, std_offset: 0, time_zone: "Etc/UTC",
utc_offset: 0, year: 9999, zone_abbr: "UTC"
}
assert DateTime.from_unix(253402300799) == {:ok, max_datetime}
assert DateTime.from_unix(253402300799999999, :microsecond) == {:ok, max_datetime}
assert DateTime.from_unix(253402300800) == {:error, :invalid_unix_time}
minus_datetime = %DateTime{
+5
View File
@@ -70,6 +70,11 @@ defmodule EnumTest do
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 2, []) == [[1, 2, 3], [3, 4, 5], [5, 6]]
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 3, 3, []) == [[1, 2, 3], [4, 5, 6]]
assert Enum.chunk_every([1, 2, 3, 4, 5], 4, 4, 6..10) == [[1, 2, 3, 4], [5, 6, 7, 8]]
assert Enum.chunk_every([1, 2, 3, 4, 5], 2, 3, []) == [[1, 2], [4, 5]]
assert Enum.chunk_every([1, 2, 3, 4, 5, 6], 2, 3, []) == [[1, 2], [4, 5]]
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 3, []) == [[1, 2], [4, 5], [7]]
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 3, [8]) == [[1, 2], [4, 5], [7, 8]]
assert Enum.chunk_every([1, 2, 3, 4, 5, 6, 7], 2, 4, []) == [[1, 2], [5, 6]]
end
test "chunk_by/2" do
@@ -1,13 +1,25 @@
defmodule Dialyzer.With do
def with_else do
with :ok <- ok_or_error(),
:ok <- ok_or_other_error() do
:ok <- ok_or_other_error(),
:ok <- ok_or_tuple_error(),
:ok <- ok_or_tuple_list_error() do
:ok
else
:error ->
:error
:other_error ->
:other_error
{:error, msg} when is_list(msg) or is_tuple(msg) ->
:error
{:error, msg} when is_list(msg) when is_tuple(msg) ->
:error
{:error, _msg} ->
:error
end
end
@@ -20,4 +32,14 @@ defmodule Dialyzer.With do
defp ok_or_other_error do
Enum.random([:ok, :other_error])
end
@spec ok_or_tuple_error() :: :ok | {:error, :err}
defp ok_or_tuple_error do
Enum.random([:ok, {:error, :err}])
end
@spec ok_or_tuple_list_error() :: :ok | {:error, [:err]}
defp ok_or_tuple_list_error do
Enum.random([:ok, {:error, [:err]}])
end
end
@@ -487,6 +487,27 @@ defmodule Kernel.WarningTest do
purge Sample
end
test "generated clause not match" do
assert capture_err(fn ->
Code.eval_string("""
defmodule Sample do
defmacro __using__(_) do
quote do
def hello, do: nil
def hello, do: nil
end
end
end
defmodule UseSample do
use Sample
end
""")
end) =~ "this clause cannot match because a previous clause at line 10 always matches"
after
purge(Sample)
purge(UseSample)
end
test "clause with defaults should be first" do
assert capture_err(fn ->
Code.eval_string ~S"""
+34
View File
@@ -108,4 +108,38 @@ defmodule MapSetTest do
list = MapSet.to_list(MapSet.new(5..120))
assert Enum.sort(list) == Enum.to_list(5..120)
end
test "MapSet v1 compatibility" do
result = 1..5 |> map_set_v1() |> MapSet.new()
assert MapSet.equal?(result, MapSet.new(1..5))
result = MapSet.put(map_set_v1(1..5), 6)
assert MapSet.equal?(result, MapSet.new(1..6))
result = MapSet.union(map_set_v1(1..5), MapSet.new(6..10))
assert MapSet.equal?(result, MapSet.new(1..10))
result = MapSet.intersection(map_set_v1(1..10), MapSet.new(6..15))
assert MapSet.equal?(result, MapSet.new(6..10))
result = MapSet.difference(map_set_v1(1..10), MapSet.new(6..50))
assert MapSet.equal?(result, MapSet.new(1..5))
result = MapSet.delete(map_set_v1(1..10), 1)
assert MapSet.equal?(result, MapSet.new(2..10))
assert MapSet.size(map_set_v1(1..5)) == 5
assert MapSet.to_list(map_set_v1(1..5)) == Enum.to_list(1..5)
assert MapSet.disjoint?(map_set_v1(1..5), MapSet.new(10..15))
refute MapSet.disjoint?(map_set_v1(1..5), MapSet.new(5..10))
assert MapSet.subset?(map_set_v1(3..7), MapSet.new(1..10))
refute MapSet.subset?(map_set_v1(7..12), MapSet.new(1..10))
end
defp map_set_v1(enumerable) do
map = Map.new(enumerable, &{&1, true})
%{__struct__: MapSet, map: map}
end
end
+40 -25
View File
@@ -109,7 +109,7 @@ defmodule ExUnit.Assertions do
# is not nil nor false. We need to rewrite the if
# to avoid silly warnings though.
return =
no_warning(quote do
suppress_warning(quote do
case right do
x when x in [nil, false] ->
raise ExUnit.AssertionError,
@@ -121,7 +121,7 @@ defmodule ExUnit.Assertions do
end)
match_expr =
no_warning(quote do
suppress_warning(quote do
case right do
unquote(left) ->
unquote(return)
@@ -401,6 +401,40 @@ defmodule ExUnit.Assertions do
quote(do: unquote(left) = received)
end
pattern_finder =
quote do
fn message ->
unquote(suppress_warning(quote do
case message do
unquote(pattern) ->
_ = unquote(vars)
true
_ ->
false
end
end))
end
end
failure_message_hit =
failure_message || quote do
"""
Found message matching #{unquote(binary)} after #{timeout}ms.
This means the message was delivered too close to the timeout value, you may want to either:
1. Give an increased timeout to `assert_receive/2`
2. Increase the default timeout to all `assert_receive` in your
test_helper.exs by setting ExUnit.configure(assert_receive_timeout: ...)
"""
end
failure_message_miss =
failure_message || quote do
"No message matching #{unquote(binary)} after #{timeout}ms."
end
quote do
timeout = unquote(timeout)
@@ -412,29 +446,10 @@ defmodule ExUnit.Assertions do
timeout ->
{:messages, messages} = Process.info(self(), :messages)
pattern_finder = fn message ->
case message do
unquote(pattern) ->
_ = unquote(vars)
true
_ ->
false
end
end
if Enum.any?(messages, pattern_finder) do
flunk(unquote(failure_message) || """
Found message matching #{unquote(binary)} after #{timeout}ms.
This means the message was delivered too close to the timeout value, you may want to either:
1. Give an increased timeout to `assert_receive/2`
2. Increase the default timeout to all `assert_receive` in your
test_helper.exs by setting ExUnit.configure(assert_receive_timeout: ...)
""")
if Enum.any?(messages, unquote(pattern_finder)) do
flunk(unquote(failure_message_hit))
else
failure_message = unquote(failure_message) || "No message matching #{unquote(binary)} after #{timeout}ms."
flunk(failure_message <>
flunk(unquote(failure_message_miss) <>
ExUnit.Assertions.__pins__(unquote(pins)) <>
ExUnit.Assertions.__mailbox__(messages))
end
@@ -515,7 +530,7 @@ defmodule ExUnit.Assertions do
|> elem(1)
end
defp no_warning({name, meta, [expr, [do: clauses]]}) do
defp suppress_warning({name, meta, [expr, [do: clauses]]}) do
clauses = Enum.map clauses, fn {:->, meta, args} ->
{:->, [generated: true] ++ meta, args}
end
+7 -1
View File
@@ -179,6 +179,12 @@ defmodule ExUnit.AssertionsTest do
:hello = assert_receive :hello
end
test "assert receive accepts custom failure message" do
send self(), :hello
assert_receive message, 0, "failure message"
:hello = message
end
test "assert receive with message in mailbox after timeout, but before reading mailbox tells user to increase timeout" do
parent = self()
# This is testing a race condition, so it's not
@@ -297,7 +303,7 @@ defmodule ExUnit.AssertionsTest do
end
end
test "assert received leaks" do
test "assert received binds variables" do
send self(), {:hello, :world}
assert_received {:hello, world}
:world = world
+1 -1
View File
@@ -1,6 +1,6 @@
{application, elixir,
[{description, "elixir"},
{vsn, "1.5.2"},
{vsn, "1.5.3"},
{modules, [
elixir
]},