aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/src
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/src')
-rw-r--r--lib/dialyzer/src/Makefile4
-rw-r--r--lib/dialyzer/src/dialyzer.erl446
-rw-r--r--lib/dialyzer/src/dialyzer.hrl8
-rw-r--r--lib/dialyzer/src/dialyzer_cl.erl119
-rw-r--r--lib/dialyzer/src/dialyzer_cl_parse.erl14
-rw-r--r--lib/dialyzer/src/dialyzer_contracts.erl1
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl22
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl4
-rw-r--r--lib/dialyzer/src/dialyzer_options.erl6
-rw-r--r--lib/dialyzer/src/dialyzer_utils.erl67
-rw-r--r--lib/dialyzer/src/typer.erl2
11 files changed, 427 insertions, 266 deletions
diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile
index fc08e7ca2f..bddd761705 100644
--- a/lib/dialyzer/src/Makefile
+++ b/lib/dialyzer/src/Makefile
@@ -90,8 +90,10 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
+else
+ERL_COMPILE_FLAGS += -Werror
endif
-ERL_COMPILE_FLAGS += +warn_export_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +warnings_as_errors
+ERL_COMPILE_FLAGS += +warn_export_vars +warn_unused_import +warn_untyped_record +warn_missing_spec
# ----------------------------------------------------
# Targets
diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl
index 185c8c9ae6..c1bc5ff597 100644
--- a/lib/dialyzer/src/dialyzer.erl
+++ b/lib/dialyzer/src/dialyzer.erl
@@ -280,20 +280,23 @@ cl_check_log(Output) ->
format_warning(W) ->
format_warning(W, basename).
--spec format_warning(raw_warning() | dial_warning(), fopt()) -> string().
-
-format_warning({Tag, {File, Line, _MFA}, Msg}, FOpt) ->
- format_warning({Tag, {File, Line}, Msg}, FOpt);
-format_warning({_Tag, {File, Line}, Msg}, FOpt) when is_list(File),
+-spec format_warning(raw_warning() | dial_warning(),
+ fopt() | proplists:proplist()) -> string().
+
+format_warning(RawWarning, FOpt) when is_atom(FOpt) ->
+ format_warning(RawWarning, [{filename_opt, FOpt}]);
+format_warning({Tag, {File, Line, _MFA}, Msg}, Opts) ->
+ format_warning({Tag, {File, Line}, Msg}, Opts);
+format_warning({_Tag, {File, Line}, Msg}, Opts) when is_list(File),
is_integer(Line) ->
- F = case FOpt of
+ F = case proplists:get_value(filename_opt, Opts, basename) of
fullpath -> File;
basename -> filename:basename(File)
end,
- String = lists:flatten(message_to_string(Msg)),
+ Indent = proplists:get_value(indent_opt, Opts, ?INDENT_OPT),
+ String = message_to_string(Msg, Indent),
lists:flatten(io_lib:format("~ts:~w: ~ts", [F, Line, String])).
-
%%-----------------------------------------------------------------------------
%% Message classification and pretty-printing below. Messages appear in
%% categories and in more or less alphabetical ordering within each category.
@@ -301,52 +304,60 @@ format_warning({_Tag, {File, Line}, Msg}, FOpt) when is_list(File),
%%----- Warnings for general discrepancies ----------------
message_to_string({apply, [Args, ArgNs, FailReason,
- SigArgs, SigRet, Contract]}) ->
- io_lib:format("Fun application with arguments ~ts ", [Args]) ++
- call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, Contract);
-message_to_string({app_call, [M, F, Args, Culprit, ExpectedType, FoundType]}) ->
+ SigArgs, SigRet, Contract]}, I) ->
+ io_lib:format("Fun application with arguments ~ts ", [a(Args, I)]) ++
+ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, Contract, I);
+message_to_string({app_call, [M, F, Args, Culprit, ExpectedType, FoundType]},
+ I) ->
io_lib:format("The call ~s:~ts~ts requires that ~ts is of type ~ts not ~ts\n",
- [M, F, Args, Culprit, ExpectedType, FoundType]);
-message_to_string({bin_construction, [Culprit, Size, Seg, Type]}) ->
+ [M, F, a(Args, I), c(Culprit, I),
+ t(ExpectedType, I), t(FoundType, I)]);
+message_to_string({bin_construction, [Culprit, Size, Seg, Type]}, I) ->
io_lib:format("Binary construction will fail since the ~s field ~s in"
- " segment ~s has type ~s\n", [Culprit, Size, Seg, Type]);
+ " segment ~s has type ~s\n",
+ [Culprit, c(Size, I), c(Seg, I), t(Type, I)]);
message_to_string({call, [M, F, Args, ArgNs, FailReason,
- SigArgs, SigRet, Contract]}) ->
- io_lib:format("The call ~w:~tw~ts ", [M, F, Args]) ++
- call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, Contract);
-message_to_string({call_to_missing, [M, F, A]}) ->
+ SigArgs, SigRet, Contract]}, I) ->
+ io_lib:format("The call ~w:~tw~ts ", [M, F, a(Args, I)]) ++
+ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet, Contract, I);
+message_to_string({call_to_missing, [M, F, A]}, _I) ->
io_lib:format("Call to missing or unexported function ~w:~tw/~w\n",
[M, F, A]);
-message_to_string({exact_eq, [Type1, Op, Type2]}) ->
+message_to_string({exact_eq, [Type1, Op, Type2]}, I) ->
io_lib:format("The test ~ts ~s ~ts can never evaluate to 'true'\n",
- [Type1, Op, Type2]);
-message_to_string({fun_app_args, [Args, Type]}) ->
+ [t(Type1, I), Op, t(Type2, I)]);
+message_to_string({fun_app_args, [ArgNs, Args, Type]}, I) ->
+ PositionString = form_position_string(ArgNs),
io_lib:format("Fun application with arguments ~ts will fail"
- " since the function has type ~ts\n", [Args, Type]);
-message_to_string({fun_app_no_fun, [Op, Type, Arity]}) ->
+ " since the function has type ~ts,"
+ " which differs in the ~s argument\n",
+ [a(Args, I), t(Type, I), PositionString]);
+message_to_string({fun_app_no_fun, [Op, Type, Arity]}, I) ->
io_lib:format("Fun application will fail since ~ts :: ~ts"
- " is not a function of arity ~w\n", [Op, Type, Arity]);
-message_to_string({guard_fail, []}) ->
+ " is not a function of arity ~w\n", [Op, t(Type, I), Arity]);
+message_to_string({guard_fail, []}, _I) ->
"Clause guard cannot succeed.\n";
-message_to_string({guard_fail, [Arg1, Infix, Arg2]}) ->
- io_lib:format("Guard test ~ts ~s ~ts can never succeed\n", [Arg1, Infix, Arg2]);
-message_to_string({map_update, [Type, Key]}) ->
+message_to_string({guard_fail, [Arg1, Infix, Arg2]}, I) ->
+ io_lib:format("Guard test ~ts ~s ~ts can never succeed\n",
+ [a(Arg1, I), Infix, a(Arg2, I)]); % a/2 rather than c/2
+message_to_string({map_update, [Type, Key]}, I) ->
io_lib:format("A key of type ~ts cannot exist "
- "in a map of type ~ts\n", [Key, Type]);
-message_to_string({neg_guard_fail, [Arg1, Infix, Arg2]}) ->
+ "in a map of type ~ts\n", [t(Key, I), t(Type, I)]);
+message_to_string({neg_guard_fail, [Arg1, Infix, Arg2]}, I) ->
io_lib:format("Guard test not(~ts ~s ~ts) can never succeed\n",
- [Arg1, Infix, Arg2]);
-message_to_string({guard_fail, [Guard, Args]}) ->
- io_lib:format("Guard test ~w~ts can never succeed\n", [Guard, Args]);
-message_to_string({neg_guard_fail, [Guard, Args]}) ->
- io_lib:format("Guard test not(~w~ts) can never succeed\n", [Guard, Args]);
-message_to_string({guard_fail_pat, [Pat, Type]}) ->
+ [a(Arg1, I), Infix, a(Arg2, I)]); % a/2 rather than c/2
+message_to_string({guard_fail, [Guard, Args]}, I) ->
+ io_lib:format("Guard test ~s~ts can never succeed\n", [Guard, a(Args, I)]);
+message_to_string({neg_guard_fail, [Guard, Args]}, I) ->
+ io_lib:format("Guard test not(~s~ts) can never succeed\n",
+ [Guard, a(Args, I)]);
+message_to_string({guard_fail_pat, [Pat, Type]}, I) ->
io_lib:format("Clause guard cannot succeed. The ~ts was matched"
- " against the type ~ts\n", [Pat, Type]);
-message_to_string({improper_list_constr, [TlType]}) ->
+ " against the type ~ts\n", [ps(Pat, I), t(Type, I)]);
+message_to_string({improper_list_constr, [TlType]}, I) ->
io_lib:format("Cons will produce an improper list"
- " since its 2nd argument is ~ts\n", [TlType]);
-message_to_string({no_return, [Type|Name]}) ->
+ " since its 2nd argument is ~ts\n", [t(TlType, I)]);
+message_to_string({no_return, [Type|Name]}, _I) ->
NameString =
case Name of
[] -> "The created fun ";
@@ -358,135 +369,150 @@ message_to_string({no_return, [Type|Name]}) ->
only_normal -> NameString ++ "has no local return\n";
both -> NameString ++ "has no local return\n"
end;
-message_to_string({record_constr, [RecConstr, FieldDiffs]}) ->
+message_to_string({record_constr, [RecConstr, FieldDiffs]}, I) ->
io_lib:format("Record construction ~ts violates the"
- " declared type of field ~ts\n", [RecConstr, FieldDiffs]);
-message_to_string({record_constr, [Name, Field, Type]}) ->
+ " declared type of field ~ts\n",
+ [t(RecConstr, I), field_diffs(FieldDiffs, I)]);
+message_to_string({record_constr, [Name, Field, Type]}, I) ->
io_lib:format("Record construction violates the declared type for #~tw{}"
- " since ~ts cannot be of type ~ts\n", [Name, Field, Type]);
-message_to_string({record_matching, [String, Name]}) ->
+ " since ~ts cannot be of type ~ts\n",
+ [Name, ps(Field, I), t(Type, I)]);
+message_to_string({record_matching, [String, Name]}, I) ->
io_lib:format("The ~ts violates the"
- " declared type for #~tw{}\n", [String, Name]);
-message_to_string({record_match, [Pat, Type]}) ->
+ " declared type for #~tw{}\n", [rec_type(String, I), Name]);
+message_to_string({record_match, [Pat, Type]}, I) ->
io_lib:format("Matching of ~ts tagged with a record name violates"
- " the declared type of ~ts\n", [Pat, Type]);
-message_to_string({pattern_match, [Pat, Type]}) ->
- io_lib:format("The ~ts can never match the type ~ts\n", [Pat, Type]);
-message_to_string({pattern_match_cov, [Pat, Type]}) ->
+ " the declared type of ~ts\n", [ps(Pat, I), t(Type, I)]);
+message_to_string({pattern_match, [Pat, Type]}, I) ->
+ io_lib:format("The ~ts can never match the type ~ts\n",
+ [ps(Pat, I), t(Type, I)]);
+message_to_string({pattern_match_cov, [Pat, Type]}, I) ->
io_lib:format("The ~ts can never match since previous"
" clauses completely covered the type ~ts\n",
- [Pat, Type]);
-message_to_string({unmatched_return, [Type]}) ->
+ [ps(Pat, I), t(Type, I)]);
+message_to_string({unmatched_return, [Type]}, I) ->
io_lib:format("Expression produces a value of type ~ts,"
- " but this value is unmatched\n", [Type]);
-message_to_string({unused_fun, [F, A]}) ->
+ " but this value is unmatched\n", [t(Type, I)]);
+message_to_string({unused_fun, [F, A]}, _I) ->
io_lib:format("Function ~tw/~w will never be called\n", [F, A]);
%%----- Warnings for specs and contracts -------------------
-message_to_string({contract_diff, [M, F, _A, Contract, Sig]}) ->
- io_lib:format("Type specification ~w:~tw~ts"
- " is not equal to the success typing: ~w:~tw~ts\n",
- [M, F, Contract, M, F, Sig]);
-message_to_string({contract_subtype, [M, F, _A, Contract, Sig]}) ->
- io_lib:format("Type specification ~w:~tw~ts"
- " is a subtype of the success typing: ~w:~tw~ts\n",
- [M, F, Contract, M, F, Sig]);
-message_to_string({contract_supertype, [M, F, _A, Contract, Sig]}) ->
- io_lib:format("Type specification ~w:~tw~ts"
- " is a supertype of the success typing: ~w:~tw~ts\n",
- [M, F, Contract, M, F, Sig]);
-message_to_string({contract_range, [Contract, M, F, ArgStrings, Line, CRet]}) ->
- io_lib:format("The contract ~w:~tw~ts cannot be right because the inferred"
+message_to_string({contract_diff, [M, F, _A, Contract, Sig]}, I) ->
+ io_lib:format("Type specification ~ts"
+ " is not equal to the success typing: ~ts\n",
+ [con(M, F, Contract, I), con(M, F, Sig, I)]);
+message_to_string({contract_subtype, [M, F, _A, Contract, Sig]}, I) ->
+ io_lib:format("Type specification ~ts"
+ " is a subtype of the success typing: ~ts\n",
+ [con(M, F, Contract, I), con(M, F, Sig, I)]);
+message_to_string({contract_supertype, [M, F, _A, Contract, Sig]}, I) ->
+ io_lib:format("Type specification ~ts"
+ " is a supertype of the success typing: ~ts\n",
+ [con(M, F, Contract, I), con(M, F, Sig, I)]);
+message_to_string({contract_range, [Contract, M, F, ArgStrings, Line, CRet]},
+ I) ->
+ io_lib:format("The contract ~ts cannot be right because the inferred"
" return for ~tw~ts on line ~w is ~ts\n",
- [M, F, Contract, F, ArgStrings, Line, CRet]);
-message_to_string({invalid_contract, [M, F, A, Sig]}) ->
+ [con(M, F, Contract, I), F, a(ArgStrings, I), Line, t(CRet, I)]);
+message_to_string({invalid_contract, [M, F, A, Sig]}, I) ->
io_lib:format("Invalid type specification for function ~w:~tw/~w."
- " The success typing is ~ts\n", [M, F, A, Sig]);
-message_to_string({contract_with_opaque, [M, F, A, OpaqueType, SigType]}) ->
+ " The success typing is ~ts\n", [M, F, A, sig(Sig, I)]);
+message_to_string({contract_with_opaque, [M, F, A, OpaqueType, SigType]},
+ I) ->
io_lib:format("The specification for ~w:~tw/~w"
" has an opaque subtype ~ts which is violated by the"
- " success typing ~ts\n", [M, F, A, OpaqueType, SigType]);
-message_to_string({extra_range, [M, F, A, ExtraRanges, SigRange]}) ->
+ " success typing ~ts\n",
+ [M, F, A, t(OpaqueType, I), sig(SigType, I)]);
+message_to_string({extra_range, [M, F, A, ExtraRanges, SigRange]}, I) ->
io_lib:format("The specification for ~w:~tw/~w states that the function"
" might also return ~ts but the inferred return is ~ts\n",
- [M, F, A, ExtraRanges, SigRange]);
-message_to_string({missing_range, [M, F, A, ExtraRanges, ContrRange]}) ->
+ [M, F, A, t(ExtraRanges, I), t(SigRange, I)]);
+message_to_string({missing_range, [M, F, A, ExtraRanges, ContrRange]}, I) ->
io_lib:format("The success typing for ~w:~tw/~w implies that the function"
" might also return ~ts but the specification return is ~ts\n",
- [M, F, A, ExtraRanges, ContrRange]);
-message_to_string({overlapping_contract, [M, F, A]}) ->
+ [M, F, A, t(ExtraRanges, I), t(ContrRange, I)]);
+message_to_string({overlapping_contract, [M, F, A]}, _I) ->
io_lib:format("Overloaded contract for ~w:~tw/~w has overlapping domains;"
" such contracts are currently unsupported and are simply ignored\n",
[M, F, A]);
-message_to_string({spec_missing_fun, [M, F, A]}) ->
+message_to_string({spec_missing_fun, [M, F, A]}, _I) ->
io_lib:format("Contract for function that does not exist: ~w:~tw/~w\n",
[M, F, A]);
%%----- Warnings for opaque type violations -------------------
-message_to_string({call_with_opaque, [M, F, Args, ArgNs, ExpArgs]}) ->
+message_to_string({call_with_opaque, [M, F, Args, ArgNs, ExpArgs]}, I) ->
io_lib:format("The call ~w:~tw~ts contains ~ts when ~ts\n",
- [M, F, Args, form_positions(ArgNs), form_expected(ExpArgs)]);
-message_to_string({call_without_opaque, [M, F, Args, ExpectedTriples]}) ->
+ [M, F, a(Args, I), form_positions(ArgNs),
+ form_expected(ExpArgs, I)]);
+message_to_string({call_without_opaque, [M, F, Args, ExpectedTriples]}, I) ->
io_lib:format("The call ~w:~tw~ts does not have ~ts\n",
- [M, F, Args, form_expected_without_opaque(ExpectedTriples)]);
-message_to_string({opaque_eq, [Type, _Op, OpaqueType]}) ->
+ [M, F, a(Args, I),
+ form_expected_without_opaque(ExpectedTriples, I)]);
+message_to_string({opaque_eq, [Type, _Op, OpaqueType]}, I) ->
io_lib:format("Attempt to test for equality between a term of type ~ts"
- " and a term of opaque type ~ts\n", [Type, OpaqueType]);
-message_to_string({opaque_guard, [Arg1, Infix, Arg2, ArgNs]}) ->
+ " and a term of opaque type ~ts\n",
+ [t(Type, I), t(OpaqueType, I)]);
+message_to_string({opaque_guard, [Arg1, Infix, Arg2, ArgNs]}, I) ->
io_lib:format("Guard test ~ts ~s ~ts contains ~s\n",
- [Arg1, Infix, Arg2, form_positions(ArgNs)]);
-message_to_string({opaque_guard, [Guard, Args]}) ->
+ [a(Arg1, I), Infix, a(Arg2, I), form_positions(ArgNs)]);
+message_to_string({opaque_guard, [Guard, Args]}, I) ->
io_lib:format("Guard test ~w~ts breaks the opacity of its argument\n",
- [Guard, Args]);
-message_to_string({opaque_match, [Pat, OpaqueType, OpaqueTerm]}) ->
+ [Guard, a(Args, I)]);
+message_to_string({opaque_match, [Pat, OpaqueType, OpaqueTerm]}, I) ->
Term = if OpaqueType =:= OpaqueTerm -> "the term";
- true -> OpaqueTerm
+ true -> t(OpaqueTerm, I)
end,
- io_lib:format("The attempt to match a term of type ~s against the ~ts"
- " breaks the opacity of ~ts\n", [OpaqueType, Pat, Term]);
-message_to_string({opaque_neq, [Type, _Op, OpaqueType]}) ->
+ io_lib:format("The attempt to match a term of type ~ts against the ~ts"
+ " breaks the opacity of ~ts\n",
+ [t(OpaqueType, I), ps(Pat, I), Term]);
+message_to_string({opaque_neq, [Type, _Op, OpaqueType]}, I) ->
io_lib:format("Attempt to test for inequality between a term of type ~ts"
- " and a term of opaque type ~ts\n", [Type, OpaqueType]);
-message_to_string({opaque_type_test, [Fun, Args, Arg, ArgType]}) ->
+ " and a term of opaque type ~ts\n",
+ [t(Type, I), t(OpaqueType, I)]);
+message_to_string({opaque_type_test, [Fun, Args, Arg, ArgType]}, I) ->
io_lib:format("The type test ~ts~ts breaks the opacity of the term ~ts~ts\n",
- [Fun, Args, Arg, ArgType]);
-message_to_string({opaque_size, [SizeType, Size]}) ->
+ [Fun, a(Args, I), Arg, t(ArgType, I)]);
+message_to_string({opaque_size, [SizeType, Size]}, I) ->
io_lib:format("The size ~ts breaks the opacity of ~ts\n",
- [SizeType, Size]);
-message_to_string({opaque_call, [M, F, Args, Culprit, OpaqueType]}) ->
+ [t(SizeType, I), c(Size, I)]);
+message_to_string({opaque_call, [M, F, Args, Culprit, OpaqueType]}, I) ->
io_lib:format("The call ~s:~ts~ts breaks the opacity of the term ~ts :: ~ts\n",
- [M, F, Args, Culprit, OpaqueType]);
+ [M, F, a(Args, I), c(Culprit, I), t(OpaqueType, I)]);
%%----- Warnings for concurrency errors --------------------
-message_to_string({race_condition, [M, F, Args, Reason]}) ->
- io_lib:format("The call ~w:~tw~ts ~ts\n", [M, F, Args, Reason]);
+message_to_string({race_condition, [M, F, Args, Reason]}, I) ->
+ %% There is a possibly huge type in Reason.
+ io_lib:format("The call ~w:~tw~ts ~ts\n", [M, F, a(Args, I), Reason]);
%%----- Warnings for behaviour errors --------------------
-message_to_string({callback_type_mismatch, [B, F, A, ST, CT]}) ->
- io_lib:format("The inferred return type of ~tw/~w (~ts) has nothing in"
+message_to_string({callback_type_mismatch, [B, F, A, ST, CT]}, I) ->
+ io_lib:format("The inferred return type of ~tw/~w ~ts has nothing in"
" common with ~ts, which is the expected return type for"
- " the callback of the ~w behaviour\n", [F, A, ST, CT, B]);
-message_to_string({callback_arg_type_mismatch, [B, F, A, N, ST, CT]}) ->
+ " 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) ->
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"
" argument in the callback of the ~w behaviour\n",
- [ordinal(N), F, A, ST, CT, B]);
-message_to_string({callback_spec_type_mismatch, [B, F, A, ST, CT]}) ->
+ [ordinal(N), F, A, t(ST, I), t(CT, I), B]);
+message_to_string({callback_spec_type_mismatch, [B, F, A, ST, CT]}, I) ->
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"
- " callback of the ~w behaviour\n", [ST, F, A, CT, B]);
-message_to_string({callback_spec_arg_type_mismatch, [B, F, A, N, ST, CT]}) ->
+ " 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) ->
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"
" argument in the callback of the ~w behaviour\n",
- [ordinal(N), F, A, ST, CT, B]);
-message_to_string({callback_missing, [B, F, A]}) ->
+ [ordinal(N), F, A, t(ST, I), t(CT, I), B]);
+message_to_string({callback_missing, [B, F, A]}, _I) ->
io_lib:format("Undefined callback function ~tw/~w (behaviour ~w)\n",
[F, A, B]);
-message_to_string({callback_info_missing, [B]}) ->
+message_to_string({callback_info_missing, [B]}, _I) ->
io_lib:format("Callback info about the ~w behaviour is not available\n", [B]);
%%----- Warnings for unknown functions, types, and behaviours -------------
-message_to_string({unknown_type, {M, F, A}}) ->
+message_to_string({unknown_type, {M, F, A}}, _I) ->
io_lib:format("Unknown type ~w:~tw/~w", [M, F, A]);
-message_to_string({unknown_function, {M, F, A}}) ->
+message_to_string({unknown_function, {M, F, A}}, _I) ->
io_lib:format("Unknown function ~w:~tw/~w", [M, F, A]);
-message_to_string({unknown_behaviour, B}) ->
+message_to_string({unknown_behaviour, B}, _I) ->
io_lib:format("Unknown behaviour ~w", [B]).
%%-----------------------------------------------------------------------------
@@ -494,7 +520,7 @@ message_to_string({unknown_behaviour, B}) ->
%%-----------------------------------------------------------------------------
call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet,
- {IsOverloaded, Contract}) ->
+ {IsOverloaded, Contract}, I) ->
PositionString = form_position_string(ArgNs),
case FailReason of
only_sig ->
@@ -502,24 +528,25 @@ call_or_apply_to_string(ArgNs, FailReason, SigArgs, SigRet,
true ->
%% We do not know which argument(s) caused the failure
io_lib:format("will never return since the success typing arguments"
- " are ~ts\n", [SigArgs]);
+ " are ~ts\n", [t(SigArgs, I)]);
false ->
io_lib:format("will never return since it differs in the ~s argument"
" from the success typing arguments: ~ts\n",
- [PositionString, SigArgs])
+ [PositionString, t(SigArgs, I)])
end;
only_contract ->
case (ArgNs =:= []) orelse IsOverloaded of
true ->
%% We do not know which arguments caused the failure
- io_lib:format("breaks the contract ~ts\n", [Contract]);
+ io_lib:format("breaks the contract ~ts\n", [sig(Contract, I)]);
false ->
io_lib:format("breaks the contract ~ts in the ~s argument\n",
- [Contract, PositionString])
+ [sig(Contract, I), PositionString])
end;
both ->
io_lib:format("will never return since the success typing is ~ts -> ~ts"
- " and the contract is ~ts\n", [SigArgs, SigRet, Contract])
+ " and the contract is ~ts\n",
+ [t(SigArgs, I), t(SigRet, I), sig(Contract, I)])
end.
form_positions(ArgNs) ->
@@ -534,24 +561,27 @@ form_positions(ArgNs) ->
%% We know which positions N are to blame;
%% the list of triples will never be empty.
-form_expected_without_opaque([{N, T, TStr}]) ->
+form_expected_without_opaque([{N, T, TStr}], I) ->
case erl_types:t_is_opaque(T) of
true ->
- io_lib:format("an opaque term of type ~ts as ", [TStr]);
+ io_lib:format("an opaque term of type ~ts as ", [t(TStr, I)]);
false ->
- io_lib:format("a term of type ~ts (with opaque subterms) as ", [TStr])
+ io_lib:format("a term of type ~ts (with opaque subterms) as ",
+ [t(TStr, I)])
end ++ form_position_string([N]) ++ " argument";
-form_expected_without_opaque(ExpectedTriples) -> %% TODO: can do much better here
+form_expected_without_opaque(ExpectedTriples, _I) -> %% TODO: can do much better here
{ArgNs, _Ts, _TStrs} = lists:unzip3(ExpectedTriples),
"opaque terms as " ++ form_position_string(ArgNs) ++ " arguments".
-form_expected(ExpectedArgs) ->
+form_expected(ExpectedArgs, I) ->
case ExpectedArgs of
[T] ->
TS = erl_types:t_to_string(T),
case erl_types:t_is_opaque(T) of
- true -> io_lib:format("an opaque term of type ~ts is expected", [TS]);
- false -> io_lib:format("a structured term of type ~ts is expected", [TS])
+ true -> io_lib:format("an opaque term of type ~ts is expected",
+ [t(TS, I)]);
+ false -> io_lib:format("a structured term of type ~ts is expected",
+ [t(TS, I)])
end;
[_,_|_] -> "terms of different types are expected in these positions"
end.
@@ -571,3 +601,155 @@ ordinal(1) -> "1st";
ordinal(2) -> "2nd";
ordinal(3) -> "3rd";
ordinal(N) when is_integer(N) -> io_lib:format("~wth", [N]).
+
+%% Functions that parse type strings, literal strings, and contract
+%% strings. Return strings formatted by erl_pp.
+
+%% Note we always have to catch any error when trying to parse
+%% the syntax because other BEAM languages may not emit an
+%% Erlang AST that transforms into valid Erlang Source Code.
+
+-define(IND, 10).
+
+con(M, F, Src, I) ->
+ S = sig(Src, I),
+ io_lib:format("~w:~tw~ts", [M, F, S]).
+
+sig(Src, false) ->
+ Src;
+sig(Src, true) ->
+ try
+ Str = lists:flatten(io_lib:format("-spec ~w:~tw~ts.", [a, b, Src])),
+ {ok, Tokens, _EndLocation} = erl_scan:string(Str),
+ {ok, {attribute, _, spec, {_MFA, Types}}} =
+ erl_parse:parse_form(Tokens),
+ indentation(?IND) ++ pp_spec(Types)
+ catch
+ _:_ -> Src
+ end.
+
+%% Argument(list)s are a mix of types and Erlang code. Note: sometimes
+%% (contract_range, call_without_opaque, opaque_type_test), the initial
+%% newline is a bit out of place.
+a(""=Args, _I) ->
+ Args;
+a(Args, I) ->
+ t(Args, I).
+
+c(Cerl, _I) ->
+ Cerl.
+
+field_diffs(Src, false) ->
+ Src;
+field_diffs(Src, true) ->
+ Fields = string:split(Src, " and ", all),
+ lists:join(" and ", [field_diff(Field) || Field <- Fields]).
+
+field_diff(Field) ->
+ [F | Ts] = string:split(Field, "::", all),
+ F ++ " ::" ++ t(lists:flatten(lists:join("::", Ts)), true).
+
+rec_type("record "++Src, I) ->
+ "record " ++ t(Src, I).
+
+%% "variable"/"pattern" ++ cerl
+ps("pattern "++Src, I) ->
+ "pattern " ++ t(Src, I);
+ps("variable "++_=Src, _I) ->
+ Src;
+ps("record field"++Rest, I) ->
+ [S, TypeStr] = string:split(Rest, "of type ", all),
+ "record field" ++ S ++ "of type " ++ t(TypeStr, I).
+
+%% Scan and parse a type or a literal, and pretty-print it using erl_pp.
+t(Src, false) ->
+ Src;
+t("("++_=Src, true) ->
+ ts(Src);
+t(Src, true) ->
+ %% Binary types and products both start with a $<.
+ try parse_type_or_literal(Src) of
+ TypeOrLiteral ->
+ indentation(?IND) ++ pp_type(TypeOrLiteral)
+ catch
+ _:_ ->
+ ts(Src)
+ end.
+
+ts(Src) ->
+ Ind = indentation(?IND),
+ [C1|Src1] = Src, % $< (product) or $( (arglist)
+ [C2|RevSrc2] = lists:reverse(Src1),
+ Src2 = lists:reverse(RevSrc2),
+ try
+ Types = parse_types_and_literals(Src2),
+ CommaInd = [$, | Ind],
+ (indentation(?IND-1) ++
+ [C1 | lists:join(CommaInd, [pp_type(Type) || Type <- Types])] ++
+ [C2])
+ catch
+ _:_ -> Src
+ end.
+
+indentation(I) ->
+ [$\n | lists:duplicate(I, $\s)].
+
+pp_type(Type) ->
+ Form = {attribute, erl_anno:new(0), type, {t, Type, []}},
+ TypeDef = erl_pp:form(Form, [{quote_singleton_atom_types, true}]),
+ {match, [S]} = re:run(TypeDef, <<"::\\s*(.*)\\.\\n*">>,
+ [{capture, all_but_first, list}, dotall]),
+ S.
+
+pp_spec(Spec) ->
+ Form = {attribute, erl_anno:new(0), spec, {{a,b,0}, Spec}},
+ Sig = erl_pp:form(Form, [{quote_singleton_atom_types, true}]),
+ {match, [S]} = re:run(Sig, <<"-spec a:b\\s*(.*)\\.\\n*">>,
+ [{capture, all_but_first, list}, dotall]),
+ S.
+
+parse_types_and_literals(Src) ->
+ {ok, Tokens, _EndLocation} = erl_scan:string(Src),
+ [parse_a_type_or_literal(Ts) || Ts <- types(Tokens)].
+
+parse_type_or_literal(Src) ->
+ {ok, Tokens, _EndLocation} = erl_scan:string(Src),
+ parse_a_type_or_literal(Tokens).
+
+parse_a_type_or_literal(Ts0) ->
+ L = erl_anno:new(1),
+ Ts = Ts0 ++ [{dot,L}],
+ Tokens = [{'-',L}, {atom,L,type}, {atom,L,t}, {'(',L}, {')',L},
+ {'::',L}] ++ Ts,
+ case erl_parse:parse_form(Tokens) of
+ {ok, {attribute, _, type, {t, Type, []}}} ->
+ Type;
+ {error, _} ->
+ %% literal
+ {ok, [T]} = erl_parse:parse_exprs(Ts),
+ T
+ end.
+
+types([]) -> [];
+types(Ts) ->
+ {Ts0, Ts1} = one_type(Ts, [], []),
+ [Ts0 | types(Ts1)].
+
+one_type([], [], Ts) ->
+ {lists:reverse(Ts), []};
+one_type([{',', _Lc}|Toks], [], Ts0) ->
+ {lists:reverse(Ts0), Toks};
+one_type([{')', Lrp}|Toks], [], Ts0) ->
+ {lists:reverse(Ts0), [{')', Lrp}|Toks]};
+one_type([{'(', Llp}|Toks], E, Ts0) ->
+ one_type(Toks, [')'|E], [{'(', Llp}|Ts0]);
+one_type([{'<<', Lls}|Toks], E, Ts0) ->
+ one_type(Toks, ['>>'|E], [{'<<', Lls}|Ts0]);
+one_type([{'[', Lls}|Toks], E, Ts0) ->
+ one_type(Toks, [']'|E], [{'[', Lls}|Ts0]);
+one_type([{'{', Llc}|Toks], E, Ts0) ->
+ one_type(Toks, ['}'|E], [{'{', Llc}|Ts0]);
+one_type([{Rb, Lrb}|Toks], [Rb|E], Ts0) ->
+ one_type(Toks, E, [{Rb, Lrb}|Ts0]);
+one_type([T|Toks], E, Ts0) ->
+ one_type(Toks, E, [T|Ts0]).
diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl
index e7cf2860b7..e1821f10eb 100644
--- a/lib/dialyzer/src/dialyzer.hrl
+++ b/lib/dialyzer/src/dialyzer.hrl
@@ -108,6 +108,7 @@
-type dial_options() :: [dial_option()].
-type fopt() :: 'basename' | 'fullpath'.
-type format() :: 'formatted' | 'raw'.
+-type iopt() :: boolean().
-type label() :: non_neg_integer().
-type dial_warn_tags():: ordsets:ordset(dial_warn_tag()).
-type rep_mode() :: 'quiet' | 'normal' | 'verbose'.
@@ -119,6 +120,8 @@
%% Record declarations used by various files
%%--------------------------------------------------------------------
+-define(INDENT_OPT, true).
+
-type doc_plt() :: 'undefined' | dialyzer_plt:plt().
-record(analysis, {analysis_pid :: pid() | 'undefined',
@@ -154,9 +157,12 @@
output_file = none :: 'none' | file:filename(),
output_format = formatted :: format(),
filename_opt = basename :: fopt(),
+ indent_opt = ?INDENT_OPT :: iopt(),
callgraph_file = "" :: file:filename(),
check_plt = true :: boolean(),
- solvers = [] :: [solver()]}).
+ solvers = [] :: [solver()],
+ native = maybe :: boolean() | 'maybe',
+ native_cache = true :: boolean()}).
-record(contract, {contracts = [] :: [contract_pair()],
args = [] :: [erl_types:erl_type()],
diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl
index 1e06d6e974..5e680062fb 100644
--- a/lib/dialyzer/src/dialyzer_cl.erl
+++ b/lib/dialyzer/src/dialyzer_cl.erl
@@ -40,6 +40,7 @@
output = standard_io :: io:device(),
output_format = formatted :: format(),
filename_opt = basename :: fopt(),
+ indent_opt = ?INDENT_OPT :: iopt(),
output_plt = none :: 'none' | file:filename(),
plt_info = none :: 'none' | dialyzer_plt:plt_info(),
report_mode = normal :: rep_mode(),
@@ -319,12 +320,6 @@ report_analysis_start(#options{analysis_type = Type,
end
end.
-report_native_comp(#options{report_mode = ReportMode}) ->
- case ReportMode of
- quiet -> ok;
- _ -> io:format(" Compiling some key modules to native code...")
- end.
-
report_elapsed_time(T1, T2, #options{report_mode = ReportMode}) ->
case ReportMode of
quiet -> ok;
@@ -374,7 +369,6 @@ do_analysis(Options) ->
do_analysis(Files, Options, Plt, PltInfo) ->
assert_writable(Options#options.output_plt),
- hipe_compile(Files, Options),
report_analysis_start(Options),
State0 = new_state(),
State1 = init_output(State0, Options),
@@ -483,113 +477,16 @@ expand_dependent_modules_1([Mod|Mods], Included, ModDeps) ->
expand_dependent_modules_1([], Included, _ModDeps) ->
Included.
--define(MIN_PARALLELISM, 7).
--define(MIN_FILES_FOR_NATIVE_COMPILE, 20).
-
--spec hipe_compile([file:filename()], #options{}) -> 'ok'.
-
-hipe_compile(Files, #options{erlang_mode = ErlangMode} = Options) ->
- NoNative = (get(dialyzer_options_native) =:= false),
- FewFiles = (length(Files) < ?MIN_FILES_FOR_NATIVE_COMPILE),
- case NoNative orelse FewFiles orelse ErlangMode of
- true -> ok;
- false ->
- case erlang:system_info(hipe_architecture) of
- undefined -> ok;
- _ ->
- Mods = [lists, dict, digraph, digraph_utils, ets,
- gb_sets, gb_trees, ordsets, sets, sofs,
- cerl, erl_types, cerl_trees, erl_bif_types,
- dialyzer_analysis_callgraph, dialyzer, dialyzer_behaviours,
- dialyzer_codeserver, dialyzer_contracts,
- dialyzer_coordinator, dialyzer_dataflow, dialyzer_dep,
- dialyzer_plt, dialyzer_succ_typings, dialyzer_typesig,
- dialyzer_worker],
- report_native_comp(Options),
- {T1, _} = statistics(wall_clock),
- Cache = (get(dialyzer_options_native_cache) =/= false),
- native_compile(Mods, Cache),
- {T2, _} = statistics(wall_clock),
- report_elapsed_time(T1, T2, Options)
- end
- end.
-
-native_compile(Mods, Cache) ->
- case dialyzer_utils:parallelism() > ?MIN_PARALLELISM of
- true ->
- Parent = self(),
- Pids = [spawn(fun () -> Parent ! {self(), hc(M, Cache)} end) || M <- Mods],
- lists:foreach(fun (Pid) -> receive {Pid, Res} -> Res end end, Pids);
- false ->
- lists:foreach(fun (Mod) -> hc(Mod, Cache) end, Mods)
- end.
-
-hc(Mod, Cache) ->
- {module, Mod} = code:ensure_loaded(Mod),
- case code:is_module_native(Mod) of
- true -> ok;
- false ->
- %% io:format(" ~w", [Mod]),
- case Cache of
- false ->
- {ok, Mod} = hipe:c(Mod),
- ok;
- true ->
- hc_cache(Mod)
- end
- end.
-
-hc_cache(Mod) ->
- CacheBase = cache_base_dir(),
- %% Use HiPE architecture, version and erts checksum in directory name,
- %% to avoid clashes between incompatible binaries.
- HipeArchVersion =
- lists:concat(
- [erlang:system_info(hipe_architecture), "-",
- hipe:version(), "-",
- hipe:erts_checksum()]),
- CacheDir = filename:join(CacheBase, HipeArchVersion),
- OrigBeamFile = code:which(Mod),
- {ok, {Mod, <<Checksum:128>>}} = beam_lib:md5(OrigBeamFile),
- CachedBeamFile = filename:join(CacheDir, lists:concat([Mod, "-", Checksum, ".beam"])),
- ok = filelib:ensure_dir(CachedBeamFile),
- ModBin =
- case filelib:is_file(CachedBeamFile) of
- true ->
- {ok, BinFromFile} = file:read_file(CachedBeamFile),
- BinFromFile;
- false ->
- {ok, Mod, CompiledBin} = compile:file(OrigBeamFile, [from_beam, native, binary]),
- ok = file:write_file(CachedBeamFile, CompiledBin),
- CompiledBin
- end,
- code:unstick_dir(filename:dirname(OrigBeamFile)),
- {module, Mod} = code:load_binary(Mod, CachedBeamFile, ModBin),
- true = code:is_module_native(Mod),
- ok.
-
-cache_base_dir() ->
- %% http://standards.freedesktop.org/basedir-spec/basedir-spec-0.7.html
- %% If XDG_CACHE_HOME is set to an absolute path, use it as base.
- XdgCacheHome = os:getenv("XDG_CACHE_HOME"),
- CacheHome =
- case is_list(XdgCacheHome) andalso filename:pathtype(XdgCacheHome) =:= absolute of
- true ->
- XdgCacheHome;
- false ->
- %% Otherwise, the default is $HOME/.cache.
- {ok, [[Home]]} = init:get_argument(home),
- filename:join(Home, ".cache")
- end,
- filename:join([CacheHome, "dialyzer_hipe_cache"]).
-
new_state() ->
#cl_state{}.
init_output(State0, #options{output_file = OutFile,
output_format = OutFormat,
- filename_opt = FOpt}) ->
- State = State0#cl_state{output_format = OutFormat, filename_opt = FOpt},
+ filename_opt = FOpt,
+ indent_opt = IOpt}) ->
+ State = State0#cl_state{output_format = OutFormat,
+ filename_opt = FOpt,
+ indent_opt = IOpt},
case OutFile =:= none of
true ->
State;
@@ -818,6 +715,7 @@ print_warnings(#cl_state{stored_warnings = []}) ->
print_warnings(#cl_state{output = Output,
output_format = Format,
filename_opt = FOpt,
+ indent_opt = IOpt,
stored_warnings = Warnings}) ->
PrWarnings = process_warnings(Warnings),
case PrWarnings of
@@ -825,7 +723,8 @@ print_warnings(#cl_state{output = Output,
[_|_] ->
S = case Format of
formatted ->
- [dialyzer:format_warning(W, FOpt) || W <- PrWarnings];
+ Opts = [{filename_opt, FOpt}, {indent_opt, IOpt}],
+ [dialyzer:format_warning(W, Opts) || W <- PrWarnings];
raw ->
[io_lib:format("~tp. \n",
[W]) || W <- set_warning_id(PrWarnings)]
diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl
index f21eaed087..cadc2116b0 100644
--- a/lib/dialyzer/src/dialyzer_cl_parse.erl
+++ b/lib/dialyzer/src/dialyzer_cl_parse.erl
@@ -134,6 +134,9 @@ cl(["--raw"|T]) ->
cl(["--fullpath"|T]) ->
put(dialyzer_filename_opt, fullpath),
cl(T);
+cl(["--no_indentation"|T]) ->
+ put(dialyzer_indent_opt, false),
+ cl(T);
cl(["-pa", Path|T]) ->
case code:add_patha(Path) of
true -> cl(T);
@@ -254,6 +257,7 @@ init() ->
put(dialyzer_options_files, DefaultOpts#options.files),
put(dialyzer_output_format, formatted),
put(dialyzer_filename_opt, basename),
+ put(dialyzer_indent_opt, ?INDENT_OPT),
put(dialyzer_options_check_plt, DefaultOpts#options.check_plt),
put(dialyzer_timing, DefaultOpts#options.timing),
put(dialyzer_solvers, DefaultOpts#options.solvers),
@@ -295,6 +299,7 @@ cl_options() ->
{output_file, get(dialyzer_output)},
{output_format, get(dialyzer_output_format)},
{filename_opt, get(dialyzer_filename_opt)},
+ {indent_opt, get(dialyzer_indent_opt)},
{analysis_type, get(dialyzer_options_analysis_type)},
{get_warnings, get(dialyzer_options_get_warnings)},
{timing, get(dialyzer_timing)},
@@ -311,7 +316,9 @@ common_options() ->
{use_spec, get(dialyzer_options_use_contracts)},
{warnings, get(dialyzer_warnings)},
{check_plt, get(dialyzer_options_check_plt)},
- {solvers, get(dialyzer_solvers)}].
+ {solvers, get(dialyzer_solvers)},
+ {native, get(dialyzer_options_native)},
+ {native_cache, get(dialyzer_options_native_cache)}].
%%-----------------------------------------------------------------------
@@ -361,7 +368,7 @@ help_message() ->
[--build_plt] [--add_to_plt] [--remove_from_plt]
[--check_plt] [--no_check_plt] [--plt_info] [--get_warnings]
[--dump_callgraph file] [--no_native] [--fullpath]
- [--statistics] [--no_native_cache]
+ [--no_indentation] [--statistics] [--no_native_cache]
Options:
files_or_dirs (for backwards compatibility also as: -c files_or_dirs)
Use Dialyzer from the command line to detect defects in the
@@ -473,6 +480,9 @@ Options:
caching.
--fullpath
Display the full path names of files for which warnings are emitted.
+ --no_indentation
+ Do not indent contracts and success typings. Note that this option has
+ no effect when combined with the --raw option.
--gui
Use the GUI.
diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl
index 9c36d745c3..17b2168852 100644
--- a/lib/dialyzer/src/dialyzer_contracts.erl
+++ b/lib/dialyzer/src/dialyzer_contracts.erl
@@ -911,7 +911,6 @@ is_remote_types_related(Contract, CSig, Sig, MFA, RecDict) ->
t_from_forms_without_remote([{FType, []}], MFA, RecDict) ->
Site = {spec, MFA},
- %% FIXME
Type1 = erl_types:t_from_form_without_remote(FType, Site, RecDict),
{ok, erl_types:subst_all_vars_to_any(Type1)};
t_from_forms_without_remote([{_FType, _Constrs}], _MFA, _RecDict) ->
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index 45b4abb253..55c77814f8 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -405,8 +405,13 @@ handle_apply(Tree, Map, State) ->
t_fun_args(OpType1, 'universe')),
case any_none(NewArgs) of
true ->
+ EnumNewArgs = lists:zip(lists:seq(1, length(NewArgs)),
+ NewArgs),
+ ArgNs = [Arg ||
+ {Arg, Type} <- EnumNewArgs, t_is_none(Type)],
Msg = {fun_app_args,
- [format_args(Args, ArgTypes, State),
+ [ArgNs,
+ format_args(Args, ArgTypes, State),
format_type(OpType, State)]},
State3 = state__add_warning(State2, ?WARN_FAILING_CALL,
Tree, Msg),
@@ -3639,14 +3644,15 @@ format_args(ArgList0, TypeList, State) ->
"(" ++ format_args_1(ArgList, TypeList, State) ++ ")".
format_args_1([Arg], [Type], State) ->
- format_arg(Arg) ++ format_type(Type, State);
+ format_arg_1(Arg, Type, State);
format_args_1([Arg|Args], [Type|Types], State) ->
- String =
- case cerl:is_literal(Arg) of
- true -> format_cerl(Arg);
- false -> format_arg(Arg) ++ format_type(Type, State)
- end,
- String ++ "," ++ format_args_1(Args, Types, State).
+ format_arg_1(Arg, Type, State) ++ "," ++ format_args_1(Args, Types, State).
+
+format_arg_1(Arg, Type, State) ->
+ case cerl:is_literal(Arg) of
+ true -> format_cerl(Arg);
+ false -> format_arg(Arg) ++ format_type(Type, State)
+ end.
format_arg(Arg) ->
Default = "",
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index b8414b7d8b..f47d90b91f 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -1135,7 +1135,9 @@ handle_help(State, Title, Txt) ->
add_warnings(#gui_state{warnings_box = WarnBox,
rawWarnings = RawWarns} = State, Warnings) ->
NewRawWarns = RawWarns ++ Warnings,
- WarnList = [dialyzer:format_warning(W) -- "\n" || W <- NewRawWarns],
+ %% The indentation cannot be turned off.
+ WarnList = [string:trim(dialyzer:format_warning(W), trailing) ||
+ W <- NewRawWarns],
wxListBox:set(WarnBox, WarnList),
State#gui_state{rawWarnings = NewRawWarns}.
diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl
index 03040f8e38..f88f4f8ea2 100644
--- a/lib/dialyzer/src/dialyzer_options.erl
+++ b/lib/dialyzer/src/dialyzer_options.erl
@@ -177,6 +177,8 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
filename_opt ->
assert_filename_opt(Value),
build_options(Rest, Options#options{filename_opt = Value});
+ indent_opt ->
+ build_options(Rest, Options#options{indent_opt = Value});
output_plt ->
assert_filename(Value),
build_options(Rest, Options#options{output_plt = Value});
@@ -195,6 +197,10 @@ build_options([{OptionName, Value} = Term|Rest], Options) ->
solvers ->
assert_solvers(Value),
build_options(Rest, Options#options{solvers = Value});
+ native ->
+ build_options(Rest, Options#options{native = Value});
+ native_cache ->
+ build_options(Rest, Options#options{native_cache = Value});
_ ->
bad_option("Unknown dialyzer command line option", Term)
end;
diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl
index 310301ee0b..245c099fef 100644
--- a/lib/dialyzer/src/dialyzer_utils.erl
+++ b/lib/dialyzer/src/dialyzer_utils.erl
@@ -46,6 +46,7 @@
]).
-include("dialyzer.hrl").
+-include("../../compiler/src/core_parse.hrl").
%%-define(DEBUG, true).
@@ -652,7 +653,7 @@ sets_filter([Mod|Mods], ExpTypes) ->
src_compiler_opts() ->
[no_copt, to_core, binary, return_errors,
no_inline, strict_record_tests, strict_record_updates,
- dialyzer].
+ dialyzer, no_spawn_compiler_process].
-spec format_errors([{module(), string()}]) -> [string()].
@@ -751,9 +752,13 @@ pp_hook(Node, Ctxt, Cont) ->
map ->
pp_map(Node, Ctxt, Cont);
literal ->
- case is_map(cerl:concrete(Node)) of
- true -> pp_map(Node, Ctxt, Cont);
- false -> Cont(Node, Ctxt)
+ case cerl:concrete(Node) of
+ Map when is_map(Map) ->
+ pp_map(Node, Ctxt, Cont);
+ Bitstr when is_bitstring(Bitstr) ->
+ pp_binary(Node, Ctxt, Cont);
+ _ ->
+ Cont(Node, Ctxt)
end;
_ ->
Cont(Node, Ctxt)
@@ -761,7 +766,7 @@ pp_hook(Node, Ctxt, Cont) ->
pp_binary(Node, Ctxt, Cont) ->
prettypr:beside(prettypr:text("<<"),
- prettypr:beside(pp_segments(cerl:binary_segments(Node),
+ prettypr:beside(pp_segments(cerl_binary_segments(Node),
Ctxt, Cont),
prettypr:text(">>"))).
@@ -780,10 +785,29 @@ pp_segment(Node, Ctxt, Cont) ->
Unit = cerl:bitstr_unit(Node),
Type = cerl:bitstr_type(Node),
Flags = cerl:bitstr_flags(Node),
- prettypr:beside(Cont(Val, Ctxt),
- prettypr:beside(pp_size(Size, Ctxt, Cont),
- prettypr:beside(pp_opts(Type, Flags),
- pp_unit(Unit, Ctxt, Cont)))).
+ RestPP =
+ case {concrete(Unit), concrete(Type), concrete(Flags)} of
+ {1, integer, [unsigned, big]} -> % Simplify common cases.
+ case concrete(Size) of
+ 8 -> prettypr:text("");
+ _ -> pp_size(Size, Ctxt, Cont)
+ end;
+ {8, binary, [unsigned, big]} ->
+ SizePP = pp_size(Size, Ctxt, Cont),
+ prettypr:beside(SizePP,
+ prettypr:beside(prettypr:text("/"), pp_atom(Type)));
+ _What ->
+ SizePP = pp_size(Size, Ctxt, Cont),
+ UnitPP = pp_unit(Unit, Ctxt, Cont),
+ OptsPP = pp_opts(Type, Flags),
+ prettypr:beside(SizePP, prettypr:beside(OptsPP, UnitPP))
+ end,
+ prettypr:beside(Cont(Val, Ctxt), RestPP).
+
+concrete(Cerl) ->
+ try cerl:concrete(Cerl)
+ catch _:_ -> anything_unexpected
+ end.
pp_size(Size, Ctxt, Cont) ->
case cerl:is_c_atom(Size) of
@@ -859,6 +883,31 @@ seq([H | T], Separator, Ctxt, Fun) ->
seq([], _, _, _) ->
[prettypr:empty()].
+cerl_binary_segments(#c_literal{val = B}) when is_bitstring(B) ->
+ segs_from_bitstring(B);
+cerl_binary_segments(CBinary) ->
+ cerl:binary_segments(CBinary).
+
+%% Copied from core_pp. The function cerl:binary_segments/2 should/could
+%% be extended to handle literals, but then the cerl module cannot be
+%% HiPE-compiled as of Erlang/OTP 22.0 (due to <<I:N>>).
+segs_from_bitstring(<<H,T/bitstring>>) ->
+ [#c_bitstr{val=#c_literal{val=H},
+ size=#c_literal{val=8},
+ unit=#c_literal{val=1},
+ type=#c_literal{val=integer},
+ flags=#c_literal{val=[unsigned,big]}}|segs_from_bitstring(T)];
+segs_from_bitstring(<<>>) ->
+ [];
+segs_from_bitstring(Bitstring) ->
+ N = bit_size(Bitstring),
+ <<I:N>> = Bitstring,
+ [#c_bitstr{val=#c_literal{val=I},
+ size=#c_literal{val=N},
+ unit=#c_literal{val=1},
+ type=#c_literal{val=integer},
+ flags=#c_literal{val=[unsigned,big]}}].
+
%%------------------------------------------------------------------------------
-spec refold_pattern(cerl:cerl()) -> cerl:cerl().
diff --git a/lib/dialyzer/src/typer.erl b/lib/dialyzer/src/typer.erl
index 4b99f5f72e..5a1b8619c9 100644
--- a/lib/dialyzer/src/typer.erl
+++ b/lib/dialyzer/src/typer.erl
@@ -980,7 +980,7 @@ fatal_error(Slogan) ->
mode_error(OldMode, NewMode) ->
Msg = io_lib:format("Mode was previously set to '~s'; "
- "can not set it to '~s' now",
+ "cannot set it to '~s' now",
[OldMode, NewMode]),
fatal_error(Msg).