summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/elixir/src/elixir_clauses.erl2
-rw-r--r--lib/elixir/src/elixir_parser.yrl18
-rw-r--r--lib/elixir/src/elixir_utils.erl2
-rw-r--r--lib/elixir/test/elixir/kernel/expansion_test.exs6
-rw-r--r--lib/elixir/test/elixir/kernel/fn_test.exs5
-rw-r--r--lib/elixir/test/elixir/kernel_test.exs15
6 files changed, 25 insertions, 23 deletions
diff --git a/lib/elixir/src/elixir_clauses.erl b/lib/elixir/src/elixir_clauses.erl
index 778193c2d..120db0d5e 100644
--- a/lib/elixir/src/elixir_clauses.erl
+++ b/lib/elixir/src/elixir_clauses.erl
@@ -26,7 +26,7 @@ clause(_Meta, _Kind, Fun, {'->', Meta, [Left, Right]}, #{export_vars := ExportVa
clause(Meta, Kind, _Fun, _, E) ->
form_error(Meta, ?key(E, file), ?MODULE, {bad_or_missing_clauses, Kind}).
-head([{'when', Meta, [_, _ | _] = All}], E) ->
+head([{'when', Meta, [_ | _] = All}], E) ->
{Args, Guard} = elixir_utils:split_last(All),
{EArgs, EA} = match(fun elixir_expand:expand_args/2, Args, E),
{EGuard, EG} = guard(Guard, EA#{context := guard}),
diff --git a/lib/elixir/src/elixir_parser.yrl b/lib/elixir/src/elixir_parser.yrl
index 4ec5a0611..52904963d 100644
--- a/lib/elixir/src/elixir_parser.yrl
+++ b/lib/elixir/src/elixir_parser.yrl
@@ -43,8 +43,9 @@ Terminals
Rootsymbol grammar.
-%% Two shift/reduce conflicts coming from call_args_parens.
-Expect 2.
+%% Two shift/reduce conflicts coming from call_args_parens and
+%% one coming from empty_paren on stab.
+Expect 3.
%% Changes in ops and precedence should be reflected on lib/elixir/lib/macro.ex
%% Note though the operator => in practice has lower precedence than all others,
@@ -243,7 +244,7 @@ access_expr -> open_paren stab ';' close_paren : build_stab(reverse('$2')).
access_expr -> open_paren ';' stab ';' close_paren : build_stab(reverse('$3')).
access_expr -> open_paren ';' stab close_paren : build_stab(reverse('$3')).
access_expr -> open_paren ';' close_paren : build_stab([]).
-access_expr -> empty_paren : nil.
+access_expr -> empty_paren : warn_empty_paren('$1'), nil.
access_expr -> number_or_char : '$1'.
access_expr -> list : element(1, '$1').
access_expr -> map : '$1'.
@@ -314,6 +315,8 @@ stab_expr -> stab_op_eol_and_expr :
build_op(element(1, '$1'), [], element(2, '$1')).
stab_expr -> empty_paren stab_op_eol_and_expr :
build_op(element(1, '$2'), [], element(2, '$2')).
+stab_expr -> empty_paren when_op expr stab_op_eol_and_expr :
+ build_op(element(1, '$4'), [{'when', meta_from_token('$2'), ['$3']}], element(2, '$4')).
stab_expr -> call_args_no_parens_all stab_op_eol_and_expr :
build_op(element(1, '$2'), unwrap_when(unwrap_splice('$1')), element(2, '$2')).
stab_expr -> stab_parens_many stab_op_eol_and_expr :
@@ -843,6 +846,15 @@ throw_invalid_kw_identifier({_, _, do} = Token) ->
throw_invalid_kw_identifier({_, _, KW} = Token) ->
throw(meta_from_token(Token), "syntax error before: ", "'" ++ atom_to_list(KW) ++ "':").
+%% TODO: Make this an error on Elixir v2.0.
+warn_empty_paren({_, {Line, _, _}}) ->
+ elixir_errors:warn(Line, ?file(),
+ "invalid expression (). "
+ "If you want to invoke or define a function, make sure there are "
+ "no spaces between the function name and its arguments. If you wanted "
+ "to pass an empty block, pass a value instead, such as a nil or an atom").
+
+%% TODO: Make this an error on Elixir v2.0.
warn_empty_stab_clause({stab_op, {Line, _Begin, _End}, '->'}) ->
elixir_errors:warn(Line, ?file(),
"an expression is always required on the right side of ->. "
diff --git a/lib/elixir/src/elixir_utils.erl b/lib/elixir/src/elixir_utils.erl
index 93e7e4a9d..4703ffed8 100644
--- a/lib/elixir/src/elixir_utils.erl
+++ b/lib/elixir/src/elixir_utils.erl
@@ -46,7 +46,7 @@ extract_or_guards(Term) -> [Term].
% Extract guards when multiple left side args are allowed.
-extract_splat_guards([{'when', _, [_, _ | _] = Args}]) ->
+extract_splat_guards([{'when', _, [_ | _] = Args}]) ->
{Left, Right} = split_last(Args),
{Left, extract_or_guards(Right)};
extract_splat_guards(Else) ->
diff --git a/lib/elixir/test/elixir/kernel/expansion_test.exs b/lib/elixir/test/elixir/kernel/expansion_test.exs
index 81e56aca0..095e35cdb 100644
--- a/lib/elixir/test/elixir/kernel/expansion_test.exs
+++ b/lib/elixir/test/elixir/kernel/expansion_test.exs
@@ -527,7 +527,7 @@ defmodule Kernel.ExpansionTest do
end
assert_raise CompileError, ~r"expected -> clauses for :else in \"with\"", fn ->
- expand(quote(do: with(_ <- true, do: :ok, else: ())))
+ expand(quote(do: with(_ <- true, do: :ok, else: :error)))
end
end
@@ -603,8 +603,8 @@ defmodule Kernel.ExpansionTest do
test "fails on nested capture" do
assert_raise CompileError,
- ~r"nested captures via & are not allowed: &\(nil\)",
- fn -> expand(quote do: &(&())) end
+ ~r"nested captures via & are not allowed: &\(&1\)",
+ fn -> expand(quote do: &(& &1)) end
end
test "fails on integers" do
diff --git a/lib/elixir/test/elixir/kernel/fn_test.exs b/lib/elixir/test/elixir/kernel/fn_test.exs
index 013ce72f1..69defde21 100644
--- a/lib/elixir/test/elixir/kernel/fn_test.exs
+++ b/lib/elixir/test/elixir/kernel/fn_test.exs
@@ -15,6 +15,11 @@ defmodule Kernel.FnTest do
refute (fn ^x -> true; _ -> false end).(1.0)
end
+ test "guards with no args" do
+ fun = fn() when node() == :nonode@nohost -> true end
+ assert is_function(fun, 0)
+ end
+
test "case function hoisting does not affect anonymous fns" do
result =
if atom?(0) do
diff --git a/lib/elixir/test/elixir/kernel_test.exs b/lib/elixir/test/elixir/kernel_test.exs
index 9b668b838..7fe1f0f87 100644
--- a/lib/elixir/test/elixir/kernel_test.exs
+++ b/lib/elixir/test/elixir/kernel_test.exs
@@ -5,21 +5,6 @@ defmodule KernelTest do
doctest Kernel
- test "paren as nil" do
- assert is_nil(()) == true
- assert (_ = (); ();) == nil
- assert [1, (), 3] == [1, nil, 3]
- assert [do: ()] == [do: nil]
- assert {1, (), 3} == {1, nil, 3}
- assert (Kernel.&& nil, ()) == nil
- assert (() && ()) == nil
- assert (if(() && ()) do
- :ok
- else
- :error
- end) == :error
- end
-
test "=~/2" do
assert ("abcd" =~ ~r/c(d)/) == true
assert ("abcd" =~ ~r/e/) == false