From c48e315dbc8e41217ef51501afef30d02b7690ce Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 12 May 2010 09:49:12 +0200 Subject: First prototype for local functions overriding autoimported Import directives still not sorted out! --- lib/stdlib/src/erl_lint.erl | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 2cc5c6a5ac..1e5464231d 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -94,6 +94,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> mod_imports=dict:new() :: dict(), %Module Imports compile=[], %Compile flags records=dict:new() :: dict(), %Record definitions + locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned) defined=gb_sets:empty() :: gb_set(), %Defined fuctions on_load=[] :: [{atom(),integer()}], %On-load function on_load_line=0 :: integer(), %Line for on_load @@ -187,12 +188,7 @@ format_error({define_import,{F,A}}) -> format_error({unused_function,{F,A}}) -> io_lib:format("function ~w/~w is unused", [F,A]); format_error({redefine_bif,{F,A}}) -> - io_lib:format("defining BIF ~w/~w", [F,A]); -format_error({call_to_redefined_bif,{F,A}}) -> - io_lib:format("call to ~w/~w will call erlang:~w/~w; " - "not ~w/~w in this module \n" - " (add an explicit module name to the call to avoid this error)", - [F,A,F,A,F,A]); + io_lib:format("redefining autoimported BIF ~w/~w", [F,A]); format_error({deprecated, MFA, ReplacementMFA, Rel}) -> io_lib:format("~s is deprecated and will be removed in ~s; use ~s", @@ -538,8 +534,9 @@ loc(L) -> forms(Forms0, St0) -> Forms = eval_file_attribute(Forms0, St0), + Locals = local_functions(Forms), %% Line numbers are from now on pairs {File,Line}. - St1 = includes_qlc_hrl(Forms, St0), + St1 = includes_qlc_hrl(Forms, St0#lint{locals = Locals}), St2 = bif_clashes(Forms, St1), St3 = not_deprecated(Forms, St2), St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms), @@ -1216,7 +1213,9 @@ define_function(Line, Name, Arity, St0) -> false -> St2 = St1#lint{defined=gb_sets:add_element(NA, St1#lint.defined)}, St = case erl_internal:bif(Name, Arity) andalso - not is_function_exported(Name, Arity, St2) of + (not is_function_exported(Name, Arity, St2)) andalso + is_warn_enabled(bif_clash, St2) andalso + is_bif_clash(Name,Arity,St2) of true -> add_warning(Line, {redefine_bif,NA}, St2); false -> St2 end, @@ -1725,7 +1724,8 @@ gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}=Isr},[_,_,_]=Args} gexpr({call,Line,{atom,_La,F},As}, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), A = length(As), - case erl_internal:guard_bif(F, A) of + case (not is_local_function(St1#lint.locals,{F,A})) andalso + erl_internal:guard_bif(F, A) of true -> %% Also check that it is auto-imported. case erl_internal:bif(F, A) of @@ -1959,7 +1959,7 @@ expr({'fun',Line,Body}, Vt, St) -> {vtupdate(Bvt, Vt), St1}; {function,F,A} -> %% N.B. Only allows BIFs here as well, NO IMPORTS!! - case erl_internal:bif(F, A) of + case ((not is_local_function(St#lint.locals,{F,A})) and erl_internal:bif(F, A)) of true -> {[],St}; false -> {[],call_function(Line, F, A, St)} end; @@ -1992,16 +1992,10 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> St1 = keyword_warning(La, F, St0), {Asvt,St2} = expr_list(As, Vt, St1), A = length(As), - case erl_internal:bif(F, A) of + case ((not is_local_function(St2#lint.locals,{F,A})) and erl_internal:bif(F, A)) of true -> St3 = deprecated_function(Line, erlang, F, As, St2), - {Asvt,case is_warn_enabled(bif_clash, St3) andalso - is_bif_clash(F, A, St3) of - false -> - St3; - true -> - add_error(Line, {call_to_redefined_bif,{F,A}}, St3) - end}; + {Asvt,St3}; false -> {Asvt,case imported(F, A, St2) of {yes,M} -> @@ -2010,8 +2004,8 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> Imp = ordsets:add_element({{F,A},M},U0#usage.imported), St3#lint{usage=U0#usage{imported = Imp}}; no -> - case {F,A} of - {record_info,2} -> + case {F,A} of + {record_info,2} -> check_record_info_call(Line,La,As,St2); N when N =:= St2#lint.func -> St2; _ -> call_function(Line, F, A, St2) @@ -3443,3 +3437,9 @@ expand_package(M, St0) -> {error, St1} end end. + +local_functions(Forms) -> + gb_sets:from_list([ {Func,Arity} || {function,_,Func,Arity,_} <- Forms ]). + +is_local_function(LocalSet,{Func,Arity}) -> + gb_sets:is_element({Func,Arity},LocalSet). -- cgit v1.2.3 From a4894eabd2117dbb8e98365e9f87acf8c7a1ae33 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 21 May 2010 12:02:04 +0200 Subject: Teach compiler to override autoimport with import --- lib/stdlib/src/erl_lint.erl | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 1e5464231d..89e31ba7e0 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -189,6 +189,8 @@ format_error({unused_function,{F,A}}) -> io_lib:format("function ~w/~w is unused", [F,A]); format_error({redefine_bif,{F,A}}) -> io_lib:format("redefining autoimported BIF ~w/~w", [F,A]); +format_error({redefine_bif_import,{F,A}}) -> + io_lib:format("import directive redefines autoimported BIF ~w/~w", [F,A]); format_error({deprecated, MFA, ReplacementMFA, Rel}) -> io_lib:format("~s is deprecated and will be removed in ~s; use ~s", @@ -1091,11 +1093,32 @@ import(Line, {Mod,Fs}, St) -> St#lint{imports=add_imports(list_to_atom(Mod1), Mfs, St#lint.imports)}; Efs -> - foldl(fun (Ef, St0) -> - add_error(Line, {redefine_import,Ef}, - St0) + {Err, St1} = + foldl(fun ({bif,{F,A},_}, {Err,St0}) -> + Warn = is_warn_enabled(bif_clash, St0), + {Err,if + Warn -> + add_warning + (Line, + {redefine_bif_import, {F,A}}, + St0); + true -> + St0 + end}; + (Ef, {_Err,St0}) -> + {true,add_error(Line, + {redefine_import,Ef}, + St0)} end, - St, Efs) + {false,St}, Efs), + if + not Err -> + St1#lint{imports= + add_imports(list_to_atom(Mod1), Mfs, + St#lint.imports)}; + true -> + St1 + end end; false -> add_error(Line, {bad_module_name, Mod1}, St) -- cgit v1.2.3 From 7f04467044c509f6a0c39fd5bc31623d440c3715 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Fri, 21 May 2010 17:01:53 +0200 Subject: Teach erl_lint to better override BIFs with local functions and imports Added only a few testcases in compiler:error_SUITE and guard_SUITE The new behaviour of warnings and errors when overriding autoimported BIF's: Bifs that were autoimported before R14 are dangerous because old code using them and overriding them in exports can start behaving differently. For newly added autoimports this can't happen to the new code that wants to (or dont want to) use them, why only warnings are added for the BIFs autoimported after the compilator change. Errors are issued only for code that could have worked in one way in R13 and now will behave in a different way. If overriding autoimport with local function: - if explicit -compile directive supresses autoimport -> no message else - if called from inside module - if pre R14 autoimported bif -> error else -> warning else -> no message If overriding autoimport with import directive - if explicit -compile directive supresses autoimport -> no message else (regardless of actual usage) - if pre R14 autoimported bif -> error else -> warning Calls of local functions or imports overriding autoimported functions (either post R14 or by using explicit -compile supressions of autoimport) always goes to the local function or the imported. The compileation errors are added to not let code like this silently and disastrously change its semantic (probably to an infinite loop) between R13 and R14: ---------- -module(m). -export([length/1]). length(X) -> ... Y = length(Z), .... ---------- The user has to select if he/she wants to call length in 'erlang' explicitly or if the overriding semantics is desired, in which case the -compile directive has to be used. -compile({no_auto_import,[F/A]}). Is added to allow to override the autoimports so that code gets unanbiguous. The directive will remove an autoimport even if there is no local function or import overriding, because any other behaviour would be inconsistent and confusing. record_info and module_info can never be overridden. --- lib/stdlib/src/erl_internal.erl | 133 +++++++++++++++++++++++++++++++++++++++- lib/stdlib/src/erl_lint.erl | 123 +++++++++++++++++++++++++++---------- 2 files changed, 221 insertions(+), 35 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index f78d8dc609..6a7c62f101 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -48,7 +48,7 @@ %% -export([bif/2,bif/3,guard_bif/2, - type_test/2,new_type_test/2,old_type_test/2]). + type_test/2,new_type_test/2,old_type_test/2,old_bif/2]). -export([arith_op/2,bool_op/2,comp_op/2,list_op/2,send_op/2,op_type/2]). %%--------------------------------------------------------------------------- @@ -354,3 +354,134 @@ bif(unlink, 1) -> true; bif(unregister, 1) -> true; bif(whereis, 1) -> true; bif(Name, A) when is_atom(Name), is_integer(A) -> false. + +-spec old_bif(Name::atom(), Arity::arity()) -> boolean(). +%% Returns true if erlang:Name/Arity is an old (pre R14) auto-imported BIF, false otherwise. +%% Use erlang:is_bultin(Mod, Name, Arity) to find whether a function is a BIF +%% (meaning implemented in C) or not. + +old_bif(abs, 1) -> true; +old_bif(apply, 2) -> true; +old_bif(apply, 3) -> true; +old_bif(atom_to_binary, 2) -> true; +old_bif(atom_to_list, 1) -> true; +old_bif(binary_to_atom, 2) -> true; +old_bif(binary_to_existing_atom, 2) -> true; +old_bif(binary_to_list, 1) -> true; +old_bif(binary_to_list, 3) -> true; +old_bif(binary_to_term, 1) -> true; +old_bif(bitsize, 1) -> true; +old_bif(bit_size, 1) -> true; +old_bif(bitstring_to_list, 1) -> true; +old_bif(byte_size, 1) -> true; +old_bif(check_process_code, 2) -> true; +old_bif(concat_binary, 1) -> true; +old_bif(date, 0) -> true; +old_bif(delete_module, 1) -> true; +old_bif(disconnect_node, 1) -> true; +old_bif(element, 2) -> true; +old_bif(erase, 0) -> true; +old_bif(erase, 1) -> true; +old_bif(exit, 1) -> true; +old_bif(exit, 2) -> true; +old_bif(float, 1) -> true; +old_bif(float_to_list, 1) -> true; +old_bif(garbage_collect, 0) -> true; +old_bif(garbage_collect, 1) -> true; +old_bif(get, 0) -> true; +old_bif(get, 1) -> true; +old_bif(get_keys, 1) -> true; +old_bif(group_leader, 0) -> true; +old_bif(group_leader, 2) -> true; +old_bif(halt, 0) -> true; +old_bif(halt, 1) -> true; +old_bif(hd, 1) -> true; +old_bif(integer_to_list, 1) -> true; +old_bif(iolist_size, 1) -> true; +old_bif(iolist_to_binary, 1) -> true; +old_bif(is_alive, 0) -> true; +old_bif(is_process_alive, 1) -> true; +old_bif(is_atom, 1) -> true; +old_bif(is_boolean, 1) -> true; +old_bif(is_binary, 1) -> true; +old_bif(is_bitstr, 1) -> true; +old_bif(is_bitstring, 1) -> true; +old_bif(is_float, 1) -> true; +old_bif(is_function, 1) -> true; +old_bif(is_function, 2) -> true; +old_bif(is_integer, 1) -> true; +old_bif(is_list, 1) -> true; +old_bif(is_number, 1) -> true; +old_bif(is_pid, 1) -> true; +old_bif(is_port, 1) -> true; +old_bif(is_reference, 1) -> true; +old_bif(is_tuple, 1) -> true; +old_bif(is_record, 2) -> true; +old_bif(is_record, 3) -> true; +old_bif(length, 1) -> true; +old_bif(link, 1) -> true; +old_bif(list_to_atom, 1) -> true; +old_bif(list_to_binary, 1) -> true; +old_bif(list_to_bitstring, 1) -> true; +old_bif(list_to_existing_atom, 1) -> true; +old_bif(list_to_float, 1) -> true; +old_bif(list_to_integer, 1) -> true; +old_bif(list_to_pid, 1) -> true; +old_bif(list_to_tuple, 1) -> true; +old_bif(load_module, 2) -> true; +old_bif(make_ref, 0) -> true; +old_bif(module_loaded, 1) -> true; +old_bif(monitor_node, 2) -> true; +old_bif(node, 0) -> true; +old_bif(node, 1) -> true; +old_bif(nodes, 0) -> true; +old_bif(nodes, 1) -> true; +old_bif(now, 0) -> true; +old_bif(open_port, 2) -> true; +old_bif(pid_to_list, 1) -> true; +old_bif(port_close, 1) -> true; +old_bif(port_command, 2) -> true; +old_bif(port_connect, 2) -> true; +old_bif(port_control, 3) -> true; +old_bif(pre_loaded, 0) -> true; +old_bif(process_flag, 2) -> true; +old_bif(process_flag, 3) -> true; +old_bif(process_info, 1) -> true; +old_bif(process_info, 2) -> true; +old_bif(processes, 0) -> true; +old_bif(purge_module, 1) -> true; +old_bif(put, 2) -> true; +old_bif(register, 2) -> true; +old_bif(registered, 0) -> true; +old_bif(round, 1) -> true; +old_bif(self, 0) -> true; +old_bif(setelement, 3) -> true; +old_bif(size, 1) -> true; +old_bif(spawn, 1) -> true; +old_bif(spawn, 2) -> true; +old_bif(spawn, 3) -> true; +old_bif(spawn, 4) -> true; +old_bif(spawn_link, 1) -> true; +old_bif(spawn_link, 2) -> true; +old_bif(spawn_link, 3) -> true; +old_bif(spawn_link, 4) -> true; +old_bif(spawn_monitor, 1) -> true; +old_bif(spawn_monitor, 3) -> true; +old_bif(spawn_opt, 2) -> true; +old_bif(spawn_opt, 3) -> true; +old_bif(spawn_opt, 4) -> true; +old_bif(spawn_opt, 5) -> true; +old_bif(split_binary, 2) -> true; +old_bif(statistics, 1) -> true; +old_bif(term_to_binary, 1) -> true; +old_bif(term_to_binary, 2) -> true; +old_bif(throw, 1) -> true; +old_bif(time, 0) -> true; +old_bif(tl, 1) -> true; +old_bif(trunc, 1) -> true; +old_bif(tuple_size, 1) -> true; +old_bif(tuple_to_list, 1) -> true; +old_bif(unlink, 1) -> true; +old_bif(unregister, 1) -> true; +old_bif(whereis, 1) -> true; +old_bif(Name, A) when is_atom(Name), is_integer(A) -> false. diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 89e31ba7e0..acb9165ead 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -95,6 +95,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> compile=[], %Compile flags records=dict:new() :: dict(), %Record definitions locals=gb_sets:empty() :: gb_set(), %All defined functions (prescanned) + no_auto=gb_sets:empty() :: gb_set(), %Functions explicitly not autoimported defined=gb_sets:empty() :: gb_set(), %Defined fuctions on_load=[] :: [{atom(),integer()}], %On-load function on_load_line=0 :: integer(), %Line for on_load @@ -117,7 +118,7 @@ value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> types = dict:new() :: dict() %Type definitions }). --type lint_state() :: #lint{}. +%% -type lint_state() :: #lint{}. %% format_error(Error) %% Return a string describing the error. @@ -188,9 +189,20 @@ format_error({define_import,{F,A}}) -> format_error({unused_function,{F,A}}) -> io_lib:format("function ~w/~w is unused", [F,A]); format_error({redefine_bif,{F,A}}) -> - io_lib:format("redefining autoimported BIF ~w/~w", [F,A]); + io_lib:format("ambiguous call of redefined auto-imported BIF ~w/~w~n" + " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" " + "to resolve name clash", [F,A,F,A,F,A]); +format_error({redefine_old_bif,{F,A}}) -> + io_lib:format("ambiguous call of redefined pre R14 auto-imported BIF ~w/~w~n" + " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" " + "to resolve name clash", [F,A,F,A,F,A]); +format_error({redefine_old_bif_import,{F,A}}) -> + io_lib:format("import directive redefines pre R14 auto-imported BIF ~w/~w~n" + " - use \"-compile({no_auto_import,[~w/~w]}).\" " + "to resolve name clash", [F,A,F,A]); format_error({redefine_bif_import,{F,A}}) -> - io_lib:format("import directive redefines autoimported BIF ~w/~w", [F,A]); + io_lib:format("import directive redefines auto-imported BIF ~w/~w~n" + " - use \"-compile({no_auto_import,[~w/~w]}).\" to resolve name clash", [F,A,F,A]); format_error({deprecated, MFA, ReplacementMFA, Rel}) -> io_lib:format("~s is deprecated and will be removed in ~s; use ~s", @@ -537,8 +549,9 @@ loc(L) -> forms(Forms0, St0) -> Forms = eval_file_attribute(Forms0, St0), Locals = local_functions(Forms), + AutoImportSupressed = auto_import_supressed(St0#lint.compile), %% Line numbers are from now on pairs {File,Line}. - St1 = includes_qlc_hrl(Forms, St0#lint{locals = Locals}), + St1 = includes_qlc_hrl(Forms, St0#lint{locals = Locals, no_auto = AutoImportSupressed}), St2 = bif_clashes(Forms, St1), St3 = not_deprecated(Forms, St2), St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms), @@ -723,12 +736,12 @@ bif_clashes(Forms, St) -> Clashes = ordsets:subtract(ordsets:from_list(Clashes0), Nowarn), St#lint{clashes=Clashes}. --spec is_bif_clash(atom(), byte(), lint_state()) -> boolean(). +%% -spec is_bif_clash(atom(), byte(), lint_state()) -> boolean(). -is_bif_clash(_Name, _Arity, #lint{clashes=[]}) -> - false; -is_bif_clash(Name, Arity, #lint{clashes=Clashes}) -> - ordsets:is_element({Name,Arity}, Clashes). +%% is_bif_clash(_Name, _Arity, #lint{clashes=[]}) -> +%% false; +%% is_bif_clash(Name, Arity, #lint{clashes=Clashes}) -> +%% ordsets:is_element({Name,Arity}, Clashes). %% not_deprecated(Forms, State0) -> State @@ -1095,12 +1108,19 @@ import(Line, {Mod,Fs}, St) -> Efs -> {Err, St1} = foldl(fun ({bif,{F,A},_}, {Err,St0}) -> - Warn = is_warn_enabled(bif_clash, St0), + Warn = is_warn_enabled(bif_clash, St0), %% PaN -> import directive + AutoImpSup = is_autoimport_supressed(St0#lint.no_auto,{F,A}), + OldBif = erl_internal:old_bif(F,A), {Err,if - Warn -> + Warn and (not AutoImpSup) and OldBif -> + add_error + (Line, + {redefine_old_bif_import, {F,A}}, + St0); + Warn and (not AutoImpSup) -> add_warning (Line, - {redefine_bif_import, {F,A}}, + {redefine_bif_import, {F,A}}, St0); true -> St0 @@ -1211,12 +1231,6 @@ call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) -> end, St#lint{called=[{NA,Line}|Cd], usage=Usage}. -%% is_function_exported(Name, Arity, State) -> false|true. - -is_function_exported(Name, Arity, #lint{exports=Exports,compile=Compile}) -> - gb_sets:is_element({Name,Arity}, Exports) orelse - member(export_all, Compile). - %% function(Line, Name, Arity, Clauses, State) -> State. function(Line, instance, _Arity, _Cs, St) when St#lint.global_vt =/= [] -> @@ -1235,16 +1249,17 @@ define_function(Line, Name, Arity, St0) -> add_error(Line, {redefine_function,NA}, St1); false -> St2 = St1#lint{defined=gb_sets:add_element(NA, St1#lint.defined)}, - St = case erl_internal:bif(Name, Arity) andalso - (not is_function_exported(Name, Arity, St2)) andalso - is_warn_enabled(bif_clash, St2) andalso - is_bif_clash(Name,Arity,St2) of - true -> add_warning(Line, {redefine_bif,NA}, St2); - false -> St2 - end, - case imported(Name, Arity, St) of - {yes,_M} -> add_error(Line, {define_import,NA}, St); - no -> St +%% St = case erl_internal:bif(Name, Arity) andalso %% PaN - Function definitions +%% (not is_function_exported(Name, Arity, St2)) andalso +%% is_warn_enabled(bif_clash, St2) andalso +%% is_bif_clash(Name,Arity,St2) andalso +%% (not is_autoimport_supressed(St0#lint.no_auto,NA)) of +%% true -> add_warning(Line, {redefine_bif,NA}, St2); +%% false -> St2 +%% end, + case imported(Name, Arity, St2) of + {yes,_M} -> add_error(Line, {define_import,NA}, St2); + no -> St2 end end. @@ -1747,8 +1762,10 @@ gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}=Isr},[_,_,_]=Args} gexpr({call,Line,{atom,_La,F},As}, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), A = length(As), - case (not is_local_function(St1#lint.locals,{F,A})) andalso - erl_internal:guard_bif(F, A) of + case (not is_local_function(St1#lint.locals,{F,A})) andalso %% PaN -> Function called in guard + (not is_imported_function(St1#lint.imports,{F,A})) andalso + erl_internal:guard_bif(F, A) andalso + (not is_autoimport_supressed(St1#lint.no_auto, {F,A})) of true -> %% Also check that it is auto-imported. case erl_internal:bif(F, A) of @@ -1982,7 +1999,9 @@ expr({'fun',Line,Body}, Vt, St) -> {vtupdate(Bvt, Vt), St1}; {function,F,A} -> %% N.B. Only allows BIFs here as well, NO IMPORTS!! - case ((not is_local_function(St#lint.locals,{F,A})) and erl_internal:bif(F, A)) of + case ((not is_local_function(St#lint.locals,{F,A})) andalso %% PaN - Fun expression + (erl_internal:bif(F, A) andalso + (not is_autoimport_supressed(St#lint.no_auto,{F,A})))) of true -> {[],St}; false -> {[],call_function(Line, F, A, St)} end; @@ -2015,7 +2034,10 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> St1 = keyword_warning(La, F, St0), {Asvt,St2} = expr_list(As, Vt, St1), A = length(As), - case ((not is_local_function(St2#lint.locals,{F,A})) and erl_internal:bif(F, A)) of + IsLocal = is_local_function(St2#lint.locals,{F,A}), + IsAutoBif = erl_internal:bif(F, A), + AutoSupressed = is_autoimport_supressed(St2#lint.no_auto,{F,A}), + case ((not IsLocal) andalso IsAutoBif andalso (not AutoSupressed)) of %% PaN - function call true -> St3 = deprecated_function(Line, erlang, F, As, St2), {Asvt,St3}; @@ -2030,8 +2052,27 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> case {F,A} of {record_info,2} -> check_record_info_call(Line,La,As,St2); - N when N =:= St2#lint.func -> St2; - _ -> call_function(Line, F, A, St2) + N when N =:= St2#lint.func -> + St2; + _ -> + St3 = if + (not AutoSupressed) andalso IsAutoBif -> + case erl_internal:old_bif(F,A) of + true -> + add_error + (Line, + {redefine_old_bif, {F,A}}, + St2); + false -> + add_warning + (Line, + {redefine_bif, {F,A}}, + St2) + end; + true -> + St2 + end, + call_function(Line, F, A, St3) end end} end; @@ -3466,3 +3507,17 @@ local_functions(Forms) -> is_local_function(LocalSet,{Func,Arity}) -> gb_sets:is_element({Func,Arity},LocalSet). + +is_imported_function(ImportSet,{Func,Arity}) -> + case orddict:find({Func,Arity}, ImportSet) of + {ok,_Mod} -> true; + error -> false + end. + +auto_import_supressed(CompileFlags) -> + L0 = [ X || {no_auto_import,X} <- CompileFlags ], + L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ], + gb_sets:from_list(L1). + +is_autoimport_supressed(NoAutoSet,{Func,Arity}) -> + gb_sets:is_element({Func,Arity},NoAutoSet). -- cgit v1.2.3 From 634dd378030292abeec98e7f332e57c5d36e13ef Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 26 May 2010 12:22:50 +0200 Subject: Return nowarn_bif_clash functionality but with warning Wrote and changed some tests in stdlib:erl_lint_SUITE nowarn_bif_clash is obsoleted but will remove warnings and errors about bif clashes. The recommended way is to use no_auto_import directives instead. Hopefully erlang.erl is the only user in the world of nowarn_bif_clash. --- lib/stdlib/src/erl_lint.erl | 133 ++++++++++++++++++++--------- lib/stdlib/test/erl_lint_SUITE.erl | 170 ++++++++++++++++++++++++++++++++++--- 2 files changed, 251 insertions(+), 52 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index acb9165ead..631ad0c1e3 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -163,6 +163,9 @@ format_error({bad_nowarn_unused_function,{F,A}}) -> io_lib:format("function ~w/~w undefined", [F,A]); format_error({bad_nowarn_bif_clash,{F,A}}) -> io_lib:format("function ~w/~w undefined", [F,A]); +format_error(deprecated_nowarn_bif_clash) -> + io_lib:format("compile directive nowarn_bif_clash is deprecated,~n" + " - use explicit module names or -compile({no_auto_import, [F/A]})", []); format_error({bad_nowarn_deprecated_function,{M,F,A}}) -> io_lib:format("~w:~w/~w is not a deprecated function", [M,F,A]); format_error({bad_on_load,Term}) -> @@ -188,11 +191,11 @@ format_error({define_import,{F,A}}) -> io_lib:format("defining imported function ~w/~w", [F,A]); format_error({unused_function,{F,A}}) -> io_lib:format("function ~w/~w is unused", [F,A]); -format_error({redefine_bif,{F,A}}) -> +format_error({call_to_redefined_bif,{F,A}}) -> io_lib:format("ambiguous call of redefined auto-imported BIF ~w/~w~n" " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" " "to resolve name clash", [F,A,F,A,F,A]); -format_error({redefine_old_bif,{F,A}}) -> +format_error({call_to_redefined_old_bif,{F,A}}) -> io_lib:format("ambiguous call of redefined pre R14 auto-imported BIF ~w/~w~n" " - use erlang:~w/~w or \"-compile({no_auto_import,[~w/~w]}).\" " "to resolve name clash", [F,A,F,A,F,A]); @@ -549,9 +552,11 @@ loc(L) -> forms(Forms0, St0) -> Forms = eval_file_attribute(Forms0, St0), Locals = local_functions(Forms), - AutoImportSupressed = auto_import_supressed(St0#lint.compile), + AutoImportSuppressed = auto_import_suppressed(St0#lint.compile), + StDeprecated = deprecated_compile_flags(Forms,St0), %% Line numbers are from now on pairs {File,Line}. - St1 = includes_qlc_hrl(Forms, St0#lint{locals = Locals, no_auto = AutoImportSupressed}), + St1 = includes_qlc_hrl(Forms, StDeprecated#lint{locals = Locals, + no_auto = AutoImportSuppressed}), St2 = bif_clashes(Forms, St1), St3 = not_deprecated(Forms, St2), St4 = foldl(fun form/2, pre_scan(Forms, St3), Forms), @@ -757,6 +762,23 @@ not_deprecated(Forms, St0) -> St1 = func_line_warning(bad_nowarn_deprecated_function, Bad, St0), St1#lint{not_deprecated = ordsets:from_list(Nowarn)}. +deprecated_compile_flags(Forms, St0) -> + %% There are (still) no line numbers in St0#lint.compile. + Warnings0 = [ {St0#lint.file,{L,erl_lint,deprecated_nowarn_bif_clash}} || + {attribute,[{line,{_,L}}],compile,nowarn_bif_clash} <- Forms ], + Warnings1 = [ {St0#lint.file,{L,erl_lint,deprecated_nowarn_bif_clash}} || + {attribute,[{line,{_,L}}],compile,{nowarn_bif_clash, {_,_}}} <- Forms ], + Disabled = (not is_warn_enabled(bif_clash, St0)), + Warnings = if + Disabled andalso Warnings0 =:= [] -> + [{St0#lint.file,{erl_lint,deprecated_nowarn_bif_clash}} | St0#lint.warnings]; + Disabled -> + Warnings0 ++ Warnings1 ++ St0#lint.warnings; + true -> + Warnings1 ++ St0#lint.warnings + end, + St0#lint{warnings=Warnings}. + %% post_traversal_check(Forms, State0) -> State. %% Do some further checking after the forms have been traversed and %% data about calls etc. have been collected. @@ -1015,7 +1037,8 @@ check_option_functions(Forms, Tag0, Type, St0) -> {Tag, FAs0} <- lists:flatten([Args]), Tag0 =:= Tag, FA <- lists:flatten([FAs0])], - DefFunctions = gb_sets:to_list(St0#lint.defined) -- pseudolocals(), + DefFunctions = (gb_sets:to_list(St0#lint.defined) -- pseudolocals()) ++ + [{F,A} || {{F,A},_} <- orddict:to_list(St0#lint.imports)], Bad = [{FA,L} || {FA,L} <- FAsL, not member(FA, DefFunctions)], func_line_error(Type, Bad, St0). @@ -1108,8 +1131,10 @@ import(Line, {Mod,Fs}, St) -> Efs -> {Err, St1} = foldl(fun ({bif,{F,A},_}, {Err,St0}) -> - Warn = is_warn_enabled(bif_clash, St0), %% PaN -> import directive - AutoImpSup = is_autoimport_supressed(St0#lint.no_auto,{F,A}), + %% BifClash - import directive + Warn = is_warn_enabled(bif_clash, St0) + and (not bif_clash_specifically_disabled(St0,{F,A})), + AutoImpSup = is_autoimport_suppressed(St0#lint.no_auto,{F,A}), OldBif = erl_internal:old_bif(F,A), {Err,if Warn and (not AutoImpSup) and OldBif -> @@ -1123,7 +1148,7 @@ import(Line, {Mod,Fs}, St) -> {redefine_bif_import, {F,A}}, St0); true -> - St0 + St0 end}; (Ef, {_Err,St0}) -> {true,add_error(Line, @@ -1249,14 +1274,6 @@ define_function(Line, Name, Arity, St0) -> add_error(Line, {redefine_function,NA}, St1); false -> St2 = St1#lint{defined=gb_sets:add_element(NA, St1#lint.defined)}, -%% St = case erl_internal:bif(Name, Arity) andalso %% PaN - Function definitions -%% (not is_function_exported(Name, Arity, St2)) andalso -%% is_warn_enabled(bif_clash, St2) andalso -%% is_bif_clash(Name,Arity,St2) andalso -%% (not is_autoimport_supressed(St0#lint.no_auto,NA)) of -%% true -> add_warning(Line, {redefine_bif,NA}, St2); -%% false -> St2 -%% end, case imported(Name, Arity, St2) of {yes,_M} -> add_error(Line, {define_import,NA}, St2); no -> St2 @@ -1762,17 +1779,16 @@ gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}=Isr},[_,_,_]=Args} gexpr({call,Line,{atom,_La,F},As}, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), A = length(As), - case (not is_local_function(St1#lint.locals,{F,A})) andalso %% PaN -> Function called in guard - (not is_imported_function(St1#lint.imports,{F,A})) andalso - erl_internal:guard_bif(F, A) andalso - (not is_autoimport_supressed(St1#lint.no_auto, {F,A})) of + %% BifClash - Function called in guard + case erl_internal:guard_bif(F, A) andalso no_guard_bif_clash(St1,{F,A}) of true -> %% Also check that it is auto-imported. case erl_internal:bif(F, A) of true -> {Asvt,St1}; false -> {Asvt,add_error(Line, {explicit_export,F,A}, St1)} end; - false -> {Asvt,add_error(Line, illegal_guard_expr, St1)} + false -> + {Asvt,add_error(Line, illegal_guard_expr, St1)} end; gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), @@ -1998,10 +2014,11 @@ expr({'fun',Line,Body}, Vt, St) -> {Bvt, St1} = fun_clauses(Cs, Vt, St), {vtupdate(Bvt, Vt), St1}; {function,F,A} -> + %% BifClash - Fun expression %% N.B. Only allows BIFs here as well, NO IMPORTS!! - case ((not is_local_function(St#lint.locals,{F,A})) andalso %% PaN - Fun expression + case ((not is_local_function(St#lint.locals,{F,A})) andalso (erl_internal:bif(F, A) andalso - (not is_autoimport_supressed(St#lint.no_auto,{F,A})))) of + (not is_autoimport_suppressed(St#lint.no_auto,{F,A})))) of true -> {[],St}; false -> {[],call_function(Line, F, A, St)} end; @@ -2036,8 +2053,9 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> A = length(As), IsLocal = is_local_function(St2#lint.locals,{F,A}), IsAutoBif = erl_internal:bif(F, A), - AutoSupressed = is_autoimport_supressed(St2#lint.no_auto,{F,A}), - case ((not IsLocal) andalso IsAutoBif andalso (not AutoSupressed)) of %% PaN - function call + AutoSuppressed = is_autoimport_suppressed(St2#lint.no_auto,{F,A}), + Warn = is_warn_enabled(bif_clash, St2) and (not bif_clash_specifically_disabled(St2,{F,A})), + case ((not IsLocal) andalso IsAutoBif andalso (not AutoSuppressed)) of true -> St3 = deprecated_function(Line, erlang, F, As, St2), {Asvt,St3}; @@ -2052,27 +2070,33 @@ expr({call,Line,{atom,La,F},As}, Vt, St0) -> case {F,A} of {record_info,2} -> check_record_info_call(Line,La,As,St2); - N when N =:= St2#lint.func -> - St2; - _ -> + N -> + %% BifClash - function call + %% Issue these warnings/errors even if it's a recursive call St3 = if - (not AutoSupressed) andalso IsAutoBif -> + (not AutoSuppressed) andalso IsAutoBif andalso Warn -> case erl_internal:old_bif(F,A) of true -> add_error (Line, - {redefine_old_bif, {F,A}}, + {call_to_redefined_old_bif, {F,A}}, St2); false -> add_warning (Line, - {redefine_bif, {F,A}}, + {call_to_redefined_bif, {F,A}}, St2) end; true -> St2 end, - call_function(Line, F, A, St3) + %% ...but don't lint recursive calls + if + N =:= St3#lint.func -> + St3; + true -> + call_function(Line, F, A, St3) + end end end} end; @@ -3502,22 +3526,55 @@ expand_package(M, St0) -> end end. + +%% Prebuild set of local functions (to override auto-import) local_functions(Forms) -> gb_sets:from_list([ {Func,Arity} || {function,_,Func,Arity,_} <- Forms ]). - +%% Predicate to find out if the function is locally defined is_local_function(LocalSet,{Func,Arity}) -> gb_sets:is_element({Func,Arity},LocalSet). - +%% Predicate to see if a function is explicitly imported is_imported_function(ImportSet,{Func,Arity}) -> case orddict:find({Func,Arity}, ImportSet) of {ok,_Mod} -> true; error -> false end. - -auto_import_supressed(CompileFlags) -> +%% Predicate to see if a function is explicitly imported from the erlang module +is_imported_from_erlang(ImportSet,{Func,Arity}) -> + case orddict:find({Func,Arity}, ImportSet) of + {ok,erlang} -> true; + _ -> false + end. +%% Build set of functions where auto-import is explicitly supressed +auto_import_suppressed(CompileFlags) -> L0 = [ X || {no_auto_import,X} <- CompileFlags ], L1 = [ {Y,Z} || {Y,Z} <- lists:flatten(L0), is_atom(Y), is_integer(Z) ], gb_sets:from_list(L1). - -is_autoimport_supressed(NoAutoSet,{Func,Arity}) -> +%% Predicate to find out if autoimport is explicitly supressed for a function +is_autoimport_suppressed(NoAutoSet,{Func,Arity}) -> gb_sets:is_element({Func,Arity},NoAutoSet). +%% Predicate to find out if a function specific bif-clash supression (old deprecated) is present +bif_clash_specifically_disabled(St,{F,A}) -> + Nowarn = nowarn_function(nowarn_bif_clash, St#lint.compile), + lists:member({F,A},Nowarn). + +%% Predicate to find out if an autoimported guard_bif is not overriden in some way +%% Guard Bif without module name is disallowed if +%% * It is overridden by local function +%% * It is overridden by -import and that import is not of itself (i.e. from module erlang) +%% * The autoimport is suppressed or it's not reimported by -import directive +%% Otherwise it's OK (given that it's actually a guard bif and actually is autoimported) +no_guard_bif_clash(St,{F,A}) -> + ( + (not is_local_function(St#lint.locals,{F,A})) + andalso + ( + (not is_imported_function(St#lint.imports,{F,A})) orelse + is_imported_from_erlang(St#lint.imports,{F,A}) + ) + andalso + ( + (not is_autoimport_suppressed(St#lint.no_auto, {F,A})) orelse + is_imported_from_erlang(St#lint.imports,{F,A}) + ) + ). diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 8581b496aa..4a366f6a28 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1784,6 +1784,9 @@ otp_5362(Config) when is_list(Config) -> {15,erl_lint,{undefined_field,ok,nix}}, {16,erl_lint,{field_name_is_variable,ok,'Var'}}]}}, + %% Nowarn_bif_clash has changed behaviour as local functions + %% nowdays supersede auto-imported BIFs, why nowarn_bif_clash in itself generates a warning + %% (OTP-8579) /PaN {otp_5362_4, <<"-compile(nowarn_deprecated_function). -compile(nowarn_bif_clash). @@ -1795,9 +1798,8 @@ otp_5362(Config) when is_list(Config) -> warn_deprecated_function, warn_bif_clash]}, {error, - [{5,erl_lint,{call_to_redefined_bif,{spawn,1}}}], - [{3,erl_lint,{redefine_bif,{spawn,1}}}, - {4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2}, + [{5,erl_lint,{call_to_redefined_old_bif,{spawn,1}}}], + [{4,erl_lint,{deprecated,{erlang,hash,2},{erlang,phash2,2}, "in a future release"}}]}}, {otp_5362_5, @@ -1809,7 +1811,7 @@ otp_5362(Config) when is_list(Config) -> ">>, {[nowarn_unused_function]}, {warnings, - [{3,erl_lint,{redefine_bif,{spawn,1}}}]}}, + [{2,erl_lint,deprecated_nowarn_bif_clash}]}}, %% The special nowarn_X are not affected by general warn_X. {otp_5362_6, @@ -1823,7 +1825,7 @@ otp_5362(Config) when is_list(Config) -> warn_deprecated_function, warn_bif_clash]}, {warnings, - [{3,erl_lint,{redefine_bif,{spawn,1}}}]}}, + [{2,erl_lint,deprecated_nowarn_bif_clash}]}}, {otp_5362_7, <<"-export([spawn/1]). @@ -1839,7 +1841,9 @@ otp_5362(Config) when is_list(Config) -> ">>, {[nowarn_unused_function]}, {error,[{4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}], - [{5,erl_lint,{bad_nowarn_deprecated_function,{3,hash,-1}}}, + [{3,erl_lint,deprecated_nowarn_bif_clash}, + {4,erl_lint,deprecated_nowarn_bif_clash}, + {5,erl_lint,{bad_nowarn_deprecated_function,{3,hash,-1}}}, {5,erl_lint,{bad_nowarn_deprecated_function,{erlang,hash,-1}}}, {5,erl_lint,{bad_nowarn_deprecated_function,{{a,b,c},hash,-1}}}]} }, @@ -1865,7 +1869,21 @@ otp_5362(Config) when is_list(Config) -> t() -> #a{}. ">>, {[]}, - []} + []}, + + {otp_5362_10, + <<"-compile({nowarn_deprecated_function,{erlang,hash,2}}). + -compile({nowarn_bif_clash,{spawn,1}}). + -import(x,[spawn/1]). + spin(A) -> + erlang:hash(A, 3000), + spawn(A). + ">>, + {[nowarn_unused_function, + warn_deprecated_function, + warn_bif_clash]}, + {warnings, + [{2,erl_lint,deprecated_nowarn_bif_clash}]}} ], @@ -2389,7 +2407,7 @@ bif_clash(Config) when is_list(Config) -> N. ">>, [], - {errors,[{2,erl_lint,{call_to_redefined_bif,{size,1}}}],[]}}, + {errors,[{2,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}}, %% Verify that (some) warnings can be turned off. {clash2, @@ -2400,17 +2418,141 @@ bif_clash(Config) when is_list(Config) -> size({N,_}) -> N. - %% My own abs/1 function works on lists too. - %% Unfortunately, it is not exported, so there will - %% be a warning that can't be turned off. + %% My own abs/1 function works on lists too. From R14 this really works. abs([H|T]) when $a =< H, H =< $z -> [H-($a-$A)|abs(T)]; abs([H|T]) -> [H|abs(T)]; abs([]) -> []; abs(X) -> erlang:abs(X). ">>, - {[nowarn_bif_clash]}, - {warnings,[{11,erl_lint,{redefine_bif,{abs,1}}}, - {11,erl_lint,{unused_function,{abs,1}}}]}}], + {[nowarn_unused_function,nowarn_bif_clash]}, + {warnings,[{erl_lint,deprecated_nowarn_bif_clash}]}}, + %% As long as noone calls an overridden BIF, it's totally OK + {clash3, + <<"-export([size/1]). + size({N,_}) -> + N; + size(X) -> + erlang:size(X). + ">>, + [], + []}, + %% But this is totally wrong - meaning of the program changed in R14, so this is an error + {clash4, + <<"-export([size/1]). + size({N,_}) -> + N; + size(X) -> + size(X). + ">>, + [], + {errors,[{5,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}}, + %% For a post R14 bif, its only a warning + {clash5, + <<"-export([binary_part/2]). + binary_part({B,_},{X,Y}) -> + binary_part(B,{X,Y}); + binary_part(B,{X,Y}) -> + binary:part(B,X,Y). + ">>, + [], + {warnings,[{3,erl_lint,{call_to_redefined_bif,{binary_part,2}}}]}}, + %% If you really mean to call yourself here, you can "unimport" size/1 + {clash6, + <<"-export([size/1]). + -compile({no_auto_import,[size/1]}). + size([]) -> + 0; + size({N,_}) -> + N; + size([_|T]) -> + 1+size(T). + ">>, + [], + []}, + %% Same for the post R14 autoimport warning + {clash7, + <<"-export([binary_part/2]). + -compile({no_auto_import,[binary_part/2]}). + binary_part({B,_},{X,Y}) -> + binary_part(B,{X,Y}); + binary_part(B,{X,Y}) -> + binary:part(B,X,Y). + ">>, + [], + []}, + %% but this doesn't mean the local function is allowed in a guard... + {clash8, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + x(X) when binary_part(X,{1,2}) =:= <<1,2>> -> + hej. + binary_part({B,_},{X,Y}) -> + binary_part(B,{X,Y}); + binary_part(B,{X,Y}) -> + binary:part(B,X,Y). + ">>, + [], + {errors,[{3,erl_lint,illegal_guard_expr}],[]}}, + %% no_auto_import is not like nowarn_bif_clash, it actually removes the autoimport + {clash9, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + x(X) -> + binary_part(X,{1,2}) =:= <<1,2>>. + ">>, + [], + {errors,[{4,erl_lint,{undefined_function,{binary_part,2}}}],[]}}, + %% but we could import it again... + {clash10, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(erlang,[binary_part/2]). + x(X) -> + binary_part(X,{1,2}) =:= <<1,2>>. + ">>, + [], + []}, + %% and actually use it in a guard... + {clash11, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(erlang,[binary_part/2]). + x(X) when binary_part(X,{0,1}) =:= <<0>> -> + binary_part(X,{1,2}) =:= <<1,2>>. + ">>, + [], + []}, + %% but for non-obvious historical reasons, imported functions cannot be used in + %% fun construction without the module name... + {clash12, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(erlang,[binary_part/2]). + x(X) when binary_part(X,{0,1}) =:= <<0>> -> + binary_part(X,{1,2}) =:= fun binary_part/2. + ">>, + [], + {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}}, + %% Not from erlang and not from anywhere else + {clash13, + <<"-export([x/1]). + -compile({no_auto_import,[binary_part/2]}). + -import(x,[binary_part/2]). + x(X) -> + binary_part(X,{1,2}) =:= fun binary_part/2. + ">>, + [], + {errors,[{5,erl_lint,{undefined_function,{binary_part,2}}}],[]}}, + %% ...while real auto-import is OK. + {clash14, + <<"-export([x/1]). + x(X) when binary_part(X,{0,1}) =:= <<0>> -> + binary_part(X,{1,2}) =:= fun binary_part/2. + ">>, + [], + []} + + ], ?line [] = run(Config, Ts), ok. -- cgit v1.2.3 From 334e4acd61605111712edefe874f98d030f0d25c Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Tue, 1 Jun 2010 11:12:14 +0200 Subject: Change warning to error for nowarn_bif_clash compiler directive --- lib/stdlib/src/erl_lint.erl | 25 +++++++++++++------------ lib/stdlib/test/erl_lint_SUITE.erl | 26 +++++++++++++------------- 2 files changed, 26 insertions(+), 25 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 631ad0c1e3..29a949432b 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -163,8 +163,8 @@ format_error({bad_nowarn_unused_function,{F,A}}) -> io_lib:format("function ~w/~w undefined", [F,A]); format_error({bad_nowarn_bif_clash,{F,A}}) -> io_lib:format("function ~w/~w undefined", [F,A]); -format_error(deprecated_nowarn_bif_clash) -> - io_lib:format("compile directive nowarn_bif_clash is deprecated,~n" +format_error(disallowed_nowarn_bif_clash) -> + io_lib:format("compile directive nowarn_bif_clash is no longer allowed,~n" " - use explicit module names or -compile({no_auto_import, [F/A]})", []); format_error({bad_nowarn_deprecated_function,{M,F,A}}) -> io_lib:format("~w:~w/~w is not a deprecated function", [M,F,A]); @@ -553,7 +553,7 @@ forms(Forms0, St0) -> Forms = eval_file_attribute(Forms0, St0), Locals = local_functions(Forms), AutoImportSuppressed = auto_import_suppressed(St0#lint.compile), - StDeprecated = deprecated_compile_flags(Forms,St0), + StDeprecated = disallowed_compile_flags(Forms,St0), %% Line numbers are from now on pairs {File,Line}. St1 = includes_qlc_hrl(Forms, StDeprecated#lint{locals = Locals, no_auto = AutoImportSuppressed}), @@ -762,22 +762,23 @@ not_deprecated(Forms, St0) -> St1 = func_line_warning(bad_nowarn_deprecated_function, Bad, St0), St1#lint{not_deprecated = ordsets:from_list(Nowarn)}. -deprecated_compile_flags(Forms, St0) -> +%% The nowarn_bif_clash directive is not only deprecated, it's actually an error from R14A +disallowed_compile_flags(Forms, St0) -> %% There are (still) no line numbers in St0#lint.compile. - Warnings0 = [ {St0#lint.file,{L,erl_lint,deprecated_nowarn_bif_clash}} || + Errors0 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} || {attribute,[{line,{_,L}}],compile,nowarn_bif_clash} <- Forms ], - Warnings1 = [ {St0#lint.file,{L,erl_lint,deprecated_nowarn_bif_clash}} || + Errors1 = [ {St0#lint.file,{L,erl_lint,disallowed_nowarn_bif_clash}} || {attribute,[{line,{_,L}}],compile,{nowarn_bif_clash, {_,_}}} <- Forms ], Disabled = (not is_warn_enabled(bif_clash, St0)), - Warnings = if - Disabled andalso Warnings0 =:= [] -> - [{St0#lint.file,{erl_lint,deprecated_nowarn_bif_clash}} | St0#lint.warnings]; + Errors = if + Disabled andalso Errors0 =:= [] -> + [{St0#lint.file,{erl_lint,disallowed_nowarn_bif_clash}} | St0#lint.errors]; Disabled -> - Warnings0 ++ Warnings1 ++ St0#lint.warnings; + Errors0 ++ Errors1 ++ St0#lint.errors; true -> - Warnings1 ++ St0#lint.warnings + Errors1 ++ St0#lint.errors end, - St0#lint{warnings=Warnings}. + St0#lint{errors=Errors}. %% post_traversal_check(Forms, State0) -> State. %% Do some further checking after the forms have been traversed and diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 4a366f6a28..8fe7881081 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1785,7 +1785,7 @@ otp_5362(Config) when is_list(Config) -> {16,erl_lint,{field_name_is_variable,ok,'Var'}}]}}, %% Nowarn_bif_clash has changed behaviour as local functions - %% nowdays supersede auto-imported BIFs, why nowarn_bif_clash in itself generates a warning + %% nowdays supersede auto-imported BIFs, why nowarn_bif_clash in itself generates an error %% (OTP-8579) /PaN {otp_5362_4, <<"-compile(nowarn_deprecated_function). @@ -1810,8 +1810,8 @@ otp_5362(Config) when is_list(Config) -> spawn(A). ">>, {[nowarn_unused_function]}, - {warnings, - [{2,erl_lint,deprecated_nowarn_bif_clash}]}}, + {errors, + [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}, %% The special nowarn_X are not affected by general warn_X. {otp_5362_6, @@ -1824,8 +1824,8 @@ otp_5362(Config) when is_list(Config) -> {[nowarn_unused_function, warn_deprecated_function, warn_bif_clash]}, - {warnings, - [{2,erl_lint,deprecated_nowarn_bif_clash}]}}, + {errors, + [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}}, {otp_5362_7, <<"-export([spawn/1]). @@ -1840,10 +1840,10 @@ otp_5362(Config) when is_list(Config) -> spawn(A). ">>, {[nowarn_unused_function]}, - {error,[{4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}], - [{3,erl_lint,deprecated_nowarn_bif_clash}, - {4,erl_lint,deprecated_nowarn_bif_clash}, - {5,erl_lint,{bad_nowarn_deprecated_function,{3,hash,-1}}}, + {error,[{3,erl_lint,disallowed_nowarn_bif_clash}, + {4,erl_lint,disallowed_nowarn_bif_clash}, + {4,erl_lint,{bad_nowarn_bif_clash,{spawn,2}}}], + [{5,erl_lint,{bad_nowarn_deprecated_function,{3,hash,-1}}}, {5,erl_lint,{bad_nowarn_deprecated_function,{erlang,hash,-1}}}, {5,erl_lint,{bad_nowarn_deprecated_function,{{a,b,c},hash,-1}}}]} }, @@ -1882,8 +1882,8 @@ otp_5362(Config) when is_list(Config) -> {[nowarn_unused_function, warn_deprecated_function, warn_bif_clash]}, - {warnings, - [{2,erl_lint,deprecated_nowarn_bif_clash}]}} + {errors, + [{2,erl_lint,disallowed_nowarn_bif_clash}],[]}} ], @@ -2409,7 +2409,7 @@ bif_clash(Config) when is_list(Config) -> [], {errors,[{2,erl_lint,{call_to_redefined_old_bif,{size,1}}}],[]}}, - %% Verify that (some) warnings can be turned off. + %% Verify that warnings can not be turned off in the old way. {clash2, <<"-export([t/1,size/1]). t(X) -> @@ -2425,7 +2425,7 @@ bif_clash(Config) when is_list(Config) -> abs(X) -> erlang:abs(X). ">>, {[nowarn_unused_function,nowarn_bif_clash]}, - {warnings,[{erl_lint,deprecated_nowarn_bif_clash}]}}, + {errors,[{erl_lint,disallowed_nowarn_bif_clash}],[]}}, %% As long as noone calls an overridden BIF, it's totally OK {clash3, <<"-export([size/1]). -- cgit v1.2.3 From 0e4daed8ff441645c94723332c6742944b2cc547 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 2 Jun 2010 08:41:53 +0200 Subject: Improve coverage of erl_int in testcases --- lib/stdlib/test/erl_lint_SUITE.erl | 52 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 8fe7881081..01f494ee38 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -2550,8 +2550,56 @@ bif_clash(Config) when is_list(Config) -> binary_part(X,{1,2}) =:= fun binary_part/2. ">>, [], - []} - + []}, + %% Import directive clashing with old bif is an error, regardless of if it's called or not + {clash15, + <<"-export([x/1]). + -import(x,[abs/1]). + x(X) -> + binary_part(X,{1,2}). + ">>, + [], + {errors,[{2,erl_lint,{redefine_old_bif_import,{abs,1}}}],[]}}, + %% For a new BIF, it's only a warning + {clash16, + <<"-export([x/1]). + -import(x,[binary_part/3]). + x(X) -> + abs(X). + ">>, + [], + {warnings,[{2,erl_lint,{redefine_bif_import,{binary_part,3}}}]}}, + %% And, you cannot redefine already imported things that aren't auto-imported + {clash17, + <<"-export([x/1]). + -import(x,[binary_port/3]). + -import(y,[binary_port/3]). + x(X) -> + abs(X). + ">>, + [], + {errors,[{3,erl_lint,{redefine_import,{{binary_port,3},x}}}],[]}}, + %% Not with local functions either + {clash18, + <<"-export([x/1]). + -import(x,[binary_port/3]). + binary_port(A,B,C) -> + binary_part(A,B,C). + x(X) -> + abs(X). + ">>, + [], + {errors,[{3,erl_lint,{define_import,{binary_port,3}}}],[]}}, + %% Like clash8: Dont accept a guard if it's explicitly module-name called either + {clash19, + <<"-export([binary_port/3]). + -compile({no_auto_import,[binary_part/3]}). + -import(x,[binary_part/3]). + binary_port(A,B,C) when x:binary_part(A,B,C) -> + binary_part(A,B,C+1). + ">>, + [], + {errors,[{4,erl_lint,illegal_guard_expr}],[]}} ], ?line [] = run(Config, Ts), -- cgit v1.2.3 From 656e4790551b2211ff51c3ca24adbd07b135327e Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 2 Jun 2010 12:00:05 +0200 Subject: Autoimport min/2 and max/2 --- lib/stdlib/src/erl_internal.erl | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index 6a7c62f101..ed7e011c54 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -299,6 +299,8 @@ bif(list_to_pid, 1) -> true; bif(list_to_tuple, 1) -> true; bif(load_module, 2) -> true; bif(make_ref, 0) -> true; +bif(max,2) -> true; +bif(min,2) -> true; bif(module_loaded, 1) -> true; bif(monitor_node, 2) -> true; bif(node, 0) -> true; -- cgit v1.2.3 From 1b8f86d8314389bf6033d23d3e6d707514ba7612 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 2 Jun 2010 15:22:19 +0200 Subject: Remove (harmless) warnings about min/max in core applications --- lib/stdlib/src/file_sorter.erl | 9 +++------ lib/stdlib/src/lists.erl | 3 +++ 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/file_sorter.erl b/lib/stdlib/src/file_sorter.erl index e21a0c88f3..3875eca39d 100644 --- a/lib/stdlib/src/file_sorter.erl +++ b/lib/stdlib/src/file_sorter.erl @@ -191,7 +191,7 @@ options([{format, Format} | L], Opts) when Format =:= binary; options([{format, binary_term} | L], Opts) -> options(L, Opts#opts{format = binary_term_fun()}); options([{size, Size} | L], Opts) when is_integer(Size), Size >= 0 -> - options(L, Opts#opts{size = max(Size, 1)}); + options(L, Opts#opts{size = erlang:max(Size, 1)}); options([{no_files, NoFiles} | L], Opts) when is_integer(NoFiles), NoFiles > 1 -> options(L, Opts#opts{no_files = NoFiles}); @@ -997,10 +997,10 @@ close_read_fun(Fd, FileName, fsort) -> file:delete(FileName). read_objs(Fd, FileName, I, L, Bin0, Size0, LSz, W) -> - Max = max(Size0, ?CHUNKSIZE), + Max = erlang:max(Size0, ?CHUNKSIZE), BSz0 = byte_size(Bin0), Min = Size0 - BSz0 + W#w.hdlen, % Min > 0 - NoBytes = max(Min, Max), + NoBytes = erlang:max(Min, Max), case read(Fd, FileName, NoBytes, W) of {ok, Bin} -> BSz = byte_size(Bin), @@ -1180,9 +1180,6 @@ make_key2([Kp], T) -> make_key2([Kp | Kps], T) -> [element(Kp, T) | make_key2(Kps, T)]. -max(A, B) when A < B -> B; -max(A, _) -> A. - infun(W) -> W1 = W#w{in = undefined}, try (W#w.in)(read) of diff --git a/lib/stdlib/src/lists.erl b/lib/stdlib/src/lists.erl index 857eda8161..08ee595f4d 100644 --- a/lib/stdlib/src/lists.erl +++ b/lib/stdlib/src/lists.erl @@ -18,6 +18,9 @@ %% -module(lists). +-compile({no_auto_import,[max/2]}). +-compile({no_auto_import,[min/2]}). + -export([append/2, append/1, subtract/2, reverse/1, nth/2, nthtail/2, prefix/2, suffix/2, last/1, seq/2, seq/3, sum/1, duplicate/2, min/1, max/1, sublist/2, sublist/3, -- cgit v1.2.3 From 750f53bfa7bfd6258504d2f7c441a103d885cce5 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 2 Jun 2010 15:33:59 +0200 Subject: Make port_command/3 auto-imported --- lib/stdlib/src/erl_internal.erl | 1 + 1 file changed, 1 insertion(+) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index ed7e011c54..bf6e5bc5ca 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -312,6 +312,7 @@ bif(open_port, 2) -> true; bif(pid_to_list, 1) -> true; bif(port_close, 1) -> true; bif(port_command, 2) -> true; +bif(port_command, 3) -> true; bif(port_connect, 2) -> true; bif(port_control, 3) -> true; bif(pre_loaded, 0) -> true; -- cgit v1.2.3 From 1461bf4ae09eb97532cec57beefbbf5ca0d52c43 Mon Sep 17 00:00:00 2001 From: Patrik Nyblom Date: Wed, 2 Jun 2010 16:52:22 +0200 Subject: Remove outcommented code from erl_lint --- lib/stdlib/src/erl_lint.erl | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'lib/stdlib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 29a949432b..229d455e06 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -741,13 +741,6 @@ bif_clashes(Forms, St) -> Clashes = ordsets:subtract(ordsets:from_list(Clashes0), Nowarn), St#lint{clashes=Clashes}. -%% -spec is_bif_clash(atom(), byte(), lint_state()) -> boolean(). - -%% is_bif_clash(_Name, _Arity, #lint{clashes=[]}) -> -%% false; -%% is_bif_clash(Name, Arity, #lint{clashes=Clashes}) -> -%% ordsets:is_element({Name,Arity}, Clashes). - %% not_deprecated(Forms, State0) -> State not_deprecated(Forms, St0) -> @@ -1733,8 +1726,6 @@ gexpr({cons,_Line,H,T}, Vt, St) -> gexpr_list([H,T], Vt, St); gexpr({tuple,_Line,Es}, Vt, St) -> gexpr_list(Es, Vt, St); -%%gexpr({struct,_Line,_Tag,Es}, Vt, St) -> -%% gexpr_list(Es, Vt, St); gexpr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end ); @@ -1952,8 +1943,6 @@ expr({bc,_Line,E,Qs}, Vt0, St0) -> {vtold(Vt,Vt0),St}; %Don't export local variables expr({tuple,_Line,Es}, Vt, St) -> expr_list(Es, Vt, St); -%%expr({struct,Line,Tag,Es}, Vt, St) -> -%% expr_list(Es, Vt, St); expr({record_index,Line,Name,Field}, _Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> record_field(Field, Name, Dfs, St1) end); -- cgit v1.2.3