diff --git a/lib/elixir/lib/kernel.ex b/lib/elixir/lib/kernel.ex index 7ed359af6c..38af774151 100644 --- a/lib/elixir/lib/kernel.ex +++ b/lib/elixir/lib/kernel.ex @@ -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 diff --git a/lib/elixir/lib/range.ex b/lib/elixir/lib/range.ex index 0f34001aaa..4d4a7cf684 100644 --- a/lib/elixir/lib/range.ex +++ b/lib/elixir/lib/range.ex @@ -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 diff --git a/lib/elixir/test/elixir/range_test.exs b/lib/elixir/test/elixir/range_test.exs index 924644e223..d25883ba38 100644 --- a/lib/elixir/test/elixir/range_test.exs +++ b/lib/elixir/test/elixir/range_test.exs @@ -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