erl_lint.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,705 行 · 第 1/5 页
ERL
1,705 行
%% -*- erlang-indent-level: 4 -*-%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id $%%%% Do necessary checking of Erlang code.%% N.B. All the code necessary for checking structs (tagged tuples) is%% here. Just comment out the lines in pattern/2, gexpr/3 and expr/3.-module(erl_lint).-export([module/1,module/2,module/3,format_error/1]).-export([exprs/2,exprs_opt/3,used_vars/2]). % Used from erl_eval.erl.-export([is_pattern_expr/1,is_guard_test/1,is_guard_test/2]).-export([is_guard_expr/1]).-export([bool_option/4,value_option/3,value_option/7]).-export([modify_line/2]).-import(lists, [member/2,map/2,foldl/3,foldr/3,mapfoldl/3,all/2,reverse/1]).%% bool_option(OnOpt, OffOpt, Default, Options) -> true | false.%% value_option(Flag, Default, Options) -> Value.%% value_option(Flag, Default, OnOpt, OnVal, OffOpt, OffVal, Options) ->%% Value.%% The option handling functions.bool_option(On, Off, Default, Opts) -> foldl(fun (Opt, _Def) when Opt =:= On -> true; (Opt, _Def) when Opt =:= Off -> false; (_Opt, Def) -> Def end, Default, Opts).value_option(Flag, Default, Opts) -> foldl(fun ({Opt,Val}, _Def) when Opt =:= Flag -> Val; (_Opt, Def) -> Def end, Default, Opts).value_option(Flag, Default, On, OnVal, Off, OffVal, Opts) -> foldl(fun ({Opt,Val}, _Def) when Opt =:= Flag -> Val; (Opt, _Def) when Opt =:= On -> OnVal; (Opt, _Def) when Opt =:= Off -> OffVal; (_Opt, Def) -> Def end, Default, Opts).%% The error and warning info structures, {Line,Module,Descriptor},%% are kept in their seperate fields in the lint state record together%% with the name of the file (when a new file is entered, marked by%% the 'file' attribute, then the field 'file' of the lint record is%% set). At the end of the run these lists are packed into a list of%% {FileName,ErrorDescList} pairs which are returned.-include_lib("stdlib/include/erl_bits.hrl").%%-define(DEBUGF(X,Y), io:format(X, Y)).-define(DEBUGF(X,Y), void).%% Usage of records, functions, and imports. The variable table, which%% is passed on as an argument, holds the usage of variables.-record(usage, { calls = dict:new(), %Who calls who imported = [], %Actually imported functions used_records=sets:new() %Used record definitions }).%% Define the lint state record.%% 'called' and 'exports' contain {Line, {Function, Arity}},%% the other function collections contain {Function, Arity}.-record(lint, {state=start, %start | attribute | function module=[], %Module package="", %Module package behaviour=[], %Behaviour exports=gb_sets:empty(), %Exports imports=[], %Imports mod_imports=dict:new(), %Module Imports compile=[], %Compile flags records=dict:new(), %Record definitions defined=gb_sets:empty(), %Defined fuctions clashes=[], %Exported functions named as BIFs not_deprecated=[], %Not considered deprecated func=[], %Current function warn_format=0, %Warn format calls enabled_warnings=[], %All enabled warnings (ordset). errors=[], %Current errors warnings=[], %Current warnings global_vt=[], %The global VarTable file=[], %From last file attribute recdef_top=false, %true in record initialisation %outside any fun or lc xqlc=false, %true if qlc.hrl included called=[], %Called functions usage = #usage{} }).%% format_error(Error)%% Return a string describing the error.format_error(undefined_module) -> "no module definition";format_error({bad_module_name, M}) -> io_lib:format("bad module name '~s'", [M]);format_error(redefine_module) -> "redefining module";%%% format_error({redefine_mod_import, M, P}) ->%%% io_lib:format("module '~s' already imported from package '~s'",%%% [M, P]);format_error(invalid_call) -> "invalid function call";format_error(invalid_record) -> "invalid record expression";format_error(no_generator) -> "list comprehension has no generator (perhaps \"|\" mistyped as \"||\"?)";format_error({attribute,A}) -> io_lib:format("attribute '~w' after function definitions", [A]);format_error({missing_qlc_hrl,A}) -> io_lib:format("qlc:q/~w called, but \"qlc.hrl\" not included", [A]);format_error({redefine_import,{bif,{F,A},M}}) -> io_lib:format("function ~w/~w already auto-imported from ~w", [F,A,M]);format_error({redefine_import,{{F,A},M}}) -> io_lib:format("function ~w/~w already imported from ~w", [F,A,M]);format_error({bad_inline,{F,A}}) -> io_lib:format("inlined function ~w/~w undefined", [F,A]);format_error({invalid_deprecated,D}) -> io_lib:format("badly formed deprecated attribute ~w", [D]);format_error({bad_deprecated,{F,A}}) -> io_lib:format("deprecated function ~w/~w undefined or not exported", [F, A]);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({bad_nowarn_deprecated_function,{M,F,A}}) -> io_lib:format("~w:~w/~w is not a deprecated function", [M,F,A]);format_error({duplicated_export, {F,A}}) -> io_lib:format("function ~w/~w already exported", [F,A]);format_error({unused_import,{{F,A},M}}) -> io_lib:format("import ~w:~w/~w is unused", [M,F,A]);format_error({undefined_function,{F,A}}) -> io_lib:format("function ~w/~w undefined", [F,A]);format_error({redefine_function,{F,A}}) -> io_lib:format("function ~w/~w already defined", [F,A]);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}}) -> 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 warning)", [F,A,F,A,F,A]);format_error({deprecated, {M1, F1, A1}, {M2, F2, A2}}) -> io_lib:format("~p:~p/~p deprecated; use ~p:~p/~p", [M1, F1, A1, M2, F2, A2]);format_error({obsolete_guard, {F, A}}) -> io_lib:format("~p/~p obsolete", [F, A]);format_error({deprecated, {M1, F1, A1}, String}) when is_list(String) -> io_lib:format("~p:~p/~p: ~s", [M1, F1, A1, String]);format_error({reserved_for_future,K}) -> io_lib:format("atom ~w: future reserved keyword - rename or quote", [K]);format_error(illegal_pattern) -> "illegal pattern";format_error(illegal_bin_pattern) -> "binary patterns cannot be matched in parallel using '='";format_error(illegal_expr) -> "illegal expression";format_error(illegal_guard_expr) -> "illegal guard expression";format_error({explicit_export,F,A}) -> io_lib:format("in this release, the call to ~w/~w must be written " "like this: erlang:~w/~w", [F,A,F,A]);format_error({undefined_record,T}) -> io_lib:format("record ~w undefined", [T]);format_error({redefine_record,T}) -> io_lib:format("record ~w already defined", [T]);format_error({redefine_field,T,F}) -> io_lib:format("field ~w already defined in record ~w", [F,T]);format_error({undefined_field,T,F}) -> io_lib:format("field ~w undefined in record ~w", [F,T]);format_error(illegal_record_info) -> "illegal record info";format_error({field_name_is_variable,T,F}) -> io_lib:format("field ~w is not an atom or _ in record ~w", [F,T]);format_error({wildcard_in_update,T}) -> io_lib:format("meaningless use of _ in update of record ~w", [T]);format_error({unused_record,T}) -> io_lib:format("record ~w is unused", [T]);format_error({unbound_var,V}) -> io_lib:format("variable ~w is unbound", [V]);format_error({unsafe_var,V,{What,Where}}) -> io_lib:format("variable ~w unsafe in ~w (line ~w)", [V,What,Where]);format_error({exported_var,V,{What,Where}}) -> io_lib:format("variable ~w exported from ~w (line ~w)", [V,What,Where]);format_error({shadowed_var,V,In}) -> io_lib:format("variable ~w shadowed in ~w", [V,In]);format_error({unused_var, V}) -> io_lib:format("variable ~w is unused", [V]);format_error({variable_in_record_def,V}) -> io_lib:format("variable ~w in record definition", [V]);format_error({undefined_bittype,Type}) -> io_lib:format("bit type ~w undefined", [Type]);format_error({bittype_mismatch,T1,T2,What}) -> io_lib:format("bit type mismatch (~s) between ~p and ~p", [What,T1,T2]);format_error(bittype_unit) -> "a bit unit size must not be specified unless a size is specified too";format_error(illegal_bitsize) -> "illegal bit size";format_error({bad_bitsize,Type}) -> io_lib:format("bad ~s bit size", [Type]);format_error(unaligned_bitpat) -> "bit pattern not byte aligned";format_error(binary_comprehension) -> "binary comprehension in code not compiled with the binary comprehension flag";format_error(binary_generator) -> "binary generator in code not compiled with the binary comprehension flag";format_error({format_error,{Fmt,Args}}) -> io_lib:format(Fmt, Args);format_error({mnemosyne,What}) -> "mnemosyne " ++ What ++ ", missing transformation";format_error({conflicting_behaviours,{Name,Arity},B,FirstL,FirstB}) -> io_lib:format("conflicting behaviours - callback ~w/~w required by both '~p' " "and '~p' (line ~p)", [Name,Arity,B,FirstB,FirstL]);format_error({undefined_behaviour_func, {Func,Arity}, Behaviour}) -> io_lib:format("undefined callback function ~w/~w (behaviour '~w')", [Func,Arity,Behaviour]);format_error({undefined_behaviour,Behaviour}) -> io_lib:format("behaviour ~w undefined", [Behaviour]);format_error({undefined_behaviour_callbacks,Behaviour}) -> io_lib:format("behaviour ~w callback functions are undefined", [Behaviour]);format_error({ill_defined_behaviour_callbacks,Behaviour}) -> io_lib:format("behaviour ~w callback functions erroneously defined", [Behaviour]).%% Local functions that are somehow automatically generated.pseudolocals() -> [{module_info,0}, {module_info,1}, {record_info,2}].%%%% Used by erl_eval.erl to check commands.%% exprs(Exprs, BindingsList) -> exprs_opt(Exprs, BindingsList, []).exprs_opt(Exprs, BindingsList, Opts) -> {St0,Vs} = foldl(fun({{record,_SequenceNumber,_Name},Attr0}, {St1,Vs1}) -> Attr = zip_file_and_line(Attr0, "none"), {attribute_state(Attr, St1),Vs1}; ({V,_}, {St1,Vs1}) -> {St1,[{V,{bound,unused,[]}} | Vs1]} end, {start("nofile",Opts),[]}, BindingsList), Vt = orddict:from_list(Vs), {_Evt,St} = exprs(zip_file_and_line(Exprs, "nofile"), Vt, St0), return_status(St).used_vars(Exprs, BindingsList) -> Vs = foldl(fun({{record,_SequenceNumber,_Name},_Attr}, Vs0) -> Vs0; ({V,_Val}, Vs0) -> [{V,{bound,unused,[]}} | Vs0] end, [], BindingsList), Vt = orddict:from_list(Vs), {Evt,_St} = exprs(zip_file_and_line(Exprs, "nofile"), Vt, start()), {ok, foldl(fun({V,{_,used,_}}, L) -> [V | L]; (_, L) -> L end, [], Evt)}.%% module([Form]) ->%% module([Form], FileName) ->%% module([Form], FileName, [CompileOption]) ->%% {ok,[Warning]} | {error,[Error],[Warning]}%% Start processing a module. Define predefined functions and exports and%% apply_lambda/2 has been called to shut lint up. N.B. these lists are%% really all ordsets!module(Forms) -> Opts = compiler_options(Forms), St = forms(Forms, start("nofile", Opts)), return_status(St). module(Forms, FileName) -> Opts = compiler_options(Forms), St = forms(Forms, start(FileName, Opts)), return_status(St).module(Forms, FileName, Opts0) -> %% We want the options given on the command line to take %% precedence over options in the module. Opts = compiler_options(Forms) ++ Opts0, St = forms(Forms, start(FileName, Opts)), return_status(St).compiler_options(Forms) -> lists:flatten([C || {attribute,_,compile,C} <- Forms]).%% start() -> State%% start(FileName, [Option]) -> Statestart() -> start("nofile", []).start(File, Opts) -> Enabled0 = [{unused_vars, bool_option(warn_unused_vars, nowarn_unused_vars, true, Opts)}, {export_vars, bool_option(warn_export_vars, nowarn_export_vars, false, Opts)}, {shadow_vars, bool_option(warn_shadow_vars, nowarn_shadow_vars, true, Opts)}, {unused_import, bool_option(warn_unused_import, nowarn_unused_import, false, Opts)}, {unused_function, bool_option(warn_unused_function, nowarn_unused_function, true, Opts)}, {bif_clash, bool_option(warn_bif_clash, nowarn_bif_clash,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?