diff options
-rw-r--r-- | lib/elixir/src/elixir_clauses.erl | 2 | ||||
-rw-r--r-- | lib/elixir/src/elixir_parser.yrl | 18 | ||||
-rw-r--r-- | lib/elixir/src/elixir_utils.erl | 2 | ||||
-rw-r--r-- | lib/elixir/test/elixir/kernel/expansion_test.exs | 6 | ||||
-rw-r--r-- | lib/elixir/test/elixir/kernel/fn_test.exs | 5 | ||||
-rw-r--r-- | lib/elixir/test/elixir/kernel_test.exs | 15 |
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 |