diff --git a/lib/enum.ex b/lib/enum.ex index 93d7d3ef89..24995f836e 100644 --- a/lib/enum.ex +++ b/lib/enum.ex @@ -322,11 +322,13 @@ defmodule Enum do Enum.keyfind list, 3, 2 #=> {:a, 3} """ + def keyfind(collection, key, position, default // nil) + def keyfind(collection, key, position, default) when is_list(collection) do :lists.keyfind(key, position, collection) || default end - def keyfind(collection, key, position, default // nil) do + def keyfind(collection, key, position, default) do { iterator, pointer } = I.iterator(collection) keyfind(iterator, pointer, key, position, default) end diff --git a/lib/keyword.ex b/lib/keyword.ex index 71b067e861..a7ca98c1b2 100644 --- a/lib/keyword.ex +++ b/lib/keyword.ex @@ -41,10 +41,11 @@ defmodule Keyword do Keyword.get [a: 1], :b #=> nil Keyword.get [a: 1], :b, 3 #=> 3 """ + def get(dict, key, default // nil) def get([{k, _}|_], key, default) when key < k, do: default def get([{k, _}|d], key, default) when key > k, do: get(d, key, default) def get([{_k, value}|_], _key, _default), do: value - def get([], _, default // nil), do: default + def get([], _, default), do: default @doc """ Returns all keys of dictionary. diff --git a/lib/module.ex b/lib/module.ex index 55d3bb93f4..66f4e15834 100644 --- a/lib/module.ex +++ b/lib/module.ex @@ -237,7 +237,7 @@ defmodule Module do def defined_functions(module) do assert_not_compiled!(:defined_functions, module) table = function_table_for(module) - lc { tuple, _, _ } in ETS.tab2list(table), do: tuple + lc { tuple, _, _, _ } in ETS.tab2list(table), do: tuple end @doc """ diff --git a/src/elixir_def.erl b/src/elixir_def.erl index 53e8315253..3834c53c59 100644 --- a/src/elixir_def.erl +++ b/src/elixir_def.erl @@ -117,11 +117,11 @@ store_definition(Kind, Line, Module, Name, Args, RawGuards, RawExpr, S) -> _ -> compile_super(Module, TS), CheckClauses = S#elixir_scope.check_clauses, - store_each(CheckClauses, Final, FunctionTable, Visibility, Filename, Function) + store_each(CheckClauses, Final, FunctionTable, Visibility, length(Defaults), Filename, Function) end, %% Store defaults - [store_each(false, Final, FunctionTable, Visibility, Filename, + [store_each(false, Final, FunctionTable, Visibility, 0, Filename, function_for_clause(Name, Default)) || Default <- Defaults], { Name, Arity }. @@ -179,7 +179,7 @@ unwrap_stored_definitions(Module) -> Functions = ets:foldl(fun(X, Acc) -> unwrap_stored_definition(X, Acc, Private) end, [], Table), { Public, Private, Macros, Functions }. -unwrap_stored_definition({{Name, Arity}, Line, Clauses}, Acc, _Private) -> +unwrap_stored_definition({{Name, Arity}, Line, _, Clauses}, Acc, _Private) -> [{function, Line, Name, Arity, lists:reverse(Clauses) }|Acc]. %% Helpers @@ -193,38 +193,30 @@ function_for_clause(Name, { clause, Line, Args, _Guards, _Exprs } = Clause) -> %% This function also checks and emit warnings in case %% the kind, of the visibility of the function changes. -store_each(Check, Kind, Table, Visibility, Filename, {function, Line, Name, Arity, Clauses}) -> - FinalClauses = case ets:lookup(Table, {Name, Arity}) of - [{{Name, Arity}, FinalLine, OtherClauses}] -> +store_each(Check, Kind, Table, Visibility, Defaults, Filename, {function, Line, Name, Arity, Clauses}) -> + case ets:lookup(Table, {Name, Arity}) of + [{{Name, Arity}, _, StoredDefaults, StoredClauses}] -> + FinalDefaults = Defaults + StoredDefaults, + FinalClauses = Clauses ++ StoredClauses, check_valid_visibility(Line, Filename, Name, Arity, Visibility, Table), check_valid_kind(Line, Filename, Name, Arity, Kind, Table), - - case Check of - false -> []; - true -> check_valid_clause(Line, Filename, Name, Arity, Table) - end, - - Clauses ++ OtherClauses; + check_valid_defaults(Line, Filename, Name, Arity, Defaults), + Check andalso check_valid_clause(Line, Filename, Name, Arity, Table); [] -> + FinalDefaults = Defaults, + FinalClauses = Clauses, add_visibility_entry(Name, Arity, Visibility, Table), add_function_entry(Name, Arity, Kind, Table), - - case Check of - false -> []; - true -> ets:insert(Table, {last, { Name, Arity }}) - end, - - FinalLine = Line, - Clauses + Check andalso ets:insert(Table, { last, { Name, Arity } }) end, - ets:insert(Table, {{Name, Arity}, FinalLine, FinalClauses}). + ets:insert(Table, {{Name, Arity}, Line, FinalDefaults, FinalClauses}). %% Handle kind (def/defmacro) entries in the table add_function_entry(Name, Arity, defmacro, Table) -> Tuple = { Name, Arity }, Current= ets:lookup_element(Table, macros, 2), - ets:insert(Table, {macros, [Tuple|Current]}); + ets:insert(Table, { macros, [Tuple|Current] }); add_function_entry(_Name, _Arity, def, _Table) -> ok. @@ -275,8 +267,15 @@ check_valid_clause(Line, Filename, Name, Arity, Table) -> { changed_clause, { {Name, Arity}, {ElseName, ElseArity} } }) end. +check_valid_defaults(_Line, _Filename, _Name, _Arity, 0) -> []; +check_valid_defaults(Line, Filename, Name, Arity, _) -> + elixir_errors:handle_file_warning(Filename, { Line, ?MODULE, { clauses_with_docs, { Name, Arity } } }). + %% Format errors +format_error({clauses_with_docs,{Name,Arity}}) -> + io_lib:format("function ~s/~B has default values and multiple clauses, it is recommended to use a separate clause for declaring defalts", [Name, Arity]); + format_error({private_doc,{Name,Arity}}) -> io_lib:format("function ~s/~B is private, @doc's are always discarded for private functions", [Name, Arity]);