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:
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user