erl_lint.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,705 行 · 第 1/5 页
ERL
1,705 行
{Gsvt,St2} = guard_tests(Gs, vtupdate(Gvt, Vt), St1), {vtupdate(Gvt, Gsvt),St2};guard_tests([], _Vt, St) -> {[],St}.%% guard_test(Test, VarTable, State) ->%% {UsedVarTable,State'}%% Check one guard test, returns NewVariables. We now allow more%% expressions in guards including the new is_XXX type tests, but%% only allow the old type tests at the top level.guard_test(G, Vt, St0) -> St1 = obsolete_guard(G, St0), guard_test2(G, Vt, St1).%% Specially handle record type test here.guard_test2({call,Line,{atom,Lr,record},[E,A]}, Vt, St0) -> gexpr({call,Line,{atom,Lr,is_record},[E,A]}, Vt, St0);guard_test2({call,_Line,{atom,_La,F},As}=G, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), %Always check this. A = length(As), case erl_internal:type_test(F, A) of true when F =/= is_record -> {Asvt,St1}; _ -> gexpr(G, Vt, St0) end;guard_test2(G, Vt, St) -> %% Everything else is a guard expression. gexpr(G, Vt, St).%% gexpr(GuardExpression, VarTable, State) ->%% {UsedVarTable,State'}%% Check a guard expression, returns NewVariables.gexpr({var,Line,V}, Vt, St) -> expr_var(V, Line, Vt, St);gexpr({char,_Line,_C}, _Vt, St) -> {[],St};gexpr({integer,_Line,_I}, _Vt, St) -> {[],St};gexpr({float,_Line,_F}, _Vt, St) -> {[],St};gexpr({atom,Line,A}, _Vt, St) -> {[],keyword_warning(Line, A, St)};gexpr({string,_Line,_S}, _Vt, St) -> {[],St};gexpr({nil,_Line}, _Vt, St) -> {[],St};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 );gexpr({record_field,Line,_,_}=M, _Vt, St0) -> case expand_package(M, St0) of {error, St1} -> {[],add_error(Line, illegal_expr, St1)}; {_, St1} -> {[], St1} end;gexpr({record_field,Line,Rec,Name,Field}, Vt, St0) -> {Rvt,St1} = gexpr(Rec, Vt, St0), {Fvt,St2} = check_record(Line, Name, St1, fun (Dfs, St) -> record_field(Field, Name, Dfs, St) end), {vtmerge(Rvt, Fvt),St2};gexpr({record,Line,Name,Inits}, Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> ginit_fields(Inits, Line, Name, Dfs, Vt, St1) end);gexpr({bin,Line,Fs}, Vt,St) -> expr_bin(Line, Fs, Vt, St, fun gexpr/3);gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,Ln,Name}]}, Vt, St0) -> {Rvt,St1} = gexpr(E, Vt, St0), {Rvt,exist_record(Ln, Name, St1)};gexpr({call,Line,{atom,_Lr,is_record},[E,R]}, Vt, St0) -> {Asvt,St1} = gexpr_list([E,R], Vt, St0), {Asvt,add_error(Line, illegal_guard_expr, St1)};gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,Lf,is_record}},[E,A]}, Vt, St0) -> gexpr({call,Line,{atom,Lf,is_record},[E,A]}, Vt, St0);gexpr({call,_Line,{atom,_Lr,is_record},[E,{atom,_,_Name},{integer,_,_}]}, Vt, St0) -> gexpr(E, Vt, St0);gexpr({call,Line,{atom,_Lr,is_record},[_,_,_]=Asvt0}, Vt, St0) -> {Asvt,St1} = gexpr_list(Asvt0, Vt, St0), {Asvt,add_error(Line, illegal_guard_expr, St1)};gexpr({call,Line,{remote,_,{atom,_,erlang},{atom,_,is_record}=Isr},[_,_,_]=Args}, Vt, St0) -> gexpr({call,Line,Isr,Args}, Vt, St0);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 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)} end;gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) -> {Asvt,St1} = gexpr_list(As, Vt, St0), A = length(As), case erl_internal:guard_bif(F, A) orelse is_gexpr_op(F, A) of true -> {Asvt,St1}; false -> {Asvt,add_error(Line, illegal_guard_expr, St1)} end;gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St) -> gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, Vt, St);gexpr({op,Line,Op,A}, Vt, St0) -> {Avt,St1} = gexpr(A, Vt, St0), case is_gexpr_op(Op, 1) of true -> {Avt,St1}; false -> {Avt,add_error(Line, illegal_guard_expr, St1)} end;gexpr({op,Line,Op,L,R}, Vt, St0) -> {Avt,St1} = gexpr_list([L,R], Vt, St0), case is_gexpr_op(Op, 2) of true -> {Avt,St1}; false -> {Avt,add_error(Line, illegal_guard_expr, St1)} end;%% Everything else is illegal! You could put explicit tests here to%% better error diagnostics.gexpr(E, _Vt, St) -> {[],add_error(element(2, E), illegal_guard_expr, St)}.%% gexpr_list(Expressions, VarTable, State) ->%% {UsedVarTable,State'}gexpr_list(Es, Vt, St) -> foldl(fun (E, {Esvt,St0}) -> {Evt,St1} = gexpr(E, Vt, St0), {vtmerge(Evt, Esvt),St1} end, {[],St}, Es).%% is_guard_test(Expression) -> true | false.%% Test if a general expression is a guard test.is_guard_test(E) -> is_guard_test2(E, dict:new()).%% is_guard_test(Expression, Forms) -> bool()is_guard_test(Expression, Forms) -> RecordAttributes = [A || A = {attribute, _, record, _D} <- Forms], St0 = foldl(fun(Attr0, St1) -> Attr = zip_file_and_line(Attr0, "none"), attribute_state(Attr, St1) end, start(), RecordAttributes), is_guard_test2(zip_file_and_line(Expression, "nofile"), St0#lint.records).%% is_guard_test2(Expression, RecordDefs) -> true | false.is_guard_test2({call,Line,{atom,Lr,record},[E,A]}, RDs) -> is_gexpr({call,Line,{atom,Lr,is_record},[E,A]}, RDs);is_guard_test2({call,_Line,{atom,_La,Test},As}=Call, RDs) -> case erl_internal:type_test(Test, length(As)) of true -> is_gexpr_list(As, RDs); false -> is_gexpr(Call, RDs) end;is_guard_test2(G, RDs) -> %%Everything else is a guard expression. is_gexpr(G, RDs).%% is_guard_expr(Expression) -> true | false.%% Test if an expression is a guard expression.is_guard_expr(E) -> is_gexpr(E, []). is_gexpr({var,_L,_V}, _RDs) -> true;is_gexpr({char,_L,_C}, _RDs) -> true;is_gexpr({integer,_L,_I}, _RDs) -> true;is_gexpr({float,_L,_F}, _RDs) -> true;is_gexpr({atom,_L,_A}, _RDs) -> true;is_gexpr({string,_L,_S}, _RDs) -> true;is_gexpr({nil,_L}, _RDs) -> true;is_gexpr({cons,_L,H,T}, RDs) -> is_gexpr_list([H,T], RDs);is_gexpr({tuple,_L,Es}, RDs) -> is_gexpr_list(Es, RDs);%%is_gexpr({struct,_L,_Tag,Es}, RDs) ->%% is_gexpr_list(Es, RDs);is_gexpr({record_index,_L,_Name,Field}, RDs) -> is_gexpr(Field, RDs);is_gexpr({record_field,_L,_,_}=M, _RDs) -> case erl_parse:package_segments(M) of error -> false; _ -> true end;is_gexpr({record_field,_L,Rec,_Name,Field}, RDs) -> is_gexpr_list([Rec,Field], RDs);is_gexpr({record,L,Name,Inits}, RDs) -> is_gexpr_fields(Inits, L, Name, RDs);is_gexpr({bin,_L,Fs}, RDs) -> all(fun ({bin_element,_Line,E,Sz,_Ts}) -> is_gexpr(E, RDs) and (Sz =:= default orelse is_gexpr(Sz, RDs)) end, Fs);is_gexpr({call,_L,{atom,_Lf,F},As}, RDs) -> A = length(As), case erl_internal:guard_bif(F, A) of true -> is_gexpr_list(As, RDs); false -> false end;is_gexpr({call,_L,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, RDs) -> A = length(As), case erl_internal:guard_bif(F, A) orelse is_gexpr_op(F, A) of true -> is_gexpr_list(As, RDs); false -> false end;is_gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, RDs) -> is_gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, RDs);is_gexpr({op,_L,Op,A}, RDs) -> case is_gexpr_op(Op, 1) of true -> is_gexpr(A, RDs); false -> false end;is_gexpr({op,_L,Op,A1,A2}, RDs) -> case is_gexpr_op(Op, 2) of true -> is_gexpr_list([A1,A2], RDs); false -> false end;is_gexpr(_Other, _RDs) -> false.is_gexpr_op('andalso', 2) -> true;is_gexpr_op('orelse', 2) -> true;is_gexpr_op(Op, A) -> case catch erl_internal:op_type(Op, A) of arith -> true; bool -> true; comp -> true; _Other -> false end.is_gexpr_list(Es, RDs) -> all(fun (E) -> is_gexpr(E, RDs) end, Es).is_gexpr_fields(Fs, L, Name, RDs) -> IFs = case dict:find(Name, RDs) of {ok,{_Line,Fields}} -> Fs ++ init_fields(Fs, L, Fields); error -> Fs end, all(fun ({record_field,_Lf,_Name,V}) -> is_gexpr(V, RDs); (_Other) -> false end, IFs).%% exprs(Sequence, VarTable, State) ->%% {UsedVarTable,State'}%% Check a sequence of expressions, return all variables.exprs([E|Es], Vt, St0) -> {Evt,St1} = expr(E, Vt, St0), {Esvt,St2} = exprs(Es, vtupdate(Evt, Vt), St1), {vtupdate(Evt, Esvt),St2};exprs([], _Vt, St) -> {[],St}.%% expr(Expression, VarTable, State) ->%% {UsedVarTable,State'}%% Check an expression, returns NewVariables. Assume naive users and%% mark illegally exported variables, e.g. from catch, as unsafe to better%% show why unbound.expr({var,Line,V}, Vt, St) -> expr_var(V, Line, Vt, St);expr({char,_Line,_C}, _Vt, St) -> {[],St};expr({integer,_Line,_I}, _Vt, St) -> {[],St};expr({float,_Line,_F}, _Vt, St) -> {[],St};expr({atom,Line,A}, _Vt, St) -> {[],keyword_warning(Line, A, St)};expr({string,_Line,_S}, _Vt, St) -> {[],St};expr({nil,_Line}, _Vt, St) -> {[],St};expr({cons,_Line,H,T}, Vt, St) -> expr_list([H,T], Vt, St);expr({lc,Line,E,Qs}, Vt0, St0) -> {Vt,St} = handle_comprehension(Line, E, Qs, Vt0, St0), {vtold(Vt, Vt0),St}; %Don't export local variablesexpr({bc,Line,E,Qs}, Vt0, St0) -> {Vt,St} = handle_comprehension(Line, E, Qs, Vt0, St0), case binary_comprehension(St0) of true -> {vtold(Vt,Vt0),St}; %Don't export local variables false -> {vtold(Vt,Vt0), add_error(Line,binary_comprehension,St)} end;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);expr({record,Line,Name,Inits}, Vt, St) -> check_record(Line, Name, St, fun (Dfs, St1) -> init_fields(Inits, Line, Name, Dfs, Vt, St1) end);expr({record_field,Line,_,_}=M, _Vt, St0) -> case expand_package(M, St0) of {error, St1} -> {[],add_error(Line, illegal_expr, St1)}; {_, St1} -> {[], St1} end;expr({record_field,Line,Rec,Name,Field}, Vt, St0) -> {Rvt,St1} = record_expr(Line, Rec, Vt, St0), {Fvt,St2} = check_record(Line, Name, St1, fun (Dfs, St) -> record_field(Field, Name, Dfs, St) end), {vtmerge(Rvt, Fvt),St2};expr({record,Line,Rec,Name,Upds}, Vt, St0) -> {Rvt,St1} = record_expr(Line, Rec, Vt, St0), {Usvt,St2} = check_record(Line, Name, St1, fun (Dfs, St) -> update_fields(Upds, Name, Dfs, Vt, St) end ), case has_wildcard_field(Upds) of true -> {[],add_error(Line, {wildcard_in_update,Name}, St2)}; false -> {vtmerge(Rvt, Usvt),St2} end;expr({bin,Line,Fs}, Vt, St) -> expr_bin(Line, Fs, Vt, St, fun expr/3);expr({block,_Line,Es}, Vt, St) -> %% Unfold block into a sequence. exprs(Es, Vt, St);expr({'if',Line,Cs}, Vt, St) -> icrt_clauses(Cs, {'if',Line}, Vt, St);expr({'case',Line,E,Cs}, Vt, St0) -> {Evt,St1} = expr(E, Vt, St0), {Cvt,St2} = icrt_clauses(Cs, {'case',Line}, vtupdate(Evt, Vt), St1), {vtmerge(Evt, Cvt),St2};expr({'cond',Line,Cs}, Vt, St) -> cond_clauses(Cs,{'cond',Line}, Vt, St);expr({'receive',Line,Cs}, Vt, St) -> icrt_clauses(Cs, {'receive',Line}, Vt, St);expr({'receive',Line,Cs,To,ToEs}, Vt, St0) -> %% Are variables from the timeout expression visible in the clauses? NO! {Tvt,St1} = expr(To, Vt, St0), {Tevt,St2} = exprs(ToEs, Vt, St1), {Cvt,St3} = icrt_clauses(Cs, Vt, St2), %% Csvts = [vtnew(Tevt, Vt)|Cvt], %This is just NEW variables! Csvts = [Tevt|Cvt], {Rvt,St4} = icrt_export(Csvts, Vt, {'receive',Line}, St3), {vtmerge([Tvt,Tevt,Rvt]),St4};expr({'fun',Line,Body}, Vt, St) -> %%No one can think funs export! case Body of {clauses,C
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?