erl_lint.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,705 行 · 第 1/5 页
ERL
1,705 行
case deprecated_flag(Flg) of false -> [{invalid_deprecated,D}]; true -> depr_fa(F, A, X, Mod) end;depr_cat({F, A}, X, Mod) -> depr_fa(F, A, X, Mod);depr_cat(module, _X, _Mod) -> [];depr_cat(D, _X, _Mod) -> [{invalid_deprecated,D}].depr_fa('_', '_', _X, _Mod) -> [];depr_fa(F, '_', X, _Mod) when is_atom(F) -> %% Don't use this syntax for built-in functions. case lists:filter(fun({F1,_}) -> F1 =:= F end, X) of [] -> [{bad_deprecated,{F,'_'}}]; _ -> [] end;depr_fa(F, A, X, Mod) when is_atom(F), is_integer(A), A >= 0 -> case lists:member({F,A}, X) of true -> []; false -> case erlang:is_builtin(Mod, F, A) of true -> []; false -> [{bad_deprecated,{F,A}}] end end;depr_fa(F, A, _X, _Mod) -> [{invalid_deprecated,{F,A}}].deprecated_flag(next_version) -> true;deprecated_flag(next_major_release) -> true;deprecated_flag(eventually) -> true;deprecated_flag(_) -> false.%% check_imports(Forms, State0) -> Statecheck_imports(Forms, St0) -> case is_warn_enabled(unused_import, St0) of false -> St0; true -> Usage = St0#lint.usage, Unused = ordsets:subtract(St0#lint.imports, Usage#usage.imported), Imports = [{{FA,list_to_atom(package_to_string(Mod))},L} || {attribute,L,import,{Mod,Fs}} <- Forms, FA <- lists:usort(Fs)], Bad = [{FM,L} || FM <- Unused, {FM2,L} <- Imports, FM =:= FM2], func_line_warning(unused_import, Bad, St0) end.%% check_inlines(Forms, State0) -> Statecheck_inlines(Forms, St0) -> check_option_functions(Forms, inline, bad_inline, St0).%% check_unused_functions(Forms, State0) -> Statecheck_unused_functions(Forms, St0) -> St1 = check_option_functions(Forms, nowarn_unused_function, bad_nowarn_unused_function, St0), Opts = St1#lint.compile, case member(export_all, Opts) orelse not is_warn_enabled(unused_function, St1) of true -> St1; false -> Nowarn = nowarn_function(nowarn_unused_function, Opts), Usage = St1#lint.usage, Used = reached_functions(gb_sets:to_list(St1#lint.exports), Usage#usage.calls), UsedOrNowarn = ordsets:union(Used, Nowarn), Unused = ordsets:subtract(gb_sets:to_list(St1#lint.defined), UsedOrNowarn), Functions = [{{N,A},L} || {function,L,N,A,_} <- Forms], Bad = [{FA,L} || FA <- Unused, {FA2,L} <- Functions, FA =:= FA2], func_line_warning(unused_function, Bad, St1) end.%% reached_functions(RootSet, CallRef) -> [ReachedFunc].%% reached_functions(RootSet, CallRef, [ReachedFunc]) -> [ReachedFunc].reached_functions(Root, Ref) -> reached_functions(Root, [], Ref, gb_sets:empty()).reached_functions([R|Rs], More0, Ref, Reached0) -> case gb_sets:is_element(R, Reached0) of true -> reached_functions(Rs, More0, Ref, Reached0); false -> Reached = gb_sets:add_element(R, Reached0), %It IS reached case dict:find(R, Ref) of {ok,More} -> reached_functions(Rs, [More|More0], Ref, Reached); error -> reached_functions(Rs, More0, Ref, Reached) end end;reached_functions([], [_|_]=More, Ref, Reached) -> reached_functions(lists:append(More), [], Ref, Reached);reached_functions([], [], _Ref, Reached) -> gb_sets:to_list(Reached).%% check_undefined_functions(State0) -> Statecheck_undefined_functions(#lint{called=Called0,defined=Def0}=St0) -> Called = sofs:relation(Called0, [{func,location}]), Def = sofs:from_external(gb_sets:to_list(Def0), [func]), Undef = sofs:to_external(sofs:drestriction(Called, Def)), foldl(fun ({NA,L}, St) -> add_error(L, {undefined_function,NA}, St) end, St0, Undef).%% check_bif_clashes(Forms, State0) -> Statecheck_bif_clashes(Forms, St0) -> %% St0#lint.defined is now complete. check_option_functions(Forms, nowarn_bif_clash, bad_nowarn_bif_clash, St0).check_option_functions(Forms, Tag0, Type, St0) -> %% There are no line numbers in St0#lint.compile. FAsL = [{FA,L} || {attribute, L, compile, Args} <- Forms, {Tag, FAs0} <- lists:flatten([Args]), Tag0 =:= Tag, FA <- lists:flatten([FAs0])], DefFunctions = gb_sets:to_list(St0#lint.defined) -- pseudolocals(), Bad = [{FA,L} || {FA,L} <- FAsL, not member(FA, DefFunctions)], func_line_error(Type, Bad, St0).nowarn_function(Tag, Opts) -> ordsets:from_list([FA || {Tag1,FAs} <- Opts, Tag1 =:= Tag, FA <- lists:flatten([FAs])]).func_line_warning(Type, Fs, St) -> foldl(fun ({F,Line}, St0) -> add_warning(Line, {Type,F}, St0) end, St, Fs).func_line_error(Type, Fs, St) -> foldl(fun ({F,Line}, St0) -> add_error(Line, {Type,F}, St0) end, St, Fs).check_unused_records(Forms, St0) -> AttrFiles = [File || {attribute,_L,file,{File,_Line}} <- Forms], case {is_warn_enabled(unused_record, St0),AttrFiles} of {true,[FirstFile|_]} -> %% The check is a bit imprecise in that uses from unused %% functions count. Usage = St0#lint.usage, UsedRecords = sets:to_list(Usage#usage.used_records), URecs = foldl(fun (Used, Recs) -> dict:erase(Used, Recs) end, St0#lint.records, UsedRecords), Unused = [{Name,Line} || {Name,{Line,_Fields}} <- dict:to_list(URecs), element(1, Line) =:= FirstFile], foldl(fun ({N,L}, St) -> add_warning(L, {unused_record,N}, St) end, St0, Unused); _ -> St0 end.%% For storing the import list we use the orddict module. %% We know an empty set is [].%% export(Line, Exports, State) -> State.%% Mark functions as exported, also as called from the export line.export(Line, Es, #lint{exports=Es0}=St0) -> {Es1,C1,St1} = foldl(fun (NA, {E,C,St2}) -> St = case gb_sets:is_element(NA, E) of true -> add_warning(Line, {duplicated_export, NA}, St2); false -> St2 end, {gb_sets:add_element(NA, E), [{NA,Line}|C], St} end, {Es0,St0#lint.called,St0}, Es), St1#lint{exports=Es1,called=C1}.%% import(Line, Imports, State) -> State.%% imported(Name, Arity, State) -> {yes,Module} | no.import(Line, {Mod,Fs}, St) -> Mod1 = package_to_string(Mod), case packages:is_valid(Mod1) of true -> Mfs = ordsets:from_list(Fs), case check_imports(Line, Mfs, St#lint.imports) of [] -> 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) end, St, Efs) end; false -> add_error(Line, {bad_module_name, Mod1}, St) end;import(Line, Mod, St) -> Mod1 = package_to_string(Mod), case packages:is_valid(Mod1) of true -> Key = list_to_atom(packages:last(Mod1)), Imps = St#lint.mod_imports,%%% case dict:is_key(Key, Imps) of%%% true ->%%% M = packages:last(Mod1),%%% P = packages:strip_last(Mod1),%%% add_error(Line, {redefine_mod_import, M, P}, St);%%% false ->%%% St#lint{mod_imports =%%% dict:store(Key, list_to_atom(Mod1), Imps)}%%% end; St#lint{mod_imports = dict:store(Key, list_to_atom(Mod1), Imps)}; false -> add_error(Line, {bad_module_name, Mod1}, St) end.check_imports(_Line, Fs, Is) -> foldl(fun (F, Efs) -> case orddict:find(F, Is) of {ok,Mod} -> [{F,Mod}|Efs]; error -> {N,A} = F, case erl_internal:bif(N, A) of true -> [{bif,F,erlang}|Efs]; false -> Efs end end end, [], Fs).add_imports(Mod, Fs, Is) -> foldl(fun (F, Is0) -> orddict:store(F, Mod, Is0) end, Is, Fs).imported(F, A, St) -> case orddict:find({F,A}, St#lint.imports) of {ok,Mod} -> {yes,Mod}; error -> no end.%% call_function(Line, Name, Arity, State) -> State.%% Add to both called and calls.call_function(Line, F, A, #lint{usage=Usage0,called=Cd,func=Func}=St) -> #usage{calls = Cs} = Usage0, NA = {F,A}, Usage = case Cs of undefined -> Usage0; _ -> Usage0#usage{calls=dict:append(Func, NA, Cs)} 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, Name, Arity, Cs, St0) -> St1 = define_function(Line, Name, Arity, St0#lint{func={Name,Arity}}), clauses(Cs, St1#lint.global_vt, St1).%% define_function(Line, Name, Arity, State) -> State.define_function(Line, Name, Arity, St0) -> St1 = keyword_warning(Line, Name, St0), NA = {Name,Arity}, case gb_sets:is_member(NA, St1#lint.defined) of true -> 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) 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 end end.%% clauses([Clause], VarTable, State) -> {VarTable, State}.clauses(Cs, Vt, St) -> foldl(fun (C, St0) -> {_,St1} = clause(C, Vt, St0), St1 end, St, Cs).clause({clause,_Line,H,G,B}, Vt0, St0) -> {Hvt,Binvt,St1} = head(H, Vt0, St0), %% Cannot ignore BinVt since "binsize variables" may have been used. Vt1 = vtupdate(Hvt, vtupdate(Binvt, Vt0)), {Gvt,St2} = guard(G, Vt1, St1), Vt2 = vtupdate(Gvt, Vt1), {Bvt,St3} = exprs(B, Vt2, St2), Upd = vtupdate(Bvt, Vt2), check_unused_vars(Upd, Vt0, St3).%% head([HeadPattern], VarTable, State) ->%% {VarTable,BinVarTable,State}%% Check a patterns in head returning "all" variables. Not updating the%% known variable list will result in multiple error messages/warnings.head(Ps, Vt, St0) -> head(Ps, Vt, Vt, St0). % Old = Vthead([P|Ps], Vt, Old, St0) -> {Pvt,Bvt1,St1} = pattern(P, Vt, Old, [], St0), {Psvt,Bvt2,St2} = head(Ps, Vt, Old, St1), {vtmerge_pat(Pvt, Psvt),vtmerge_pat(Bvt1,Bvt2),St2};head([], _Vt, _Env, St) -> {[],[],St}.%% pattern(Pattern, VarTable, Old, BinVarTable, State) -> %% {UpdVarTable,BinVarTable,State}.%% Check pattern return variables. Old is the set of variables used for%% deciding whether an occurrence is a binding occurrence or a use, and%% VarTable is the set of variables used for arguments to binary%% patterns. UpdVarTable is updated when same variable in VarTable is%% used in the size part of a bit segment. All other information about%% used variables are recorded in BinVarTable. The caller can then decide%% what to do with it depending on whether variables in the pattern shadow%% variabler or not. This separation is one way of dealing with these:%% A = 4, fun(<<A:A>>) -> % A #2 unused%% A = 4, fun(<<A:8,16:A>>) -> % A #1 unusedpattern(P, Vt, St) -> pattern(P, Vt, Vt, [], St). % Old = Vtpattern({var,_Line,'_'}, _Vt, _Old, _Bvt, St) ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?