Packages
elvis_core
5.0.0
5.0.4
5.0.3
5.0.2
5.0.1
5.0.0
retired
4.2.3
4.2.2
4.2.1
4.2.0
4.1.1
4.1.0
4.0.0
3.2.5
3.2.4
3.2.3
3.2.2
3.2.1
3.2.0
3.1.0
3.0.1
3.0.0
2.0.1
2.0.0
1.4.0
1.3.2
1.3.1
1.3.0
1.2.0
1.1.2
1.1.1
1.1.0
1.0.0
0.7.0
0.6.1
0.6.0
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
Core library for the Erlang style reviewer
Retired package: Release invalid - We found a bug that needs fixing before using this release in rebar3_lint/elvis
Current section
Files
Jump to
Current section
Files
src/elvis_core.erl
-module(elvis_core).
-feature(maybe_expr, enable).
%% Public API
-export([rock/1]).
-export([start/0]).
%% for internal use only
-export([do_rock/2]).
%% for eating our own dogfood
-export([main/1]).
-export_type([target/0]).
-ifdef(TEST).
-export([apply_rule/2]).
% For tests (we can't Xref the tests because rebar3 fails to compile some files).
-ignore_xref([apply_rule/2]).
-endif.
% For eating our own dogfood.
-ignore_xref([main/1]).
% For internal use only
-ignore_xref([do_rock/2]).
% For shell usage.
-ignore_xref([start/0]).
% API exports, not consumed locally.
-ignore_xref([rock/1]).
-type source_filename() :: nonempty_string().
-type target() :: source_filename() | module().
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Public API
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% @doc Used when starting the application on the shell.
-spec start() -> ok.
start() ->
{ok, _} = application:ensure_all_started(elvis_core),
ok.
%% In this context, `throw` means an error, e.g., validation or internal, not an actual
%% call to `erlang:throw/1`.
-spec rock([elvis_config:t()]) -> ok | {errors, _} | {warnings, _}.
rock(ElvisConfig) ->
maybe
ok ?= elvis_config:validate_config(ElvisConfig),
_ = elvis_ruleset:drop_custom(),
Results = lists:map(fun do_parallel_rock/1, ElvisConfig),
ok ?= lists:foldl(fun combine_results/2, ok, Results)
else
{error, Term} ->
{errors_or_warnings(), Term}
end.
errors_or_warnings() ->
case elvis_config:warnings_as_errors() of
false ->
warnings;
_ ->
errors
end.
%% In this context, `throw` means an error, e.g., validation or internal, not an actual
%% call to `erlang:throw/1`.
-spec do_parallel_rock(elvis_config:t()) ->
ok
| {error, [elvis_result:file() | elvis_result:rule()]}.
do_parallel_rock(ElvisConfig0) ->
Parallel = elvis_config:parallel(),
ElvisConfig = elvis_config:resolve_files(ElvisConfig0),
Files = elvis_config:files(ElvisConfig),
Result =
elvis_task:chunk_fold(
{?MODULE, do_rock},
fun(Elem, Acc) ->
elvis_result:print_results(Elem),
{ok, [Elem | Acc]}
end,
[],
[ElvisConfig],
Files,
Parallel
),
case Result of
{ok, Results} ->
elvis_result_status(Results);
{error, _} = Error ->
Error
end.
-spec do_rock(elvis_file:t(), [elvis_config:t()] | elvis_config:t()) ->
{ok, elvis_result:file()} | {error, string()}.
do_rock(File, ElvisConfig) ->
maybe
{ok, LoadedFile} ?= load_file_data(ElvisConfig, File),
Results = apply_rules(ElvisConfig, LoadedFile),
{ok, Results}
else
{error, _} = Error ->
Error
end.
-spec load_file_data([elvis_config:t()] | elvis_config:t(), elvis_file:t()) ->
{ok, elvis_file:t()} | {error, string()}.
load_file_data(ElvisConfig, File) ->
Path = elvis_file:path(File),
_ = elvis_utils:info("Loading ~s", [Path]),
try
{ok, elvis_file:load_file_data(ElvisConfig, File)}
catch
_:Reason ->
Msg = "~w when loading file ~p.",
{error, elvis_utils:error(Msg, [Reason, Path])}
end.
-spec main([]) -> true | no_return().
main([]) ->
ok = application:load(elvis_core),
{module, _} = code:ensure_loaded(elvis_style),
case rock(elvis_config:config()) of
ok -> true;
_ -> elvis_utils:erlang_halt(1)
end.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Private
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
-spec combine_results(
ok | {error, [elvis_result:file()]},
ok | {error, [elvis_result:file()]}
) ->
ok | {error, [elvis_result:file()]}.
combine_results(ok, Acc) ->
Acc;
combine_results(Item, ok) ->
Item;
combine_results({error, ItemResults}, {error, AccResults}) ->
{error, ItemResults ++ AccResults}.
-spec apply_rules(
[elvis_config:t()] | elvis_config:t(),
File :: elvis_file:t()
) ->
elvis_result:file().
apply_rules(ElvisConfig, File) ->
Rules = elvis_config:rules(ElvisConfig),
Acc = {[], ElvisConfig, File},
{ParseTree, _} = elvis_file:parse_tree(File, ElvisConfig),
{RulesResults, _, _} =
lists:foldl(fun apply_rule/2, Acc, merge_rules({file, ParseTree}, lists:flatten(Rules))),
elvis_result:new(file, File, RulesResults).
merge_rules({file, ParseTree}, ElvisConfigRules) ->
{nodes, ElvisAttrs} =
elvis_code:find(#{
of_types => [elvis],
inside => ParseTree
}),
ElvisAttrRules = elvis_attr_rules(ElvisAttrs),
elvis_config:merge_rules(ElvisAttrRules, ElvisConfigRules).
elvis_attr_rules([] = _ElvisAttrs) ->
[];
elvis_attr_rules(ElvisAttrs) ->
[Rule || ElvisAttr <- ElvisAttrs, Rule <- ktn_code:attr(value, ElvisAttr)].
-spec apply_rule(Rule, {Results, ElvisConfig, File}) -> Result when
Rule :: elvis_rule:t(),
Results :: [elvis_result:rule() | elvis_result:elvis_error()],
ElvisConfig :: elvis_config:t(),
File :: elvis_file:t(),
Result :: {Results, ElvisConfig, File}.
apply_rule(Rule, {Result, ElvisConfig, File}) ->
RuleResult =
try
AnalyzedModule = elvis_file:module(File),
IgnorableModule = elvis_rule:ignorable(AnalyzedModule),
case elvis_rule:ignored(IgnorableModule, Rule) of
false ->
Results = elvis_rule:execute(elvis_rule:file(Rule, File), ElvisConfig),
SortFun = fun(#{line_num := L1}, #{line_num := L2}) -> L1 =< L2 end,
SortResults = lists:sort(SortFun, Results),
elvis_result:new(rule, Rule, SortResults);
true ->
elvis_result:new(rule, Rule, [])
end
catch
_:Reason:Stacktrace ->
Msg = "'~p' while applying rule '~p': ~p",
elvis_result:new(error, Msg, [
Reason, {elvis_rule:ns(Rule), elvis_rule:name(Rule)}, Stacktrace
])
end,
{[RuleResult | Result], ElvisConfig, File}.
elvis_result_status(Results) ->
case elvis_result:status(Results) of
fail ->
{error, elvis_result:clean(Results)};
ok ->
ok
end.