mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 17:33:38 +00:00
compiler/unused_args_warn_pragma.m:
The existing code processed only the first procedure of each predicate,
skipping all the later procedures. It had a comment saying that it
warns about an unused arg only if it was unused in all modes,
but this claim was false.
Replace this old code with new code that
- gathers the set of unused args in each procedure, recording
which ones have mode "unused",
- considers all the procedures of a predicate together, and then
- generates either a single warning for the predicate as a whole,
or separate warnings for each procedure that has unused arguments.
We now generate a single warning for the predicate only if all the
procedures agree both on which arguments are unused, and on
which of those are *marked* by the mode as unused. Of course,
most of the time this will be the case simply because most predicates
have just one procedure.
Stop module qualifying predicate names in the warnings we generate,
since we do not generate warnings for imported predicates.
Color the unqualified name as the subject of the diagnostic.
When reporting unused args, list the arguments with "unused" modes
separately from the other arguments.
Simplify the interface with our caller in unused_args.m.
compiler/unused_args.m:
Conform to the simplified interface with unused_args_warn_pragma.m.
compiler/hlds_error_util.m:
Add a new version of an existing utility function.
tests/warnings/unused_args_some_modes.{m,err_exp}:
Add a test case for the new capability.
tests/warnings/Mmakefile:
Enable the new test case.
Stop mixing "VAR = VALUE" and "Var += VALUE" definitions
of make variables. Give some make variables better names.
Move some dependency definitions out of a block of rules.
tests/warnings/Mercury.options:
Enable --warn-unused-args for the new test case.
Delete some accidentally-duplicated entries.
tests/warnings/unused_args_test.err_exp:
Update the expected output.
957 lines
38 KiB
Mathematica
957 lines
38 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-2007, 2009-2012 The University of Melbourne.
|
|
% Copyright (C) 2014-2017, 2019-2026 The Mercury team.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: hlds_error_util.m.
|
|
% Main author: zs.
|
|
%
|
|
% This module contains code that can be helpful in the generation or
|
|
% formatting of error messages. It builds upon parse_tree.error_spec,
|
|
% and extends it with predicates that access HLDS data structures.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module hlds.hlds_error_util.
|
|
:- interface.
|
|
|
|
:- import_module hlds.hlds_goal.
|
|
:- import_module hlds.hlds_markers.
|
|
:- import_module hlds.hlds_module.
|
|
:- import_module hlds.hlds_pred.
|
|
:- import_module hlds.pred_table.
|
|
:- import_module libs.
|
|
:- import_module libs.maybe_util.
|
|
:- import_module parse_tree.
|
|
:- import_module parse_tree.error_spec.
|
|
:- import_module parse_tree.parse_tree_out_info.
|
|
:- import_module parse_tree.prog_data.
|
|
:- import_module parse_tree.var_db.
|
|
|
|
:- import_module assoc_list.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module pair.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Predicates to convert predicate and procedure names to strings.
|
|
%
|
|
|
|
:- type should_module_qualify
|
|
---> should_module_qualify
|
|
; should_not_module_qualify.
|
|
|
|
% describe_qual_pred_name(ModuleInfo, PredId) = Spec:
|
|
% describe_unqual_pred_name(ModuleInfo, PredId) = Spec:
|
|
% describe_one_pred_name(ModuleInfo, MaybeColor, Qual, SuffixPieces,
|
|
% PredId) = Spec:
|
|
%
|
|
% Return a description of the given predicate or function. This
|
|
% description will have one of the forms
|
|
%
|
|
% predicate `symname'/arity
|
|
% function `symname'/arity
|
|
%
|
|
% in both cases followed by SuffixPieces.
|
|
%
|
|
% The Qual parameter governs whether the sym_name will contain
|
|
% its full module qualification, or none.
|
|
%
|
|
% If MaybeColor is yes(Color), then the initial "predicate" or "function"
|
|
% will not be in that color, but the`symname'/arity part, *and*
|
|
% the SuffixPieces, will be. (The reason for taking SuffixPieces
|
|
% as an argument is specifically intended to make this possible
|
|
% without redundant switches between colors.)
|
|
%
|
|
% For describe_qual_pred_name/describe_unqual_pred_name, MaybeColor
|
|
% is implicitly "no", SuffixPieces is [], and the value of Qual is
|
|
% given in the predicate name.
|
|
%
|
|
:- func describe_qual_pred_name(module_info, pred_id) = list(format_piece).
|
|
:- func describe_unqual_pred_name(module_info, pred_id) = list(format_piece).
|
|
:- func describe_one_pred_name(module_info, maybe(color_name),
|
|
should_module_qualify, list(format_piece), pred_id) = list(format_piece).
|
|
|
|
% describe_one_pred_info_name(MaybeColor, Qual, SuffixPieces, PredInfo)
|
|
% = Spec:
|
|
%
|
|
% Does the same job as describe_one_pred_name, except for letting the
|
|
% caller do the lookup of the pred_info.
|
|
%
|
|
:- func describe_one_pred_info_name(maybe(color_name), should_module_qualify,
|
|
list(format_piece), pred_info) = list(format_piece).
|
|
|
|
% describe_several_pred_names(ModuleInfo, MaybeColor, Qual, PredIds)
|
|
% = Spec:
|
|
%
|
|
% Invoke describe_one_pred_name on each of the given PredIds,
|
|
% and return the results of those invocations joined togther in a list
|
|
% with the final pair of predicate descriptions separated by "and".
|
|
% We pass the given MaybeColor and Qual, and [] as SuffixPieces,
|
|
% to each call to describe_one_pred_name. This is because it does not
|
|
% make sense to add the same suffix to the description of every pred.
|
|
% (Though it may make sense to add it to the last one.)
|
|
%
|
|
:- func describe_several_pred_names(module_info, maybe(color_name),
|
|
should_module_qualify, list(pred_id)) = list(format_piece).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% describe_one_proc_name_maybe_argmodes(ModuleInfo, Lang, MaybeColor,
|
|
% Qual, SuffixPieces, PredProcId) = Spec:
|
|
%
|
|
% Return a description of the given procedure.
|
|
%
|
|
% If the procedure is the only procedure in its predicate or function,
|
|
% the description will just a description of the predicate or function
|
|
% as constructed by describe_one_pred_name.
|
|
%
|
|
% If the procedure is NOT the only procedure in its predicate or function,
|
|
% then description will consist of the name of the predicate or function
|
|
% (module qualified if Qual is should_module_qualify), followed by
|
|
% the modes of its arguments. The result will look like one of these:
|
|
%
|
|
% `name(in, in, out)'
|
|
% `name(in, in) = out'
|
|
%
|
|
:- func describe_one_proc_name_maybe_argmodes(module_info, output_lang,
|
|
maybe(color_name), should_module_qualify, list(format_piece), pred_proc_id)
|
|
= list(format_piece).
|
|
|
|
% describe_one_proc_name_maybe_argmodes(PredInfo, Lang, MaybeColor,
|
|
% Qual, SuffixPieces, ProcId) = Spec:
|
|
%
|
|
%
|
|
% Does the same job as describe_one_proc_name_maybe_argmodes, but
|
|
% lets the caller look up the pred_info.
|
|
%
|
|
:- func describe_one_proc_name_pred_info_maybe_argmodes(pred_info, output_lang,
|
|
maybe(color_name), should_module_qualify, list(format_piece), proc_id)
|
|
= list(format_piece).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% describe_qual_proc_name(ModuleInfo, PredProcId) = Spec:
|
|
% describe_unqual_proc_name(ModuleInfo, PredProcId) = Spec:
|
|
% describe_one_proc_name(ModuleInfo, MaybeColor, Qual, SuffixPieces,
|
|
% PredProcId) = Spec:
|
|
%
|
|
% Return a description of the given procedure. These descriptions
|
|
% will consist of a description of the predicate or function to which
|
|
% the procedure belongs (as constructed by describe_one_pred_name),
|
|
% followed by a suffix of the form "mode N".
|
|
%
|
|
:- func describe_qual_proc_name(module_info, pred_proc_id)
|
|
= list(format_piece).
|
|
:- func describe_unqual_proc_name(module_info, pred_proc_id)
|
|
= list(format_piece).
|
|
:- func describe_one_proc_name(module_info, maybe(color_name),
|
|
should_module_qualify, pred_proc_id) = list(format_piece).
|
|
|
|
% describe_several_proc_names(ModuleInfo, MaybeColor, Qual, PredProcIds)
|
|
% = Spec:
|
|
%
|
|
% Do the same job for procedures as describe_several_pred_names does
|
|
% for predicates and functions.
|
|
%
|
|
:- func describe_several_proc_names(module_info, maybe(color_name),
|
|
should_module_qualify, list(pred_proc_id)) = list(format_piece).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% describe_one_call_site(ModuleInfo, MaybeColor, Qual, Site) = Pieces:
|
|
%
|
|
% Return a description of a call site, which a pair consisting of the
|
|
% id of the callee, and the context of the call. The description
|
|
% will consist of the description of the callee as returned by
|
|
% describe_one_proc_name, followed by test of the form
|
|
% "at filename:linenumber".
|
|
%
|
|
:- func describe_one_call_site(module_info, maybe(color_name),
|
|
should_module_qualify, pair(pred_proc_id, prog_context))
|
|
= list(format_piece).
|
|
|
|
% describe_several_call_sites(ModuleInfo, MaybeColor, Qual, Sites)
|
|
% = Pieces:
|
|
%
|
|
% Invoke describe_one_call_site on each call site, and join the
|
|
% resulting descriptions together with commas and a final "and".
|
|
%
|
|
:- func describe_several_call_sites(module_info, maybe(color_name),
|
|
should_module_qualify, assoc_list(pred_proc_id, prog_context))
|
|
= list(format_piece).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type last_context_word
|
|
---> lcw_none
|
|
; lcw_call
|
|
; lcw_result
|
|
; lcw_argument
|
|
; lcw_element.
|
|
|
|
% unify_context_to_pieces generates a message such as
|
|
% foo.m:123: in argument 3 of functor `foo/5':
|
|
% foo.m:123: in unification of `X' and `blah':
|
|
% based on the unify_context and prog_context.
|
|
%
|
|
:- pred unify_context_to_pieces(unify_context::in, last_context_word::out,
|
|
list(format_piece)::in, list(format_piece)::out) is det.
|
|
|
|
% unify_context_first_to_pieces is the same as above, except that
|
|
% it also takes and returns a flag which specifies whether this is the
|
|
% start of a sentence. If the first argument is `is_first', then it means
|
|
% this is the first line of an error message, so the message starts with
|
|
% a capital letter, e.g.
|
|
% foo.m:123: In argument 3 of functor `foo/5':
|
|
% foo.m:123: in unification of `X' and `blah':
|
|
% The flag returned as the second argument will be `is_not_first'
|
|
% unless nothing was generated, in which case it will be the same
|
|
% as the first argument.
|
|
%
|
|
:- pred unify_context_first_to_pieces(is_first::in, is_first::out,
|
|
unify_context::in, last_context_word::out,
|
|
list(format_piece)::in, list(format_piece)::out) is det.
|
|
|
|
% Succeed iff the given cons_id either *does* represent list.[|],
|
|
% or (before typechecking has finished) *may* represent list.[|].
|
|
%
|
|
:- pred cons_id_may_be_list_cons(cons_id::in) is semidet.
|
|
|
|
:- func argument_to_pieces(unify_sub_context) = list(format_piece).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% When a higher order call uses either P(A, B, C) or C = F(A, B) syntax,
|
|
% we normally identify the call as being to "the predicate P" or to
|
|
% "the function F". However, there is a category of errors for which
|
|
% this is inappropriate: when the error is calling a function-valued
|
|
% variable as if it were a predicate, and vice versa. In such cases,
|
|
% we don't want the description of the error's context to say e.g.
|
|
% "in the call to the predicate P", and the description of the error
|
|
% itself to say "P is a function, but should be a predicate".
|
|
% Code that wants to report such errors should call the functions below
|
|
% with do_not_print_ho_var_name; pretty much all other callers should
|
|
% pass print_ho_var_name.
|
|
:- type maybe_print_ho_var_name
|
|
---> do_not_print_ho_var_name
|
|
; print_ho_var_name.
|
|
|
|
:- func call_id_to_pieces(maybe_print_ho_var_name, call_id) =
|
|
list(format_piece).
|
|
|
|
% generic_call_to_pieces(PrintHoVarName, VarNameSrc, GenericCall) = Pieces:
|
|
%
|
|
% Return a description of GenericCall as Pieces.
|
|
%
|
|
% For a description of the semantics of PrintHoVarName, see the
|
|
% definition of its type above.
|
|
%
|
|
% We use VarNameSrc for describing the callee of higher order calls.
|
|
% The type of this argument is var_name_source because we use this
|
|
% function both during the type analysis pass (which occurs before
|
|
% we construct var_tables, since it actually constructs var_tables),
|
|
% and during mode and determinism analysis, which do use var_tables.
|
|
%
|
|
:- func generic_call_to_pieces(maybe_print_ho_var_name, var_name_source,
|
|
generic_call) = list(format_piece).
|
|
|
|
% This variant of generic_call_to_string returns a string that
|
|
% specifically describes the *callee* of the call, not the call
|
|
% as a whole.
|
|
%
|
|
:- func generic_callee_to_pieces(maybe_print_ho_var_name, var_name_source,
|
|
generic_call) = list(format_piece).
|
|
|
|
% Generate a message of the form "argument %i of call to pred_or_func
|
|
% `foo/n'". The pred_markers argument is used to tell if the calling
|
|
% predicate is a type class method implementation; if so, we omit the
|
|
% "call to" part, since the user didn't write any explicit call.
|
|
%
|
|
:- func call_arg_id_to_pieces(maybe_print_ho_var_name, call_id, int,
|
|
pred_markers) = list(format_piece).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Return the arities that the given pred_ids have.
|
|
%
|
|
:- pred find_pred_arities(pred_id_table::in, list(pred_id)::in,
|
|
list(pred_form_arity)::out) is det.
|
|
:- pred find_user_arities(pred_id_table::in, list(pred_id)::in,
|
|
list(user_arity)::out) is det.
|
|
|
|
% Return the arities that the given pred_ids have,
|
|
% other than the given arity.
|
|
%
|
|
:- pred find_pred_arities_other_than(pred_id_table::in, list(pred_id)::in,
|
|
pred_form_arity::in, list(pred_form_arity)::out) is det.
|
|
:- pred find_user_arities_other_than(pred_id_table::in, list(pred_id)::in,
|
|
user_arity::in, list(user_arity)::out) is det.
|
|
|
|
:- func project_user_arity_int(user_arity) = int.
|
|
|
|
:- func project_pred_form_arity_int(pred_form_arity) = int.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module hlds.hlds_out.
|
|
:- import_module hlds.hlds_out.hlds_out_util.
|
|
:- import_module hlds.pred_name.
|
|
:- import_module hlds.special_pred.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.builtin_modules.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.parse_tree_out_cons_id.
|
|
:- import_module parse_tree.parse_tree_out_inst.
|
|
:- import_module parse_tree.parse_tree_out_misc.
|
|
:- import_module parse_tree.prog_mode.
|
|
:- import_module parse_tree.prog_util.
|
|
|
|
:- import_module int.
|
|
:- import_module map.
|
|
:- import_module string.
|
|
:- import_module term_context.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
describe_qual_pred_name(ModuleInfo, PredId) =
|
|
describe_one_pred_name(ModuleInfo, no, should_module_qualify,
|
|
[], PredId).
|
|
|
|
describe_unqual_pred_name(ModuleInfo, PredId) =
|
|
describe_one_pred_name(ModuleInfo, no, should_not_module_qualify,
|
|
[], PredId).
|
|
|
|
describe_one_pred_name(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
|
SuffixPieces, PredId) = Pieces :-
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
Pieces = describe_one_pred_info_name(MaybeColor, ShouldModuleQualify,
|
|
SuffixPieces, PredInfo).
|
|
|
|
describe_one_pred_info_name(MaybeColor, ShouldModuleQualify, SuffixPieces,
|
|
PredInfo) = Pieces :-
|
|
% NOTE The code of this predicate duplicates the functionality of
|
|
% hlds_out.write_pred_id. Changes here should be made there as well.
|
|
%
|
|
% XXX This predicate should subcontract its work to pred_name.m.
|
|
PredName = pred_info_name(PredInfo),
|
|
ModuleName = pred_info_module(PredInfo),
|
|
pred_info_get_orig_arity(PredInfo, PredFormArity),
|
|
pred_info_get_markers(PredInfo, Markers),
|
|
pred_info_get_origin(PredInfo, Origin),
|
|
( if Origin = origin_compiler(made_for_uci(SpecialId, TypeCtor)) then
|
|
special_pred_description(SpecialId, Descr),
|
|
TypeCtor = type_ctor(TypeSymName, TypeArity),
|
|
(
|
|
ShouldModuleQualify = should_module_qualify,
|
|
TypeSymNamePiece = qual_sym_name(TypeSymName)
|
|
;
|
|
ShouldModuleQualify = should_not_module_qualify,
|
|
TypeSymNamePiece = unqual_sym_name(TypeSymName)
|
|
),
|
|
( if TypeArity = 0 then
|
|
Pieces0 = [words(Descr), words("for type"), TypeSymNamePiece]
|
|
else
|
|
Pieces0 = [words(Descr), words("for type constructor"),
|
|
TypeSymNamePiece]
|
|
),
|
|
Pieces = maybe_color_pieces(MaybeColor, Pieces0 ++ SuffixPieces)
|
|
else if Origin = origin_user(user_made_class_method(_, PFNA)) then
|
|
PFNA = pred_pf_name_arity(PredOrFunc, SymName, UserArity),
|
|
UserArity = user_arity(UserArityInt),
|
|
SNA = sym_name_arity(SymName, UserArityInt),
|
|
(
|
|
ShouldModuleQualify = should_module_qualify,
|
|
SNAPiece = qual_sym_name_arity(SNA)
|
|
;
|
|
ShouldModuleQualify = should_not_module_qualify,
|
|
SNAPiece = unqual_sym_name_arity(SNA)
|
|
),
|
|
Pieces = [words("typeclass method"), p_or_f(PredOrFunc)] ++
|
|
maybe_color_pieces(MaybeColor, [SNAPiece] ++ SuffixPieces)
|
|
else if Origin = origin_user(user_made_instance_method(PFNA, _)) then
|
|
PFNA = pred_pf_name_arity(PredOrFunc, SymName, UserArity),
|
|
UserArity = user_arity(UserArityInt),
|
|
SNA = sym_name_arity(SymName, UserArityInt),
|
|
(
|
|
ShouldModuleQualify = should_module_qualify,
|
|
SNAPiece = qual_sym_name_arity(SNA)
|
|
;
|
|
ShouldModuleQualify = should_not_module_qualify,
|
|
SNAPiece = unqual_sym_name_arity(SNA)
|
|
),
|
|
Pieces = [words("instance method"), p_or_f(PredOrFunc)] ++
|
|
maybe_color_pieces(MaybeColor, [SNAPiece] ++ SuffixPieces)
|
|
else if marker_is_present(Markers, marker_class_instance_method) then
|
|
Pieces0 = [words("type class method implementation")] ++ SuffixPieces,
|
|
Pieces = maybe_color_pieces(MaybeColor, Pieces0)
|
|
else if pred_info_is_promise(PredInfo, PromiseType) then
|
|
Pieces0 = [quote(promise_to_string(PromiseType)),
|
|
words("declaration")] ++ SuffixPieces,
|
|
Pieces = maybe_color_pieces(MaybeColor, Pieces0)
|
|
else
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
( if marker_is_present(Markers, marker_class_method) then
|
|
PrefixPieces =
|
|
[words("type class"), p_or_f(PredOrFunc), words("method")]
|
|
else
|
|
PrefixPieces = [p_or_f(PredOrFunc)]
|
|
),
|
|
SymName = qualified(ModuleName, PredName),
|
|
user_arity_pred_form_arity(PredOrFunc,
|
|
user_arity(UserArityInt), PredFormArity),
|
|
SNA = sym_name_arity(SymName, UserArityInt),
|
|
(
|
|
ShouldModuleQualify = should_module_qualify,
|
|
SNAPiece = qual_sym_name_arity(SNA)
|
|
;
|
|
ShouldModuleQualify = should_not_module_qualify,
|
|
SNAPiece = unqual_sym_name_arity(SNA)
|
|
),
|
|
Pieces = PrefixPieces ++
|
|
maybe_color_pieces(MaybeColor, [SNAPiece] ++ SuffixPieces)
|
|
).
|
|
|
|
describe_several_pred_names(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
|
PredIds) = Pieces :-
|
|
SuffixPieces = [],
|
|
PiecesList = list.map(
|
|
describe_one_pred_name(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
|
SuffixPieces),
|
|
PredIds),
|
|
Pieces = pieces_list_to_pieces("and", PiecesList).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
describe_one_proc_name_maybe_argmodes(ModuleInfo, Lang, MaybeColor,
|
|
ShouldModuleQualify, SuffixPieces, PredProcId) = Pieces :-
|
|
PredProcId = proc(PredId, ProcId),
|
|
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
|
Pieces = describe_one_proc_name_pred_info_maybe_argmodes(PredInfo,
|
|
Lang, MaybeColor, ShouldModuleQualify, SuffixPieces, ProcId).
|
|
|
|
describe_one_proc_name_pred_info_maybe_argmodes(PredInfo, Lang, MaybeColor,
|
|
ShouldModuleQualify, SuffixPieces, ProcId) = Pieces :-
|
|
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
|
ModuleName = pred_info_module(PredInfo),
|
|
PredName = pred_info_name(PredInfo),
|
|
pred_info_get_proc_table(PredInfo, ProcTable),
|
|
map.count(ProcTable, NumProcs),
|
|
( if NumProcs > 1 then
|
|
pred_info_proc_info(PredInfo, ProcId, ProcInfo),
|
|
pred_info_get_orig_arity(PredInfo, PredFormArity),
|
|
proc_info_get_argmodes(ProcInfo, ArgModes0),
|
|
NumExtraArgs = num_extra_args(PredFormArity, ArgModes0),
|
|
% We need to strip off the extra type_info arguments inserted at the
|
|
% front by polymorphism.m - we only want the last `PredFormArity'
|
|
% of them.
|
|
list.det_drop(NumExtraArgs, ArgModes0, ArgModes),
|
|
strip_module_names_from_mode_list(strip_builtin_module_name,
|
|
set_default_func, ArgModes, StrippedArgModes),
|
|
proc_info_get_inst_varset(ProcInfo, InstVarSet),
|
|
(
|
|
PredOrFunc = pf_predicate,
|
|
ArgModesStr = arg_modes_to_string(Lang, InstVarSet,
|
|
StrippedArgModes)
|
|
;
|
|
PredOrFunc = pf_function,
|
|
pred_args_to_func_args(StrippedArgModes,
|
|
FuncArgModes, FuncRetMode),
|
|
ArgModesStr =
|
|
arg_modes_to_string(Lang, InstVarSet, FuncArgModes) ++ " = " ++
|
|
mercury_mode_to_string(Lang, InstVarSet, FuncRetMode)
|
|
),
|
|
MaybeModuleNameDotStr =
|
|
maybe_module_qualification(ModuleName, ShouldModuleQualify),
|
|
% The absence or presence of a distinguished return value argument
|
|
% tells the reader whether we are reporting the name of a predicate
|
|
% or a function; no need to specify that same info in a word as well.
|
|
string.append_list([
|
|
"`",
|
|
MaybeModuleNameDotStr,
|
|
PredName,
|
|
ArgModesStr,
|
|
"'"], Descr),
|
|
Pieces = maybe_color_pieces(MaybeColor, [words(Descr)] ++ SuffixPieces)
|
|
else
|
|
% The Pieces we now return identifies a predicate or a function,
|
|
% not a procedure per se. We *could* add a prefix such as
|
|
% "the only mode of" in front of Pieces to fix this category error, but
|
|
%
|
|
% - even for novices, such a prefix would help only the first few
|
|
% times they see such a diagnostic, after which it will become
|
|
% superfluous, and
|
|
%
|
|
% - for non-novices and ex-novices, such a prefix will be clutter
|
|
% that hurts more than it helps.
|
|
Pieces = describe_one_pred_info_name(MaybeColor, ShouldModuleQualify,
|
|
SuffixPieces, PredInfo)
|
|
).
|
|
|
|
:- func maybe_module_qualification(module_name, should_module_qualify)
|
|
= string.
|
|
|
|
maybe_module_qualification(ModuleName, ShouldModuleQualify)
|
|
= MaybeModuleNameDotStr :-
|
|
(
|
|
ShouldModuleQualify = should_module_qualify,
|
|
MaybeModuleNameDotStr = sym_name_to_string(ModuleName) ++ "."
|
|
;
|
|
ShouldModuleQualify = should_not_module_qualify,
|
|
MaybeModuleNameDotStr = ""
|
|
).
|
|
|
|
:- func arg_modes_to_string(output_lang, inst_varset, list(mer_mode)) = string.
|
|
|
|
arg_modes_to_string(Lang, InstVarSet, ArgModes0) = Str :-
|
|
(
|
|
ArgModes0 = [],
|
|
Str = ""
|
|
;
|
|
ArgModes0 = [_ | _],
|
|
ArgModes = list.map(simplify_std_from_to_mode, ArgModes0),
|
|
ArgsStr = mercury_mode_list_to_string(Lang, InstVarSet, ArgModes),
|
|
Str = "(" ++ ArgsStr ++ ")"
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
describe_qual_proc_name(ModuleInfo, PredProcId) =
|
|
describe_one_proc_name(ModuleInfo, no, should_module_qualify,
|
|
PredProcId).
|
|
|
|
describe_unqual_proc_name(ModuleInfo, PredProcId) =
|
|
describe_one_proc_name(ModuleInfo, no, should_not_module_qualify,
|
|
PredProcId).
|
|
|
|
describe_one_proc_name(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
|
PredProcId) = Pieces :-
|
|
SuffixPieces = [],
|
|
PredProcId = proc(PredId, ProcId),
|
|
PredPieces = describe_one_pred_name(ModuleInfo, MaybeColor,
|
|
ShouldModuleQualify, SuffixPieces, PredId),
|
|
proc_id_to_int(ProcId, ProcIdInt),
|
|
Pieces = PredPieces ++ [words("mode"), int_fixed(ProcIdInt)].
|
|
|
|
describe_several_proc_names(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
|
PPIds) = Pieces :-
|
|
PiecesList = list.map(
|
|
describe_one_proc_name(ModuleInfo, MaybeColor, ShouldModuleQualify),
|
|
PPIds),
|
|
Pieces = pieces_list_to_pieces("and", PiecesList).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
describe_one_call_site(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
|
PPId - Context) = Pieces :-
|
|
ProcNamePieces = describe_one_proc_name(ModuleInfo, MaybeColor,
|
|
ShouldModuleQualify, PPId),
|
|
Context = context(FileName, LineNumber),
|
|
string.int_to_string(LineNumber, LineNumberStr),
|
|
Pieces = ProcNamePieces ++
|
|
[words("at"), fixed(FileName ++ ":" ++ LineNumberStr)].
|
|
|
|
describe_several_call_sites(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
|
Sites) = Pieces :-
|
|
PiecesList = list.map(
|
|
describe_one_call_site(ModuleInfo, MaybeColor, ShouldModuleQualify),
|
|
Sites),
|
|
Pieces = pieces_list_to_pieces("and", PiecesList).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Write out the contexts of unifications.
|
|
%
|
|
|
|
unify_context_to_pieces(UnifyContext, LastContextWord, !Pieces) :-
|
|
unify_context_first_to_pieces(is_not_first, _, UnifyContext,
|
|
LastContextWord, !Pieces).
|
|
|
|
unify_context_first_to_pieces(!First, UnifyContext, LastContextWord,
|
|
!Pieces) :-
|
|
UnifyContext = unify_context(MainContext, BottomUpSubContexts),
|
|
list.reverse(BottomUpSubContexts, TopDownSubContexts),
|
|
unify_main_context_to_pieces(!First, MainContext,
|
|
LastContextWord0, !Pieces),
|
|
unify_sub_contexts_to_pieces(!First, TopDownSubContexts,
|
|
LastContextWord0, LastContextWord, !Pieces).
|
|
|
|
:- pred unify_main_context_to_pieces(is_first::in, is_first::out,
|
|
unify_main_context::in, last_context_word::out,
|
|
list(format_piece)::in, list(format_piece)::out) is det.
|
|
|
|
unify_main_context_to_pieces(!First, MainContext, LastContextWord, !Pieces) :-
|
|
(
|
|
MainContext = umc_explicit,
|
|
LastContextWord = lcw_none
|
|
;
|
|
MainContext = umc_head(ArgNum),
|
|
LastContextWord = lcw_argument,
|
|
ArgNumStr = int_to_string(ArgNum),
|
|
!:Pieces = !.Pieces ++ start_in_message_to_pieces(!.First) ++
|
|
[words("argument"), fixed(ArgNumStr), words("of clause head:"),
|
|
nl],
|
|
!:First = is_not_first
|
|
;
|
|
MainContext = umc_head_result,
|
|
LastContextWord = lcw_result,
|
|
!:Pieces = !.Pieces ++ start_in_message_to_pieces(!.First) ++
|
|
[words("function result term of clause head:"), nl],
|
|
!:First = is_not_first
|
|
;
|
|
MainContext = umc_call(CallId, ArgNum),
|
|
LastContextWord = lcw_call,
|
|
% The markers argument below is used only for type class method
|
|
% implementations defined using the named syntax rather than
|
|
% the clause syntax, and the bodies of such procedures should
|
|
% only contain a single call, so we shouldn't get unifications
|
|
% nested inside calls. Hence we can safely initialize the
|
|
% markers to empty here. (Anyway the worst possible consequence
|
|
% is slightly sub-optimal text for an error message.)
|
|
init_markers(Markers),
|
|
ArgIdPieces = call_arg_id_to_pieces(print_ho_var_name, CallId,
|
|
ArgNum, Markers),
|
|
!:Pieces = !.Pieces ++ start_in_message_to_pieces(!.First) ++
|
|
ArgIdPieces ++ [suffix(":"), nl],
|
|
!:First = is_not_first
|
|
;
|
|
MainContext = umc_implicit(Source),
|
|
LastContextWord = lcw_none,
|
|
string.format("implicit %s unification:", [s(Source)], Msg),
|
|
!:Pieces = !.Pieces ++ start_in_message_to_pieces(!.First) ++
|
|
[words(Msg), nl],
|
|
!:First = is_not_first
|
|
).
|
|
|
|
:- pred unify_sub_contexts_to_pieces(is_first::in, is_first::out,
|
|
list(unify_sub_context)::in, last_context_word::in, last_context_word::out,
|
|
list(format_piece)::in, list(format_piece)::out) is det.
|
|
|
|
unify_sub_contexts_to_pieces(!First, [], !LastContextWord, !Pieces).
|
|
unify_sub_contexts_to_pieces(!First, [SubContext | SubContexts],
|
|
_, !:LastContextWord, !Pieces) :-
|
|
( if
|
|
contexts_describe_list_element([SubContext | SubContexts],
|
|
0, ElementNum, AfterContexts)
|
|
then
|
|
HeadPieces = element_to_pieces(ElementNum),
|
|
!:LastContextWord = lcw_element,
|
|
NextContexts = AfterContexts
|
|
else
|
|
HeadPieces = argument_to_pieces(SubContext),
|
|
!:LastContextWord = lcw_argument,
|
|
NextContexts = SubContexts
|
|
),
|
|
!:Pieces = !.Pieces ++ start_in_message_to_pieces(!.First) ++
|
|
HeadPieces ++ [suffix(":"), nl],
|
|
!:First = is_not_first,
|
|
unify_sub_contexts_to_pieces(!First, NextContexts,
|
|
!LastContextWord, !Pieces).
|
|
|
|
:- pred contexts_describe_list_element(list(unify_sub_context)::in,
|
|
int::in, int::out, list(unify_sub_context)::out) is semidet.
|
|
|
|
contexts_describe_list_element([SubContext | SubContexts],
|
|
NumElementsBefore, ElementNum, AfterContexts) :-
|
|
SubContext = unify_sub_context(ConsId, ArgNum),
|
|
cons_id_may_be_list_cons(ConsId),
|
|
(
|
|
ArgNum = 1,
|
|
% If there were zero elements before this element,
|
|
% then this is element #1.
|
|
ElementNum = NumElementsBefore + 1,
|
|
AfterContexts = SubContexts
|
|
;
|
|
ArgNum = 2,
|
|
contexts_describe_list_element(SubContexts,
|
|
NumElementsBefore + 1, ElementNum, AfterContexts)
|
|
).
|
|
|
|
cons_id_may_be_list_cons(ConsId) :-
|
|
ConsId = du_data_ctor(DuCtor),
|
|
DuCtor = du_ctor(DuCtorSymName, 2, _TypeCtor),
|
|
% We ignore _TypeCtor since it may not have been set yet.
|
|
(
|
|
DuCtorSymName = unqualified("[|]")
|
|
;
|
|
DuCtorSymName = qualified(ModuleSymName, "[|]"),
|
|
is_std_lib_module_name(ModuleSymName, "list")
|
|
).
|
|
|
|
argument_to_pieces(SubContext) = Pieces :-
|
|
SubContext = unify_sub_context(ConsId, ArgNum),
|
|
ArgNumStr = int_to_string(ArgNum),
|
|
% XXX Using cons_id_and_arity_to_string here results in the
|
|
% quotes being in the wrong place.
|
|
ConsIdStr = cons_id_and_arity_to_string(ConsId),
|
|
Pieces = [words("argument"), fixed(ArgNumStr),
|
|
words("of functor"), quote(ConsIdStr)].
|
|
|
|
:- func element_to_pieces(int) = list(format_piece).
|
|
|
|
element_to_pieces(ElementNum) = Pieces :-
|
|
ElementNumStr = int_to_string(ElementNum),
|
|
Pieces = [words("list element"), prefix("#"), fixed(ElementNumStr)].
|
|
|
|
:- func start_in_message_to_pieces(is_first) = list(format_piece).
|
|
|
|
start_in_message_to_pieces(First) = Pieces :-
|
|
(
|
|
First = is_first,
|
|
% It is possible for First to be yes and !.Pieces to be nonempty,
|
|
% since !.Pieces may contain stuff from before the unify context.
|
|
Pieces = [words("In")]
|
|
;
|
|
First = is_not_first,
|
|
Pieces = [words("in")]
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% Write out ids of calls.
|
|
%
|
|
|
|
call_id_to_pieces(_PrintHoVarName, plain_call_id(PFSNA)) =
|
|
[qual_pf_sym_name_pred_form_arity(PFSNA)].
|
|
call_id_to_pieces(PrintHoVarName, generic_call_id(VarNameSrc, GenericCall)) =
|
|
generic_call_to_pieces(PrintHoVarName, VarNameSrc, GenericCall).
|
|
|
|
generic_call_to_pieces(PrintHoVarName, VarNameSrc, GenericCall) = Pieces :-
|
|
(
|
|
GenericCall = higher_order(Var, Purity, PredOrFunc, _, Syntax),
|
|
(
|
|
Syntax = hos_var,
|
|
(
|
|
PrintHoVarName = do_not_print_ho_var_name,
|
|
Pieces = [words("the higher order"), p_or_f(PredOrFunc),
|
|
words("call")]
|
|
;
|
|
PrintHoVarName = print_ho_var_name,
|
|
lookup_var_name_in_source(VarNameSrc, Var, VarName),
|
|
Pieces = [words("the higher order call to the"),
|
|
p_or_f(PredOrFunc), words("variable"), quote(VarName)]
|
|
)
|
|
;
|
|
Syntax = hos_call_or_apply,
|
|
(
|
|
PredOrFunc = pf_predicate,
|
|
Pieces = [words("the call to the"), quote("call"),
|
|
words("builtin predicate")]
|
|
;
|
|
PredOrFunc = pf_function,
|
|
ApplyFuncName = apply_func_name(Purity),
|
|
Pieces = [words("the call to the"), quote(ApplyFuncName),
|
|
words("builtin function")]
|
|
)
|
|
)
|
|
;
|
|
GenericCall = class_method(_TCI, _MethodNum, _ClassId, MethodId),
|
|
Pieces = [qual_pf_sym_name_pred_form_arity(MethodId)]
|
|
;
|
|
GenericCall = event_call(EventName),
|
|
Pieces = [words("event"), words(EventName)]
|
|
;
|
|
GenericCall = cast(CastType),
|
|
Pieces = [words(cast_type_to_string(CastType))]
|
|
).
|
|
|
|
generic_callee_to_pieces(PrintHoVarName, VarNameSrc, GenericCall) = Pieces :-
|
|
(
|
|
GenericCall = higher_order(Var, Purity, PredOrFunc, _, Syntax),
|
|
(
|
|
Syntax = hos_var,
|
|
(
|
|
PrintHoVarName = do_not_print_ho_var_name,
|
|
Pieces = [words("the higher order"), p_or_f(PredOrFunc),
|
|
words("variable")]
|
|
;
|
|
PrintHoVarName = print_ho_var_name,
|
|
lookup_var_name_in_source(VarNameSrc, Var, VarName),
|
|
Pieces = [words("the higher order"), p_or_f(PredOrFunc),
|
|
words("variable"), quote(VarName)]
|
|
)
|
|
;
|
|
Syntax = hos_call_or_apply,
|
|
(
|
|
PredOrFunc = pf_predicate,
|
|
Pieces = [words("the predicate argument of the"),
|
|
quote("call"), words("builtin predicate")]
|
|
;
|
|
PredOrFunc = pf_function,
|
|
Pieces = [words("the function argument of the"),
|
|
quote(apply_func_name(Purity)), words("builtin function")]
|
|
)
|
|
)
|
|
;
|
|
GenericCall = class_method(_TCI, _MethodNum, _ClassId, MethodId),
|
|
Pieces = [qual_pf_sym_name_pred_form_arity(MethodId)]
|
|
;
|
|
GenericCall = event_call(EventName),
|
|
Pieces = [words("event"), words(EventName)]
|
|
;
|
|
GenericCall = cast(CastType),
|
|
Pieces = [words(cast_type_to_string(CastType))]
|
|
).
|
|
|
|
:- func apply_func_name(purity) = string.
|
|
|
|
apply_func_name(purity_pure) = "apply".
|
|
apply_func_name(purity_semipure) = "semipure_apply".
|
|
apply_func_name(purity_impure) = "impure_apply".
|
|
|
|
call_arg_id_to_pieces(PrintHoVarName, CallId, ArgNum, PredMarkers) = Pieces :-
|
|
( if ArgNum =< 0 then
|
|
% Argument numbers that are less than or equal to zero
|
|
% are used for the type_info and typeclass_info arguments
|
|
% that are introduced by polymorphism.m.
|
|
% I think argument zero might also be used in some other cases
|
|
% when we just don't have any information about which argument it is.
|
|
% For both of these, we just say "in call to"
|
|
% rather than "in argument N of call to".
|
|
ArgNumPieces = []
|
|
else
|
|
ArgNumPieces = arg_number_to_pieces(CallId, ArgNum) ++ [words("of")]
|
|
),
|
|
( if
|
|
(
|
|
% The text printed for generic calls other than
|
|
% `class_method' does not need the "call to"
|
|
% prefix ("in call to higher-order call" is redundant,
|
|
% it's much better to just say "in higher-order call").
|
|
CallId = generic_call_id(_, GenericCallId),
|
|
not GenericCallId = class_method(_, _, _, _)
|
|
;
|
|
% For calls from type class instance implementations
|
|
% that were defined using the named syntax rather
|
|
% than the clause syntax, we also omit the "call to",
|
|
% since in that case there was no explicit call in
|
|
% the user's source code.
|
|
marker_is_present(PredMarkers, marker_named_class_instance_method)
|
|
)
|
|
then
|
|
CallToPieces = []
|
|
else
|
|
CallToPieces = [words("call to")]
|
|
),
|
|
CallIdPieces = call_id_to_pieces(PrintHoVarName, CallId),
|
|
Pieces = ArgNumPieces ++ CallToPieces ++ CallIdPieces.
|
|
|
|
:- func arg_number_to_pieces(call_id, int) = list(format_piece).
|
|
|
|
arg_number_to_pieces(CallId, ArgNum) = Pieces :-
|
|
(
|
|
CallId = plain_call_id(PFSymNameArity),
|
|
PFSymNameArity = pf_sym_name_arity(PredOrFunc, _, PredFormArity),
|
|
PredFormArity = pred_form_arity(Arity),
|
|
( if
|
|
PredOrFunc = pf_function,
|
|
Arity = ArgNum
|
|
then
|
|
Pieces = [words("the return value")]
|
|
else
|
|
Pieces = [words("argument"), int_fixed(ArgNum)]
|
|
)
|
|
;
|
|
CallId = generic_call_id(_VarNameSrc, GenericCall),
|
|
(
|
|
GenericCall = higher_order(_Var, _Purity, PredOrFunc,
|
|
PredFormArity, Syntax),
|
|
PredFormArity = pred_form_arity(PredFormArityInt),
|
|
( if
|
|
PredOrFunc = pf_function,
|
|
ArgNum = PredFormArityInt
|
|
then
|
|
Pieces = [words("the return value")]
|
|
else
|
|
(
|
|
Syntax = hos_var,
|
|
( if ArgNum = 1 then
|
|
Pieces = [words("the"), p_or_f(PredOrFunc),
|
|
words("term")]
|
|
else
|
|
Pieces = [words("argument"), int_fixed(ArgNum - 1)]
|
|
)
|
|
;
|
|
Syntax = hos_call_or_apply,
|
|
Pieces = [words("argument"), int_fixed(ArgNum)]
|
|
)
|
|
)
|
|
;
|
|
( GenericCall = class_method(_, _, _, _)
|
|
; GenericCall = event_call(_)
|
|
; GenericCall = cast(unsafe_type_cast)
|
|
; GenericCall = cast(unsafe_type_inst_cast)
|
|
; GenericCall = cast(equiv_type_cast)
|
|
; GenericCall = cast(exists_cast)
|
|
),
|
|
Pieces = [words("argument"), int_fixed(ArgNum)]
|
|
;
|
|
GenericCall = cast(subtype_coerce),
|
|
( if ArgNum = 2 then
|
|
Pieces = [words("the result")]
|
|
else
|
|
Pieces = [words("the argument")]
|
|
)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
find_pred_arities(PredTable, PredIds, PredFormArities) :-
|
|
gather_pred_form_arities(PredTable, PredIds, [], PredFormArities0),
|
|
list.sort_and_remove_dups(PredFormArities0, PredFormArities).
|
|
|
|
find_user_arities(PredTable, PredIds, UserArities) :-
|
|
gather_user_arities(PredTable, PredIds, [], UserArities0),
|
|
list.sort_and_remove_dups(UserArities0, UserArities).
|
|
|
|
find_pred_arities_other_than(PredTable, PredIds, Arity, OtherArities) :-
|
|
find_pred_arities(PredTable, PredIds, AllArities),
|
|
list.delete_all(AllArities, Arity, OtherArities).
|
|
|
|
find_user_arities_other_than(PredTable, PredIds, Arity, OtherArities) :-
|
|
find_user_arities(PredTable, PredIds, AllArities),
|
|
list.delete_all(AllArities, Arity, OtherArities).
|
|
|
|
%---------------------%
|
|
|
|
:- pred gather_pred_form_arities(pred_id_table::in, list(pred_id)::in,
|
|
list(pred_form_arity)::in, list(pred_form_arity)::out) is det.
|
|
|
|
gather_pred_form_arities(_, [], !PredFormArities).
|
|
gather_pred_form_arities(PredTable, [PredId | PredIds], !PredFormArities) :-
|
|
map.lookup(PredTable, PredId, PredInfo),
|
|
pred_info_get_orig_arity(PredInfo, PredFormArity),
|
|
!:PredFormArities = [PredFormArity | !.PredFormArities],
|
|
gather_pred_form_arities(PredTable, PredIds, !PredFormArities).
|
|
|
|
:- pred gather_user_arities(pred_id_table::in, list(pred_id)::in,
|
|
list(user_arity)::in, list(user_arity)::out) is det.
|
|
|
|
gather_user_arities(_, [], !UserArities).
|
|
gather_user_arities(PredTable, [PredId | PredIds], !UserArities) :-
|
|
map.lookup(PredTable, PredId, PredInfo),
|
|
UserArity = pred_info_user_arity(PredInfo),
|
|
!:UserArities = [UserArity | !.UserArities],
|
|
gather_user_arities(PredTable, PredIds, !UserArities).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
project_user_arity_int(user_arity(A)) = A.
|
|
|
|
project_pred_form_arity_int(pred_form_arity(A)) = A.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module hlds.hlds_error_util.
|
|
%---------------------------------------------------------------------------%
|