summaryrefslogtreecommitdiff
path: root/lib/dialyzer/src/dialyzer.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/src/dialyzer.erl')
-rw-r--r--lib/dialyzer/src/dialyzer.erl188
1 files changed, 150 insertions, 38 deletions
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 83d3c03e7e..ecfc509e34 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -29,6 +29,8 @@
%%--------------------------------------------------------------------
-export([plain_cl/0,
run/1,
+ run_report_modules_analyzed/1,
+ run_report_modules_changed_and_analyzed/1,
gui/0,
gui/1,
plt_info/1,
@@ -41,6 +43,14 @@
%% Interfaces:
%% - plain_cl/0 : to be used ONLY by the dialyzer C program.
%% - run/1: Erlang interface for a command line-like analysis
+%% - run_report_modules_analyzed/1: Erlang interface for a command line-like
+%% analysis, but also returns the list of modules that
+%% had to be analyzed to compute the result
+%% - run_report_modules_analyzed/1: Erlang interface for a command line-like
+%% analysis, but also returns the list of modules that
+%% had to be analyzed to compute the result, plus the
+%% set of modules that have changed since the PLT was
+%% created (if applicable)
%% - gui/0/1: Erlang interface for the gui.
%% - format_warning/1: Get the string representation of a warning.
%% - format_warning/2: Likewise, but with an option whether
@@ -88,6 +98,7 @@ cl_check_init(#options{analysis_type = AnalType} = Opts) ->
plt_build -> {ok, ?RET_NOTHING_SUSPICIOUS};
plt_add -> {ok, ?RET_NOTHING_SUSPICIOUS};
plt_remove -> {ok, ?RET_NOTHING_SUSPICIOUS};
+ incremental -> {ok, ?RET_NOTHING_SUSPICIOUS};
Other when Other =:= succ_typings; Other =:= plt_check ->
F = fun() ->
NewOpts = Opts#options{analysis_type = plt_check},
@@ -109,16 +120,37 @@ print_plt_info(#options{init_plts = PLTs, output_file = OutputFile}) ->
get_plt_info([PLT|PLTs]) ->
String =
- case dialyzer_plt:included_files(PLT) of
- {ok, Files} ->
- io_lib:format("The PLT ~ts includes the following files:\n~tp\n\n",
- [PLT, Files]);
- {error, read_error} ->
- Msg = io_lib:format("Could not read the PLT file ~tp\n\n", [PLT]),
- throw({dialyzer_error, Msg});
- {error, no_such_file} ->
- Msg = io_lib:format("The PLT file ~tp does not exist\n\n", [PLT]),
- throw({dialyzer_error, Msg})
+ case dialyzer_plt:plt_kind(PLT) of
+ cplt ->
+ case dialyzer_cplt:included_files(PLT) of
+ {ok, Files} ->
+ io_lib:format("The classic PLT ~ts includes the following files:\n~tp\n\n",
+ [PLT, Files]);
+ {error, read_error} ->
+ Msg = io_lib:format("Could not read the classic PLT file ~tp\n\n", [PLT]),
+ throw({dialyzer_error, Msg});
+ {error, no_such_file} ->
+ Msg = io_lib:format("The classic PLT file ~tp does not exist\n\n", [PLT]),
+ throw({dialyzer_error, Msg})
+ end;
+ iplt ->
+ case dialyzer_iplt:included_modules(PLT) of
+ {ok, Modules} ->
+ io_lib:format("The incremental PLT ~ts includes the following modules:\n~tp\n\n",
+ [PLT, Modules]);
+ {error, read_error} ->
+ Msg = io_lib:format("Could not read the incremental PLT file ~tp\n\n", [PLT]),
+ throw({dialyzer_error, Msg});
+ {error, no_such_file} ->
+ Msg = io_lib:format("The incremental PLT file ~tp does not exist\n\n", [PLT]),
+ throw({dialyzer_error, Msg})
+ end;
+ bad_file ->
+ Msg = io_lib:format("Could not read the PLT file ~tp\n\n", [PLT]),
+ throw({dialyzer_error, Msg});
+ no_file ->
+ Msg = io_lib:format("The PLT file ~tp does not exist\n\n", [PLT]),
+ throw({dialyzer_error, Msg})
end,
String ++ get_plt_info(PLTs);
get_plt_info([]) -> "".
@@ -142,10 +174,17 @@ do_print_plt_info(PLTInfo, OutputFile) ->
end.
cl(Opts) ->
- F = fun() ->
- {Ret, _Warnings} = dialyzer_cl:start(Opts),
- Ret
- end,
+ F =
+ fun() ->
+ {Ret, _Warnings} =
+ case Opts#options.analysis_type of
+ incremental ->
+ dialyzer_incremental:start(Opts);
+ _ ->
+ dialyzer_cl:start(Opts)
+ end,
+ Ret
+ end,
doit(F).
-spec run(Options) -> Warnings when
@@ -153,15 +192,41 @@ cl(Opts) ->
Warnings :: [dial_warning()].
run(Opts) ->
+ {Warnings, _ModulesAnalyzed} = run_report_modules_analyzed(Opts),
+ Warnings.
+
+-spec run_report_modules_analyzed(Options) -> {Warnings, ModulesAnalyzed} when
+ Options :: [dial_option()],
+ Warnings :: [dial_warning()],
+ ModulesAnalyzed :: [module()].
+
+-spec run_report_modules_changed_and_analyzed(Options) -> {Warnings, ModulesChanged, ModulesAnalyzed} when
+ Options :: [dial_option()],
+ Warnings :: [dial_warning()],
+ ModulesChanged :: undefined | [module()],
+ ModulesAnalyzed :: [module()].
+
+run_report_modules_analyzed(Opts) ->
+ {Warnings, _ModulesChanged, ModulesAnalyzed} = run_report_modules_changed_and_analyzed(Opts),
+ {Warnings, ModulesAnalyzed}.
+
+run_report_modules_changed_and_analyzed(Opts) ->
try dialyzer_options:build([{report_mode, quiet},
{erlang_mode, true}|Opts]) of
{error, Msg} ->
throw({dialyzer_error, Msg});
OptsRecord ->
ok = check_init(OptsRecord),
- case dialyzer_cl:start(OptsRecord) of
- {?RET_DISCREPANCIES, Warnings} -> Warnings;
- {?RET_NOTHING_SUSPICIOUS, _} -> []
+ AnalysisResult =
+ case OptsRecord#options.analysis_type of
+ incremental ->
+ dialyzer_incremental:start_report_modules_changed_and_analyzed(OptsRecord);
+ _ ->
+ dialyzer_cl:start_report_modules_changed_and_analyzed(OptsRecord)
+ end,
+ case AnalysisResult of
+ {{?RET_DISCREPANCIES, Warnings}, ModulesChanged, ModulesAnalyzed} -> {Warnings, ModulesChanged, ModulesAnalyzed};
+ {{?RET_NOTHING_SUSPICIOUS, _}, ModulesChanged, ModulesAnalyzed} -> {[], ModulesChanged, ModulesAnalyzed}
end
catch
throw:{dialyzer_error, ErrorMsg} ->
@@ -219,15 +284,26 @@ check_gui_options(#options{analysis_type = Mode}) ->
throw({dialyzer_error, Msg}).
-spec plt_info(Plt) ->
- {'ok', Result} | {'error', Reason} when
+ {'ok', ClassicResult | IncrementalResult } | {'error', Reason} when
Plt :: file:filename(),
- Result :: [{'files', [file:filename()]}],
+ ClassicResult :: [{'files', [file:filename()]}],
+ IncrementalResult :: {incremental, [{'modules', [module()]}]},
Reason :: 'not_valid' | 'no_such_file' | 'read_error'.
plt_info(Plt) ->
- case dialyzer_plt:included_files(Plt) of
- {ok, Files} -> {ok, [{files, Files}]};
- Error -> Error
+ case dialyzer_plt:plt_kind(Plt) of
+ cplt ->
+ case dialyzer_cplt:included_files(Plt) of
+ {ok, Files} -> {ok, [{files, Files}]};
+ Error -> Error
+ end;
+ iplt ->
+ case dialyzer_iplt:included_modules(Plt) of
+ {ok, Modules} -> {ok, {incremental, [{modules, Modules}]}};
+ Error -> Error
+ end;
+ bad_file -> {error, not_valid};
+ no_file -> {error, no_such_file}
end.
@@ -339,8 +415,8 @@ message_to_string({app_call, [M, F, Args, Culprit, ExpectedType, FoundType]},
[M, F, a(Args, I), c(Culprit, I),
t(ExpectedType, I), t(FoundType, I)]);
message_to_string({bin_construction, [Culprit, Size, Seg, Type]}, I, _E) ->
- io_lib:format("Binary construction will fail since the ~s field ~s in"
- " segment ~s has type ~s\n",
+ io_lib:format("Binary construction will fail since the ~ts field ~ts in"
+ " segment ~ts has type ~ts\n",
[Culprit, c(Size, I), c(Seg, I), t(Type, I)]);
message_to_string({call, [M, F, Args, ArgNs, FailReason,
SigArgs, SigRet, Contract]}, I, _E) ->
@@ -440,9 +516,16 @@ message_to_string({contract_range, [Contract, M, F, ArgStrings,
" return for ~tw~ts on position ~s is ~ts\n",
[con(M, F, Contract, I), F, a(ArgStrings, I),
pos(Location, E), t(CRet, I)]);
-message_to_string({invalid_contract, [M, F, A, Sig]}, I, _E) ->
- io_lib:format("Invalid type specification for function ~w:~tw/~w."
- " The success typing is ~ts\n", [M, F, A, sig(Sig, I)]);
+message_to_string({invalid_contract, [M, F, A, none, Contract, Sig]}, I, _E) ->
+ io_lib:format("Invalid type specification for function ~w:~tw/~w.\n"
+ " The success typing is ~ts\n"
+ " But the spec is ~ts\n", [M, F, A, con(M, F, Sig, I), con(M, F, Contract, I)]);
+message_to_string({invalid_contract, [M, F, A, InvalidContractDetails, Contract, Sig]}, I, _E) ->
+ io_lib:format("Invalid type specification for function ~w:~tw/~w.\n"
+ " The success typing is ~ts\n"
+ " But the spec is ~ts\n"
+ "~ts",
+ [M, F, A, con(M, F, Sig, I), con(M, F, Contract, I), format_invalid_contract_details(InvalidContractDetails)]);
message_to_string({contract_with_opaque, [M, F, A, OpaqueType, SigType]},
I, _E) ->
io_lib:format("The specification for ~w:~tw/~w"
@@ -459,7 +542,7 @@ message_to_string({missing_range, [M, F, A, ExtraRanges, ContrRange]}, I, _E) ->
[M, F, A, t(ExtraRanges, I), t(ContrRange, I)]);
message_to_string({overlapping_contract, [M, F, A]}, _I, _E) ->
io_lib:format("Overloaded contract for ~w:~tw/~w has overlapping domains;"
- " such contracts are currently unsupported and are simply ignored\n",
+ " such contracts cannot establish a dependency between the overloaded input and output types\n",
[M, F, A]);
message_to_string({spec_missing_fun, [M, F, A]}, _I, _E) ->
io_lib:format("Contract for function that does not exist: ~w:~tw/~w\n",
@@ -510,19 +593,19 @@ message_to_string({callback_type_mismatch, [B, F, A, ST, CT]}, I, _E) ->
" the callback of the ~w behaviour\n",
[F, A, t("("++ST++")", I), t(CT, I), B]);
message_to_string({callback_arg_type_mismatch, [B, F, A, N, ST, CT]}, I, _E) ->
- io_lib:format("The inferred type for the ~s argument of ~tw/~w (~ts) is"
- " not a supertype of ~ts, which is expected type for this"
+ io_lib:format("The inferred type for the ~s argument of ~tw/~w (~ts)"
+ " has nothing in common with ~ts, which is expected type for this"
" argument in the callback of the ~w behaviour\n",
[ordinal(N), F, A, t(ST, I), t(CT, I), B]);
message_to_string({callback_spec_type_mismatch, [B, F, A, ST, CT]}, I, _E) ->
- io_lib:format("The return type ~ts in the specification of ~tw/~w is not a"
- " subtype of ~ts, which is the expected return type for the"
+ io_lib:format("The return type ~ts in the specification of ~tw/~w has nothing"
+ " in common with ~ts, which is the expected return type for the"
" callback of the ~w behaviour\n",
[t(ST, I), F, A, t(CT, I), B]);
message_to_string({callback_spec_arg_type_mismatch, [B, F, A, N, ST, CT]},
I, _E) ->
- io_lib:format("The specified type for the ~ts argument of ~tw/~w (~ts) is"
- " not a supertype of ~ts, which is expected type for this"
+ io_lib:format("The specified type for the ~ts argument of ~tw/~w (~ts) has"
+ " nothing in common with ~ts, which is expected type for this"
" argument in the callback of the ~w behaviour\n",
[ordinal(N), F, A, t(ST, I), t(CT, I), B]);
message_to_string({callback_missing, [B, F, A]}, _I, _E) ->
@@ -545,6 +628,27 @@ message_to_string({unknown_behaviour, B}, _I, _E) ->
%% Auxiliary functions below
%%-----------------------------------------------------------------------------
+format_invalid_contract_details({InvalidArgIdxs, IsRangeInvalid}) ->
+ ArgOrd = form_position_string(InvalidArgIdxs),
+ ArgDesc =
+ case InvalidArgIdxs of
+ [] -> "";
+ [_] -> io_lib:format("They do not overlap in the ~ts argument", [ArgOrd]);
+ [_|_] -> io_lib:format("They do not overlap in the ~ts arguments", [ArgOrd])
+ end,
+ RangeDesc =
+ case IsRangeInvalid of
+ true -> "return types do not overlap";
+ false -> ""
+ end,
+ case {ArgDesc, RangeDesc} of
+ {"", ""} -> "";
+ {"", [_|_]} -> io_lib:format(" The ~ts\n", [RangeDesc]);
+ {[_|_], ""} -> io_lib:format(" ~ts\n", [ArgDesc]);
+ {[_|_], [_|_]} -> io_lib:format(" ~ts, and the ~ts\n", [ArgDesc, RangeDesc])
+ end.
+
+
call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet,
{IsOverloaded, Contract}, I) ->
PositionString = form_position_string(ArgNs),
@@ -623,10 +727,18 @@ form_position_string(ArgNs) ->
Head ++ " and " ++ ordinal(Last)
end.
-ordinal(1) -> "1st";
-ordinal(2) -> "2nd";
-ordinal(3) -> "3rd";
-ordinal(N) when is_integer(N) -> io_lib:format("~wth", [N]).
+ordinal(N) when is_integer(N),
+ ((N rem 100) =:= 11) orelse
+ ((N rem 100) =:= 12) orelse
+ ((N rem 100) =:= 13) ->
+ io_lib:format("~Bth", [N]);
+ordinal(N) when is_integer(N) ->
+ case min(N rem 10, 4) of
+ 1 -> io_lib:format("~Bst", [N]);
+ 2 -> io_lib:format("~Bnd", [N]);
+ 3 -> io_lib:format("~Brd", [N]);
+ _ -> io_lib:format("~Bth", [N])
+ end.
%% Functions that parse type strings, literal strings, and contract
%% strings. Return strings formatted by erl_pp.