diff options
-rw-r--r-- | src/rebar.app.src | 1 | ||||
-rw-r--r-- | src/rebar3.erl | 3 | ||||
-rw-r--r-- | src/rebar_prv_dialyzer.erl | 105 |
3 files changed, 108 insertions, 1 deletions
diff --git a/src/rebar.app.src b/src/rebar.app.src index dc3e689..0341f79 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -24,6 +24,7 @@ %% any_dir processing modules {providers, [rebar_prv_clean, rebar_prv_deps, + rebar_prv_dialyzer, rebar_prv_do, rebar_prv_eunit, rebar_prv_lock, diff --git a/src/rebar3.erl b/src/rebar3.erl index 297233a..e8bb8d5 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -56,7 +56,8 @@ main(Args) -> ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace", []), ?DEBUG("Uncaught error: ~p ~p", [Module, Reason]); _ -> - ?ERROR(Module:format_error(Reason, []), []) + {Error, _} = Module:format_error(Reason, []), + ?ERROR(Error, []) end, rebar_utils:delayed_halt(1); {error, Error} when is_list(Error) -> diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl new file mode 100644 index 0000000..a43999b --- /dev/null +++ b/src/rebar_prv_dialyzer.erl @@ -0,0 +1,105 @@ +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 et + +-module(rebar_prv_dialyzer). + +-behaviour(provider). + +-export([init/1, + do/1, + format_error/2]). + +-include("rebar.hrl"). + +-define(PROVIDER, dialyzer). +-define(DEPS, [compile]). + +%% =================================================================== +%% Public API +%% =================================================================== + +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {bare, false}, + {deps, ?DEPS}, + {example, "rebar dialyzer"}, + {short_desc, "Run the Dialyzer analyzer on the project."}, + {desc, ""}, + {opts, []}])), + {ok, State1}. + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(State) -> + ?INFO("Dialyzer starting, this may take a while...", []), + BuildDir = rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), + {ProjectPlt, _DepPlt} = get_plt_location(BuildDir), + Apps = rebar_state:project_apps(State), + ?INFO("Doing plt for project apps...", []), + + try + update_dep_plt(State, ProjectPlt, Apps), + WarningTypes = rebar_state:get(State, dialyzer_warnings, default_warnings()), + Paths = [filename:join(rebar_app_info:dir(App), "ebin") || App <- Apps], + Opts = [{analysis_type, succ_typings}, + {from, byte_code}, + {files_rec, Paths}, + {warnings, WarningTypes}, + {plts, [ProjectPlt]}], + + case dialyzer:run(Opts) of + [] -> + {ok, State}; + Warnings -> + [?WARN(dialyzer:format_warning(Warning), []) || Warning <- Warnings], + {ok, State} + end + catch + _:{dialyzer_error, Error} -> + {error, {?MODULE, {error_processing_apps, Error, Apps}}} + end. + +-spec format_error(any(), rebar_state:t()) -> {iolist(), rebar_state:t()}. +format_error({error_processing_apps, Error, _Apps}, State) -> + {io_lib:format("Error in dialyzing apps: ~s", [Error]), State}; +format_error(Reason, State) -> + {io_lib:format("~p", [Reason]), State}. + +%% Internal functions + +get_plt_location(BuildDir) -> + {filename:join([BuildDir, ".project.plt"]), + filename:join([BuildDir, ".deps.plt"])}. + +update_dep_plt(_State, DepPlt, AppList) -> + Opts0 = + case filelib:is_file(DepPlt) of + true -> + ?INFO("Plt is built, checking/updating ...", []), + [{analysis_type, plt_check}, + {plts, [DepPlt]}]; + false -> + ?INFO("Building the plt, this is really going to " + "take a long time ...", []), + [{analysis_type, plt_build}, + {output_plt, DepPlt}] + end, + Paths = [filename:join(rebar_app_info:dir(App), "ebin") || App <- AppList], + Opts = [{files_rec, Paths}, + {from, byte_code}] ++ Opts0, + + dialyzer:run(Opts). + +default_warnings() -> + [no_return, + no_unused, + no_improper_lists, + no_fun_app, + no_match, + no_opaque, + no_fail_call, + error_handling, + race_conditions, + unmatched_returns, + underspecs]. |