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 + -
显示快捷键?