Print a warning if defaults are used with multiple clauses, closes #209.

This commit is contained in:
José Valim
2012-04-21 18:28:39 +02:00
parent 6fdccdb802
commit 2aa72dfd6b
4 changed files with 28 additions and 26 deletions
+3 -1
View File
@@ -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
+2 -1
View File
@@ -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.
+1 -1
View File
@@ -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 """
+22 -23
View File
@@ -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]);