Fix Range validation

- checks for lists when defining range
- replace (left .. right) with (first .. last) in error messages
  to use the same terminology used in the function definition
- add guards to Range.new/2
- add specs to Range.range?/1
- test added to check for lists in range.
This commit is contained in:
eksperimental
2015-09-27 22:46:18 +07:00
parent 0ce1c441d5
commit 31a30afafd
3 changed files with 30 additions and 8 deletions
+5 -3
View File
@@ -2444,16 +2444,18 @@ defmodule Kernel do
defmacro first .. last do
case is_float(first) or is_float(last) or
is_atom(first) or is_atom(last) or
is_binary(first) or is_binary(last) do
is_binary(first) or is_binary(last) or
is_list(first) or is_list(last) do
true ->
raise ArgumentError,
"ranges (left .. right) expect both sides to be integers, " <>
"ranges (first .. last) expect both sides to be integers, " <>
"got: #{Macro.to_string({:.., [], [first, last]})}"
false ->
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]}
end
end
@doc """
Provides a short-circuit operator that evaluates and returns
the second expression only if the first one evaluates to `true`
@@ -2677,7 +2679,7 @@ defmodule Kernel do
{:%{}, [], [__struct__: Elixir.Range, first: first, last: last]} ->
in_range(left, Macro.expand(first, __CALLER__), Macro.expand(last, __CALLER__))
_ ->
raise ArgumentError, <<"invalid args for operator in, it expects a compile time list ",
raise ArgumentError, <<"invalid args for operator \"in\", it expects a compile time list ",
"or range on the right side when used in guard expressions, got: ",
Macro.to_string(right) :: binary>>
end
+15 -4
View File
@@ -31,12 +31,13 @@ defmodule Range do
@doc """
Creates a new range.
"""
def new(first, last) do
@spec new(integer, integer) :: t
def new(first, last) when is_integer(first) and is_integer(last) do
%Range{first: first, last: last}
end
@doc """
Returns `true` if the given argument is a range.
Returns `true` if the given `term` is a range.
## Examples
@@ -47,6 +48,12 @@ defmodule Range do
false
"""
# @spec range?(term) :: boolean
@spec range?(arg) :: false when arg: %Range{first: nil, last: nil}
@spec range?(arg) :: true when arg: %Range{}
@spec range?(term) :: false
def range?(term)
def range?(%Range{first: nil, last: nil}), do: false
def range?(%Range{}), do: true
def range?(_), do: false
end
@@ -95,10 +102,14 @@ defimpl Enumerable, for: Range do
end
end
defp validate_range!(first, last) when is_integer(first) and is_integer(last), do: :ok
defp validate_range!(first, last) when is_integer(first)
and is_integer(last),
do: :ok
defp validate_range!(first, last) do
raise ArgumentError,
"ranges (left .. right) expect both sides to be integers, got: #{inspect first..last}"
"ranges (first .. last) expect both sides to be integers, " <>
"got: #{Macro.to_string({:.., [], [first, last]})}"
end
end
+10 -1
View File
@@ -42,9 +42,18 @@ defmodule RangeTest do
test "integer only" do
x = 1.0
y = 3.0
message = "ranges (left .. right) expect both sides to be integers, got: 1.0..3.0"
message = "ranges (first .. last) expect both sides to be integers, got: 1.0..3.0"
assert_raise ArgumentError, message, fn ->
Enum.map(x..y, &(&1 * 2))
end
first = []
last = []
message = "ranges (first .. last) expect both sides to be integers, got: []..[]"
assert_raise ArgumentError, message, fn ->
first..last
Enum.map(first..last, &(&1))
end
end
end