From b0a7495c44ebf6741714948bdf5c0cb9593ec953 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 15 Mar 2019 15:45:28 +0100 Subject: dialyzer: Add an --no_indentation option By default Dialyzer tries to improve the readability of warnings. Newlines are inserted before and inside types, signatures, and Erlang code. Sometimes the newlines look misplaced. The raw format (option --raw) is not affected. Use the new --no_indentation option get the old behavior (no inserted line breaks). A note on the implementation. The types &c present in the warning tuples are parsed, and then formatted using erl_pp, the Erlang Pretty Printer. The alternative, to create message tuples with formatted types, turned out to more complicated, and has no real benefits. Also notice that the fixes for binaries (commit 9db8a098) and union elements (commit a1e51d12) are required. As a consequence, messages created before these commits using the --raw option cannot always be indented. --- lib/dialyzer/src/dialyzer.erl | 449 +++++++++++++++++++++++++++++------------- 1 file changed, 317 insertions(+), 132 deletions(-) (limited to 'lib/dialyzer/src/dialyzer.erl') diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index a168b3c8c5..d114980a91 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,55 +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, [ArgNs, 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," " which differs in the ~s argument\n", - [Args, Type, PositionString]); -message_to_string({fun_app_no_fun, [Op, Type, Arity]}) -> + [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 ~w~ts can never succeed\n", [Guard, a(Args, I)]); +message_to_string({neg_guard_fail, [Guard, Args]}, I) -> + io_lib:format("Guard test not(~w~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 "; @@ -361,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]). %%----------------------------------------------------------------------------- @@ -497,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 -> @@ -505,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) -> @@ -537,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. @@ -574,3 +601,161 @@ 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. + +%% If lib/hipe/cerl/erl_types.erl is compiled with DEBUG=true, +%% the contents of opaque types are showed inside brackets. +%% Since erl_parse:parse_form() cannot handle the bracket syntax, +%% no indentation is done. +%%-define(DEBUG , true). + +-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) -> + Str = lists:flatten(io_lib:format("-spec ~w:~tw~ts.", [a, b, Src])), + {ok, Tokens, _EndLocation} = erl_scan:string(Str), + exec(fun() -> + {ok, {attribute, _, spec, {_MFA, Types}}} = + erl_parse:parse_form(Tokens), + indentation(?IND) ++ pp_spec(Types) + end, Src). + +%% 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 "), + lists:join(" and ", [field_diff(Field) || Field <- Fields]). + +field_diff(Field) -> + [F | Ts] = string:split(Field, "::"), + 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 "), + "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), + exec(fun() -> + Types = parse_types_and_literals(Src2), + CommaInd = [$, | Ind], + (indentation(?IND-1) ++ + [C1 | lists:join(CommaInd, [pp_type(Type) || Type <- Types])] ++ + [C2]) + end, Src). + +-ifdef(DEBUG). +exec(F, R) -> + try F() catch _:_ -> R end. +-else. +exec(F, _) -> + F(). +-endif. + +indentation(I) -> + [$\n | lists:duplicate(I, $\s)]. + +pp_type(Type) -> + Form = {attribute, erl_anno:new(0), type, {t, Type, []}}, + TypeDef = erl_pp:form(Form), + {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]). -- cgit v1.2.3 From c2cd09c3d6807d2e93d117d9c6b47a706a50c763 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Sun, 14 Apr 2019 11:24:47 +0200 Subject: dialyzer: Remove quotes around operators --- lib/dialyzer/src/dialyzer.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'lib/dialyzer/src/dialyzer.erl') diff --git a/lib/dialyzer/src/dialyzer.erl b/lib/dialyzer/src/dialyzer.erl index d114980a91..cfe5fa9b3f 100644 --- a/lib/dialyzer/src/dialyzer.erl +++ b/lib/dialyzer/src/dialyzer.erl @@ -347,9 +347,9 @@ message_to_string({neg_guard_fail, [Arg1, Infix, Arg2]}, I) -> io_lib:format("Guard test not(~ts ~s ~ts) can never succeed\n", [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 ~w~ts can never succeed\n", [Guard, a(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(~w~ts) can never succeed\n", + 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" @@ -702,7 +702,7 @@ indentation(I) -> pp_type(Type) -> Form = {attribute, erl_anno:new(0), type, {t, Type, []}}, - TypeDef = erl_pp:form(Form), + TypeDef = erl_pp:form(Form, [{quote_singleton_atom_types, true}]), {match, [S]} = re:run(TypeDef, <<"::\\s*(.*)\\.\\n*">>, [{capture, all_but_first, list}, dotall]), S. -- cgit v1.2.3