mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
Generate mode-specific unused args warnings if needed.
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.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
% vim: ft=mercury ts=4 sw=4 et
|
||||
%---------------------------------------------------------------------------%
|
||||
% Copyright (C) 1997-2007, 2009-2012 The University of Melbourne.
|
||||
% Copyright (C) 2014-2017, 2019-2025 The Mercury team.
|
||||
% 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.
|
||||
%---------------------------------------------------------------------------%
|
||||
@@ -124,6 +124,17 @@
|
||||
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:
|
||||
@@ -424,13 +435,20 @@ describe_several_pred_names(ModuleInfo, MaybeColor, ShouldModuleQualify,
|
||||
|
||||
describe_one_proc_name_maybe_argmodes(ModuleInfo, Lang, MaybeColor,
|
||||
ShouldModuleQualify, SuffixPieces, PredProcId) = Pieces :-
|
||||
module_info_pred_proc_info(ModuleInfo, PredProcId, PredInfo, ProcInfo),
|
||||
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),
|
||||
|
||||
@@ -122,10 +122,9 @@ unused_args_process_module(GatherPragmas, RecordAnalysis,
|
||||
% maybe_write_string(VeryVerbose, "% Finished analysis.\n", !IO),
|
||||
|
||||
map.init(ProcToUnusedArgsMap0),
|
||||
build_proc_to_unused_args_map(!.ModuleInfo, GlobalVarUsageMap, FixpointPredProcIds,
|
||||
ProcToUnusedArgsMap0, ProcToUnusedArgsMap),
|
||||
build_proc_to_unused_args_map(!.ModuleInfo, GlobalVarUsageMap,
|
||||
FixpointPredProcIds, ProcToUnusedArgsMap0, ProcToUnusedArgsMap),
|
||||
|
||||
map.keys(ProcToUnusedArgsMap, PredProcIdsToFix),
|
||||
globals.lookup_bool_option(Globals, warn_unused_args, DoWarnBool),
|
||||
( DoWarnBool = no, DoWarn = do_not_warn_unused_args
|
||||
; DoWarnBool = yes, DoWarn = do_warn_unused_args
|
||||
@@ -135,10 +134,8 @@ unused_args_process_module(GatherPragmas, RecordAnalysis,
|
||||
; GatherPragmas = do_gather_pragma_unused_args
|
||||
)
|
||||
then
|
||||
set.init(WarnedPredIds0),
|
||||
gather_warnings_and_pragmas(!.ModuleInfo, ProcToUnusedArgsMap,
|
||||
DoWarn, GatherPragmas, PredProcIdsToFix, WarnedPredIds0,
|
||||
[], Specs, set.init, PragmaUnusedArgInfos)
|
||||
DoWarn, GatherPragmas, Specs, PragmaUnusedArgInfos)
|
||||
else
|
||||
Specs = [],
|
||||
set.init(PragmaUnusedArgInfos)
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
|
||||
:- import_module hlds.
|
||||
:- import_module hlds.hlds_module.
|
||||
:- import_module hlds.hlds_pred.
|
||||
:- import_module parse_tree.
|
||||
:- import_module parse_tree.error_spec.
|
||||
:- import_module parse_tree.prog_item.
|
||||
@@ -51,17 +50,17 @@
|
||||
:- pred gather_warnings_and_pragmas(module_info::in,
|
||||
proc_to_unused_args_map::in,
|
||||
maybe_warn_unused_args::in, maybe_gather_pragma_unused_args::in,
|
||||
list(pred_proc_id)::in, set(pred_id)::in,
|
||||
list(error_spec)::in, list(error_spec)::out,
|
||||
set(gen_pragma_unused_args_info)::in,
|
||||
set(gen_pragma_unused_args_info)::out) is det.
|
||||
list(error_spec)::out, set(gen_pragma_unused_args_info)::out) is det.
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- implementation.
|
||||
|
||||
:- import_module hlds.hlds_error_util.
|
||||
:- import_module hlds.hlds_markers.
|
||||
:- import_module hlds.hlds_pred.
|
||||
:- import_module hlds.mode_test.
|
||||
:- import_module hlds.pred_name.
|
||||
:- import_module hlds.status.
|
||||
:- import_module libs.
|
||||
@@ -69,50 +68,58 @@
|
||||
:- import_module mdbcomp.
|
||||
:- import_module mdbcomp.prim_data.
|
||||
:- import_module mdbcomp.sym_name.
|
||||
:- import_module parse_tree.parse_tree_out_info.
|
||||
:- import_module parse_tree.prog_data.
|
||||
:- import_module parse_tree.prog_util.
|
||||
|
||||
:- import_module bool.
|
||||
:- import_module int.
|
||||
:- import_module map.
|
||||
:- import_module maybe.
|
||||
:- import_module one_or_more.
|
||||
:- import_module pair.
|
||||
:- import_module require.
|
||||
:- import_module term.
|
||||
:- import_module term_context.
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
gather_warnings_and_pragmas(_, _, _, _, [], _, !Specs, !PragmaUnusedArgInfos).
|
||||
gather_warnings_and_pragmas(ModuleInfo, ProcToUnusedArgsMap,
|
||||
DoWarn, DoPragma, [PredProcId | PredProcIds], !.WarnedPredIds,
|
||||
!Specs, !PragmaUnusedArgInfos) :-
|
||||
( if map.search(ProcToUnusedArgsMap, PredProcId, UnusedArgs) then
|
||||
PredProcId = proc(PredId, ProcId) ,
|
||||
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
||||
( if may_gather_warning_pragma_for_pred(PredInfo) then
|
||||
(
|
||||
DoWarn = do_not_warn_unused_args
|
||||
;
|
||||
DoWarn = do_warn_unused_args,
|
||||
maybe_gather_warning(ModuleInfo, PredInfo, PredId, ProcId,
|
||||
UnusedArgs, !WarnedPredIds, !Specs)
|
||||
),
|
||||
(
|
||||
DoPragma = do_not_gather_pragma_unused_args
|
||||
;
|
||||
DoPragma = do_gather_pragma_unused_args,
|
||||
maybe_gather_unused_args_pragma(PredInfo, ProcId, UnusedArgs,
|
||||
!PragmaUnusedArgInfos)
|
||||
)
|
||||
else
|
||||
true
|
||||
DoWarn, DoPragma, Specs, PragmaUnusedArgInfos) :-
|
||||
map.foldl2(gather_warnings_and_pragmas_ppid(ModuleInfo, DoWarn, DoPragma),
|
||||
ProcToUnusedArgsMap,
|
||||
map.init, WarnUnusedPredArgsMap, set.init, PragmaUnusedArgInfos),
|
||||
map.foldl(warn_unused_args_in_pred, WarnUnusedPredArgsMap, [], Specs).
|
||||
|
||||
:- pred gather_warnings_and_pragmas_ppid(module_info::in,
|
||||
maybe_warn_unused_args::in, maybe_gather_pragma_unused_args::in,
|
||||
pred_proc_id::in, list(int)::in,
|
||||
warn_unused_pred_args_map::in, warn_unused_pred_args_map::out,
|
||||
set(gen_pragma_unused_args_info)::in,
|
||||
set(gen_pragma_unused_args_info)::out) is det.
|
||||
|
||||
gather_warnings_and_pragmas_ppid(ModuleInfo, DoWarn, DoPragma,
|
||||
PredProcId, UnusedArgs,
|
||||
!WarnUnusedPredArgsMap, !PragmaUnusedArgInfos) :-
|
||||
PredProcId = proc(PredId, ProcId) ,
|
||||
module_info_pred_info(ModuleInfo, PredId, PredInfo),
|
||||
( if may_gather_warning_pragma_for_pred(PredInfo) then
|
||||
(
|
||||
DoWarn = do_not_warn_unused_args
|
||||
;
|
||||
DoWarn = do_warn_unused_args,
|
||||
maybe_add_proc_to_unused_args_map(ModuleInfo, PredInfo, PredId,
|
||||
ProcId, UnusedArgs, !WarnUnusedPredArgsMap)
|
||||
),
|
||||
(
|
||||
DoPragma = do_not_gather_pragma_unused_args
|
||||
;
|
||||
DoPragma = do_gather_pragma_unused_args,
|
||||
maybe_gather_unused_args_pragma(PredInfo, ProcId, UnusedArgs,
|
||||
!PragmaUnusedArgInfos)
|
||||
)
|
||||
else
|
||||
true
|
||||
),
|
||||
gather_warnings_and_pragmas(ModuleInfo, ProcToUnusedArgsMap,
|
||||
DoWarn, DoPragma, PredProcIds, !.WarnedPredIds,
|
||||
!Specs, !PragmaUnusedArgInfos).
|
||||
).
|
||||
|
||||
:- pred may_gather_warning_pragma_for_pred(pred_info::in) is semidet.
|
||||
|
||||
@@ -260,32 +267,64 @@ may_gather_warning_pragma_for_pred(PredInfo) :-
|
||||
)
|
||||
).
|
||||
|
||||
:- pred maybe_gather_warning(module_info::in, pred_info::in,
|
||||
pred_id::in, proc_id::in, list(int)::in,
|
||||
set(pred_id)::in, set(pred_id)::out,
|
||||
list(error_spec)::in, list(error_spec)::out) is det.
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
maybe_gather_warning(ModuleInfo, PredInfo, PredId, ProcId, UnusedArgs0,
|
||||
!WarnedPredIds, !Specs) :-
|
||||
( if set.member(PredId, !.WarnedPredIds) then
|
||||
true
|
||||
else
|
||||
set.insert(PredId, !WarnedPredIds),
|
||||
pred_info_get_proc_table(PredInfo, ProcTable),
|
||||
map.lookup(ProcTable, ProcId, Proc),
|
||||
pred_info_get_orig_arity(PredInfo, PredFormArity),
|
||||
proc_info_get_headvars(Proc, HeadVars),
|
||||
NumExtraArgs = num_extra_args(PredFormArity, HeadVars),
|
||||
% Strip off the extra type_info/typeclass_info arguments
|
||||
% inserted at the front by polymorphism.m.
|
||||
drop_poly_inserted_args(NumExtraArgs, UnusedArgs0, UnusedArgs),
|
||||
(
|
||||
UnusedArgs = [_ | _],
|
||||
Spec = report_unused_args(ModuleInfo, PredInfo, UnusedArgs),
|
||||
!:Specs = [Spec | !.Specs]
|
||||
;
|
||||
UnusedArgs = []
|
||||
:- type warn_unused_pred_args_map == map(pred_id, warn_unused_pred_args).
|
||||
|
||||
:- type warn_unused_pred_args
|
||||
---> warn_unused_pred_args(
|
||||
pred_info,
|
||||
one_or_more(pair(proc_id, unused_proc_args))
|
||||
).
|
||||
|
||||
:- type unused_proc_args == one_or_more(unused_proc_arg).
|
||||
:- type unused_proc_arg
|
||||
---> unused_proc_arg(
|
||||
% The argument number.
|
||||
int,
|
||||
|
||||
maybe_marked_unused
|
||||
).
|
||||
|
||||
:- type maybe_marked_unused
|
||||
---> not_marked_unused
|
||||
; marked_unused.
|
||||
|
||||
:- pred maybe_add_proc_to_unused_args_map(module_info::in, pred_info::in,
|
||||
pred_id::in, proc_id::in, list(int)::in,
|
||||
warn_unused_pred_args_map::in, warn_unused_pred_args_map::out) is det.
|
||||
|
||||
maybe_add_proc_to_unused_args_map(ModuleInfo, PredInfo, PredId, ProcId,
|
||||
UnusedArgs0, !WarnUnusedPredArgsMap) :-
|
||||
pred_info_get_orig_arity(PredInfo, PredFormArity),
|
||||
pred_info_get_arg_types(PredInfo, ArgTypes),
|
||||
NumExtraArgs = num_extra_args(PredFormArity, ArgTypes),
|
||||
% Strip off the extra type_info/typeclass_info arguments
|
||||
% inserted at the front by polymorphism.m.
|
||||
drop_poly_inserted_args(NumExtraArgs, UnusedArgs0, UnusedArgs),
|
||||
(
|
||||
UnusedArgs = [_ | _],
|
||||
pred_info_proc_info(PredInfo, ProcId, ProcInfo),
|
||||
proc_info_get_argmodes(ProcInfo, ArgModes0),
|
||||
list.det_drop(NumExtraArgs, ArgModes0, ArgModes),
|
||||
record_which_unused_args_are_marked(ModuleInfo, ArgModes,
|
||||
UnusedArgs, UnusedProcArgs),
|
||||
% If UnusedArgs is not empty, then UnusedProcArgs cannot be empty.
|
||||
det_list_to_one_or_more(UnusedProcArgs, OoMUnusedProcArgs),
|
||||
( if
|
||||
map.search(!.WarnUnusedPredArgsMap, PredId, WarnUnusedPredArgs0)
|
||||
then
|
||||
WarnUnusedPredArgs0 = warn_unused_pred_args(_PredInfo, ProcAL0),
|
||||
one_or_more.cons(ProcId - OoMUnusedProcArgs, ProcAL0, ProcAL),
|
||||
WarnUnusedPredArgs = warn_unused_pred_args(PredInfo, ProcAL),
|
||||
map.det_update(PredId, WarnUnusedPredArgs, !WarnUnusedPredArgsMap)
|
||||
else
|
||||
ProcAL = one_or_more(ProcId - OoMUnusedProcArgs, []),
|
||||
WarnUnusedPredArgs = warn_unused_pred_args(PredInfo, ProcAL),
|
||||
map.det_insert(PredId, WarnUnusedPredArgs, !WarnUnusedPredArgsMap)
|
||||
)
|
||||
;
|
||||
UnusedArgs = []
|
||||
).
|
||||
|
||||
% Adjust the argument numbers from how they look in an argument list
|
||||
@@ -307,42 +346,175 @@ drop_poly_inserted_args(NumInserted, [HeadArgWith | TailArgsWith],
|
||||
ArgsWithout = [HeadArgWithout | TailArgsWithout]
|
||||
).
|
||||
|
||||
% Warn about unused arguments in a predicate. We consider an argument
|
||||
% unused *only* if it is unused in *every* mode of the predicate.
|
||||
% We also never warn about arguments inserted by the polymorphism pass.
|
||||
%
|
||||
% The latter test is done by maybe_gather_warning with help from
|
||||
% drop_poly_inserted_args.
|
||||
%
|
||||
% XXX I (zs) would like to know where the first test is done,
|
||||
% since it is *not* done here. My suspicion is that it is not done at all.
|
||||
%
|
||||
:- func report_unused_args(module_info, pred_info, list(int)) = error_spec.
|
||||
:- pred record_which_unused_args_are_marked(module_info::in,
|
||||
list(mer_mode)::in, list(int)::in, list(unused_proc_arg)::out) is det.
|
||||
|
||||
report_unused_args(_ModuleInfo, PredInfo, UnusedArgs) = Spec :-
|
||||
list.length(UnusedArgs, NumArgs),
|
||||
pred_info_get_context(PredInfo, Context),
|
||||
PredOrFunc = pred_info_is_pred_or_func(PredInfo),
|
||||
ModuleName = pred_info_module(PredInfo),
|
||||
PredName = pred_info_name(PredInfo),
|
||||
pred_info_get_orig_arity(PredInfo, PredFormArity),
|
||||
user_arity_pred_form_arity(PredOrFunc,
|
||||
user_arity(UserArityInt), PredFormArity),
|
||||
SNA = sym_name_arity(qualified(ModuleName, PredName), UserArityInt),
|
||||
Pieces1 = [words("In"), fixed(pred_or_func_to_full_str(PredOrFunc)),
|
||||
qual_sym_name_arity(SNA), suffix(":"), nl, words("warning:")],
|
||||
UnusedArgNs = list.map(func(N) = int_fixed(N), UnusedArgs),
|
||||
UnusedArgPieces = piece_list_to_color_pieces(color_subject, "and", [],
|
||||
UnusedArgNs),
|
||||
( if NumArgs = 1 then
|
||||
Pieces2 = [words("argument")] ++ UnusedArgPieces ++
|
||||
[words("is")] ++ color_as_incorrect([words("unused.")]) ++ [nl]
|
||||
record_which_unused_args_are_marked(_, _, [], []).
|
||||
record_which_unused_args_are_marked(ModuleInfo, ArgModes,
|
||||
[ArgNum | ArgNums], [ProcArg | ProcArgs]) :-
|
||||
list.det_index1(ArgModes, ArgNum, ArgMode),
|
||||
( if mode_is_unused(ModuleInfo, ArgMode) then
|
||||
MaybeMarked = marked_unused
|
||||
else
|
||||
Pieces2 = [words("arguments")] ++ UnusedArgPieces ++
|
||||
[words("are")] ++ color_as_incorrect([words("unused.")]) ++ [nl]
|
||||
MaybeMarked = not_marked_unused
|
||||
),
|
||||
Spec = spec($pred, severity_warning(warn_requested_by_option),
|
||||
phase_code_gen, Context, Pieces1 ++ Pieces2).
|
||||
ProcArg = unused_proc_arg(ArgNum, MaybeMarked),
|
||||
record_which_unused_args_are_marked(ModuleInfo, ArgModes,
|
||||
ArgNums, ProcArgs).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- pred warn_unused_args_in_pred(pred_id::in, warn_unused_pred_args::in,
|
||||
list(error_spec)::in, list(error_spec)::out) is det.
|
||||
|
||||
warn_unused_args_in_pred(_PredId, WarnUnusedPredArgs, !Specs) :-
|
||||
WarnUnusedPredArgs = warn_unused_pred_args(PredInfo, ProcUnusedArgsAL0),
|
||||
one_or_more.sort(ProcUnusedArgsAL0, ProcUnusedArgsAL),
|
||||
pred_info_get_proc_table(PredInfo, ProcTable),
|
||||
one_or_more.foldl2(do_all_procs_have_same_unused_args, ProcUnusedArgsAL,
|
||||
map.init, UnusedArgsToProcMap, ProcTable, UnmentionedProcTable),
|
||||
map.to_assoc_list(UnusedArgsToProcMap, UnusedArgsToProcAL),
|
||||
% We can generate a single warning that applies to the whole predicate
|
||||
% only if
|
||||
% - all procedures have some unused arguments, and
|
||||
% - they all have the *same set* of unused arguments, *and*
|
||||
% they agree on which unused args are marked as such.
|
||||
( if
|
||||
map.is_empty(UnmentionedProcTable),
|
||||
UnusedArgsToProcAL = [UnusedProcArgs - _OoMProcIds]
|
||||
then
|
||||
report_pred_general_unused_args(PredInfo, UnusedProcArgs, !Specs)
|
||||
else
|
||||
% Otherwise, we generate procedure-specific warnings.
|
||||
%
|
||||
% We *could* generate a single warning for whole sets of procedures
|
||||
% that share the same pattern of unused arguments, but
|
||||
%
|
||||
% - having different procedures having different sets of unused args
|
||||
% is extremely rare, and
|
||||
%
|
||||
% - if that *does* happen, then getting the mode-specific reports
|
||||
% in mode number order probably does more to improve readability
|
||||
% than shortening the output would do, especially if the procedures
|
||||
% in a set sharing the same set of unused args are not adjacent.
|
||||
one_or_more.foldl(report_proc_specific_unused_args(PredInfo),
|
||||
ProcUnusedArgsAL, !Specs)
|
||||
).
|
||||
|
||||
:- pred do_all_procs_have_same_unused_args(
|
||||
pair(proc_id, unused_proc_args)::in,
|
||||
map(unused_proc_args, one_or_more(proc_id))::in,
|
||||
map(unused_proc_args, one_or_more(proc_id))::out,
|
||||
map(proc_id, proc_info)::in, map(proc_id, proc_info)::out) is det.
|
||||
|
||||
do_all_procs_have_same_unused_args(ProcId - ProcUnusedArgs,
|
||||
!UnusedArgsToProcmap, !UnmentionedProcTable) :-
|
||||
( if map.search(!.UnusedArgsToProcmap, ProcUnusedArgs, OoMProcIds0) then
|
||||
one_or_more.cons(ProcId, OoMProcIds0, OoMProcIds),
|
||||
map.det_update(ProcUnusedArgs, OoMProcIds, !UnusedArgsToProcmap)
|
||||
else
|
||||
OoMProcIds = one_or_more(ProcId, []),
|
||||
map.det_insert(ProcUnusedArgs, OoMProcIds, !UnusedArgsToProcmap)
|
||||
),
|
||||
% By construction, each ProcId may appear in the input list
|
||||
% (or actually one_or_more) only once.
|
||||
map.det_remove(ProcId, _, !UnmentionedProcTable).
|
||||
|
||||
% Warn about unused arguments in a predicate.
|
||||
%
|
||||
% We never warn about arguments inserted by the polymorphism pass.
|
||||
% This filtering out is done by the call to drop_poly_inserted_args
|
||||
% in maybe_add_proc_to_unused_args_map above.
|
||||
%
|
||||
:- pred report_pred_general_unused_args(pred_info::in, unused_proc_args::in,
|
||||
list(error_spec)::in, list(error_spec)::out) is det.
|
||||
|
||||
report_pred_general_unused_args(PredInfo, ProcUnusedArgs, !Specs) :-
|
||||
NameColonNlPieces = describe_one_pred_info_name(yes(color_subject),
|
||||
should_not_module_qualify, [suffix(":"), nl], PredInfo),
|
||||
pred_info_get_context(PredInfo, Context),
|
||||
report_unused_args(NameColonNlPieces, Context, ProcUnusedArgs, !Specs).
|
||||
|
||||
:- pred report_proc_specific_unused_args(pred_info::in,
|
||||
pair(proc_id, unused_proc_args)::in,
|
||||
list(error_spec)::in, list(error_spec)::out) is det.
|
||||
|
||||
report_proc_specific_unused_args(PredInfo, ProcId - ProcUnusedArgs, !Specs) :-
|
||||
NameColonNlPieces = describe_one_proc_name_pred_info_maybe_argmodes(
|
||||
PredInfo, output_mercury, yes(color_subject),
|
||||
should_not_module_qualify, [suffix(":"), nl], ProcId),
|
||||
pred_info_proc_info(PredInfo, ProcId, ProcInfo),
|
||||
proc_info_get_context(ProcInfo, Context),
|
||||
report_unused_args(NameColonNlPieces, Context, ProcUnusedArgs, !Specs).
|
||||
|
||||
:- pred report_unused_args(list(format_piece)::in, prog_context::in,
|
||||
unused_proc_args::in, list(error_spec)::in, list(error_spec)::out) is det.
|
||||
|
||||
report_unused_args(NameColonNlPieces, Context, ProcUnusedArgs, !Specs) :-
|
||||
one_or_more.foldl2(classify_unused_proc_arg, ProcUnusedArgs,
|
||||
[], UnmarkedArgs0, [], MarkedArgs0),
|
||||
list.sort(UnmarkedArgs0, UnmarkedArgs),
|
||||
list.sort(MarkedArgs0, MarkedArgs),
|
||||
(
|
||||
UnmarkedArgs = []
|
||||
% Since ProcUnusedArgs cannot be empty, we get here only if
|
||||
% all the unused arguments are explicitly marked as such.
|
||||
% In such cases, the warning would be a distraction, not a help.
|
||||
;
|
||||
UnmarkedArgs = [_ | TailUnmarkedArgs],
|
||||
Pieces1 = [words("In")] ++ NameColonNlPieces ++ [words("warning:")],
|
||||
UnmarkedArgPieces = piece_list_to_color_pieces(color_subject,
|
||||
"and", [], UnmarkedArgs),
|
||||
(
|
||||
TailUnmarkedArgs = [],
|
||||
Pieces2 = [words("argument")] ++ UnmarkedArgPieces ++
|
||||
[words("is")] ++ color_as_incorrect([words("unused.")]) ++ [nl]
|
||||
;
|
||||
TailUnmarkedArgs = [_ | _],
|
||||
Pieces2 = [words("arguments")] ++ UnmarkedArgPieces ++
|
||||
[words("are")] ++ color_as_incorrect([words("unused.")]) ++ [nl]
|
||||
),
|
||||
Addendum = marked_unused_args_addendum(MarkedArgs),
|
||||
Spec = spec($pred, severity_warning(warn_requested_by_option),
|
||||
phase_code_gen, Context, Pieces1 ++ Pieces2 ++ Addendum),
|
||||
!:Specs = [Spec | !.Specs]
|
||||
).
|
||||
|
||||
:- pred classify_unused_proc_arg(unused_proc_arg::in,
|
||||
list(format_piece)::in, list(format_piece)::out,
|
||||
list(format_piece)::in, list(format_piece)::out) is det.
|
||||
|
||||
classify_unused_proc_arg(UnusedProcArg, !UnmarkedArgNums, !MarkedArgNums) :-
|
||||
UnusedProcArg = unused_proc_arg(ArgNum, MaybeMarked),
|
||||
(
|
||||
MaybeMarked = not_marked_unused,
|
||||
!:UnmarkedArgNums = [int_fixed(ArgNum) | !.UnmarkedArgNums]
|
||||
;
|
||||
MaybeMarked = marked_unused,
|
||||
!:MarkedArgNums = [int_fixed(ArgNum) | !.MarkedArgNums]
|
||||
).
|
||||
|
||||
:- func marked_unused_args_addendum(list(format_piece)) = list(format_piece).
|
||||
|
||||
marked_unused_args_addendum(MarkedArgs) = Pieces :-
|
||||
MarkedArgPieces = piece_list_to_color_pieces(color_subject,
|
||||
"and", [], MarkedArgs),
|
||||
(
|
||||
MarkedArgs = [],
|
||||
Pieces = []
|
||||
;
|
||||
MarkedArgs = [_],
|
||||
Pieces = [words("(Argument")] ++ MarkedArgPieces ++
|
||||
[words("is also unused, but its mode")] ++
|
||||
color_as_correct([words("marks it")]) ++
|
||||
[words("as unused.)"), nl]
|
||||
;
|
||||
MarkedArgs = [_, _ | _],
|
||||
Pieces = [words("(Arguments")] ++ MarkedArgPieces ++
|
||||
[words("are also unused, but their modes")] ++
|
||||
color_as_correct([words("mark them")]) ++
|
||||
[words("as unused.)"), nl]
|
||||
).
|
||||
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
|
||||
@@ -31,9 +31,7 @@ MCFLAGS-unused_args_analysis += --warn-unused-args
|
||||
MCFLAGS-unused_args_analysis_helper_1 += --intermodule-analysis
|
||||
MCFLAGS-unused_args_analysis_helper_1 += --optimize-unused-args
|
||||
MCFLAGS-unused_args_analysis_helper_1 += --trace-optimized
|
||||
MCFLAGS-unused_args_analysis += --intermodule-analysis
|
||||
MCFLAGS-unused_args_analysis += --optimize-unused-args
|
||||
MCFLAGS-unused_args_analysis += --warn-unused-args
|
||||
MCFLAGS-unused_args_some_modes += --warn-unused-args
|
||||
MCFLAGS-unused_args_test += --warn-unused-args
|
||||
MCFLAGS-unused_import += --warn-unused-interface-imports
|
||||
MCFLAGS-inference_test += --infer-all
|
||||
|
||||
@@ -8,7 +8,7 @@ MAYBE_J1 =
|
||||
|
||||
#-----------------------------------------------------------------------------#
|
||||
|
||||
ERRORCHECK_PROGS = \
|
||||
STD_PROGS = \
|
||||
abstract_type_decl \
|
||||
allow_non_contiguity_for \
|
||||
ambiguous_overloading \
|
||||
@@ -67,6 +67,7 @@ ERRORCHECK_PROGS = \
|
||||
unneeded_final_statevar \
|
||||
unsigned_zero_cmp \
|
||||
unsorted_import_blocks \
|
||||
unused_args_some_modes \
|
||||
unused_args_test \
|
||||
unused_import \
|
||||
unused_interface_import \
|
||||
@@ -106,20 +107,27 @@ ERRORCHECK_PROGS = \
|
||||
#
|
||||
# Since we do bootchecks with mmc --make only in non-C grades,
|
||||
# during bootchecks, one or the other of these two tests will prevent us
|
||||
# from adding unused_args_analysis to ERRORCHECK_PROGS.
|
||||
# from enabling the unused_args_analysis test case.
|
||||
ifeq ($(MMAKE_USE_MMC_MAKE),yes)
|
||||
ifeq "$(filter java% csharp%,$(GRADE))" ""
|
||||
ERRORCHECK_PROGS += unused_args_analysis
|
||||
endif
|
||||
ifeq "$(filter java% csharp%,$(GRADE))" ""
|
||||
C_ONLY_PROGS = unused_args_analysis
|
||||
else
|
||||
C_ONLY_PROGS =
|
||||
endif
|
||||
else
|
||||
C_ONLY_PROGS =
|
||||
endif
|
||||
|
||||
PROGS = $(ERRORCHECK_PROGS) help_opt_levels help_text up_to_date
|
||||
# The simple programs are all handled by the same single general rule.
|
||||
# The other three programs each have their own specific rule.
|
||||
SIMPLE_PROGS = $(STD_PROGS) $(C_ONLY_PROGS)
|
||||
PROGS = $(SIMPLE_PROGS) help_opt_levels help_text up_to_date
|
||||
|
||||
TESTS = $(sort $(PROGS))
|
||||
include ../Mmake.common
|
||||
|
||||
# Module-specific options should go in Mercury.options so they
|
||||
# can be found by `mmc --make'.
|
||||
# Module-specific options should go in Mercury.options,
|
||||
# to allow them to be found by `mmc --make'.
|
||||
include Mercury.options
|
||||
|
||||
# With `mmc --make', the errors should only go to the `.err' files, not stderr.
|
||||
@@ -127,7 +135,13 @@ MCFLAGS += --output-compile-error-lines 0
|
||||
MCFLAGS += --color-diagnostics
|
||||
MCFLAGS += --infer-all
|
||||
|
||||
$(ERRORCHECK_PROGS:%=%.runtest): %.runtest: %.err_res ;
|
||||
$(SIMPLE_PROGS:%=%.runtest): %.runtest: %.err_res ;
|
||||
|
||||
# Build the `.analysis' file for unused_args_analysis_helper_1
|
||||
# before building unused_args_analysis.c.
|
||||
# NOTE As mentioned above, these dependencies are only for C.
|
||||
unused_args_analysis.c: unused_args_analysis_helper_1.c
|
||||
unused_args_analysis.err: unused_args_analysis_helper_1.c
|
||||
|
||||
help_opt_levels.runtest:
|
||||
$(MC) $(MCFLAGS) --output-optimization-options-upto 8 \
|
||||
@@ -142,12 +156,6 @@ help_text.runtest:
|
||||
> help_text.res || \
|
||||
{ cat help_text.res; exit 1; }
|
||||
|
||||
# Build the `.analysis' file for unused_args_analysis_helper_1
|
||||
# before building unused_args_analysis.c.
|
||||
# XXX These dependencies are only for C.
|
||||
unused_args_analysis.c: unused_args_analysis_helper_1.c
|
||||
unused_args_analysis.err: unused_args_analysis_helper_1.c
|
||||
|
||||
# Check that `mmc --make up_to_date.m' generates a warning.
|
||||
up_to_date.runtest:
|
||||
$(MCM) up_to_date.m > up_to_date.err_make 2>&1
|
||||
|
||||
10
tests/warnings/unused_args_some_modes.err_exp
Normal file
10
tests/warnings/unused_args_some_modes.err_exp
Normal file
@@ -0,0 +1,10 @@
|
||||
unused_args_some_modes.m:024: In [38;5;87m`p(in(unused_args_some_modes.i1), in, in,[39;49m
|
||||
unused_args_some_modes.m:024: [38;5;87min(free), in(free), out)':[39;49m
|
||||
unused_args_some_modes.m:024: warning: arguments [38;5;87m2[39;49m and [38;5;87m3[39;49m are [38;5;203munused.[39;49m
|
||||
unused_args_some_modes.m:024: (Arguments [38;5;87m4[39;49m and [38;5;87m5[39;49m are also unused, but their
|
||||
unused_args_some_modes.m:024: modes [38;5;40mmark them[39;49m as unused.)
|
||||
unused_args_some_modes.m:025: In [38;5;87m`p(in(unused_args_some_modes.i2), in, in,[39;49m
|
||||
unused_args_some_modes.m:025: [38;5;87min(free), in, out)':[39;49m
|
||||
unused_args_some_modes.m:025: warning: arguments [38;5;87m3[39;49m and [38;5;87m5[39;49m are [38;5;203munused.[39;49m
|
||||
unused_args_some_modes.m:025: (Argument [38;5;87m4[39;49m is also unused, but its mode [38;5;40mmarks[39;49m
|
||||
unused_args_some_modes.m:025: [38;5;40mit[39;49m as unused.)
|
||||
53
tests/warnings/unused_args_some_modes.m
Normal file
53
tests/warnings/unused_args_some_modes.m
Normal file
@@ -0,0 +1,53 @@
|
||||
%---------------------------------------------------------------------------%
|
||||
% vim: ts=4 sw=4 et ft=mercury
|
||||
%---------------------------------------------------------------------------%
|
||||
%
|
||||
% Test how unused_args.m handles arguments whose status (is it used or not,
|
||||
% and if not, is this expected or not) differs between clauses.
|
||||
%
|
||||
%---------------------------------------------------------------------------%
|
||||
|
||||
:- module unused_args_some_modes.
|
||||
:- interface.
|
||||
:- import_module char.
|
||||
|
||||
:- type t
|
||||
---> f1(int)
|
||||
; f2(char).
|
||||
|
||||
:- inst i1 for t/0
|
||||
---> f1(ground).
|
||||
:- inst i2 for t/0
|
||||
---> f2(ground).
|
||||
|
||||
:- pred p(t, int, int, int, int, int).
|
||||
:- mode p(in(i1), in, in, unused, unused, out) is det.
|
||||
:- mode p(in(i2), in, in, unused, in, out) is semidet.
|
||||
|
||||
:- implementation.
|
||||
|
||||
:- pragma promise_equivalent_clauses(pred(p/6)).
|
||||
|
||||
% Input arg I's status in the two clauses is unused/used.
|
||||
% Input arg J's status in the two clauses is unused/unused.
|
||||
% Input arg K's status in the two clauses is unused/unused, but both expected.
|
||||
% Input arg L's status in the two clauses is unused/unused, with ONE expected.
|
||||
|
||||
p(T::in(i1), _I::in, _J::in, _K::unused, _L::unused, O::out) :-
|
||||
% I is unused in this clause.
|
||||
% J is unused in this clause.
|
||||
% K is unused in this clause, but this is expected.
|
||||
% L is unused in this clause, but this is expected.
|
||||
T = f1(N),
|
||||
O = N.
|
||||
p(T::in(i2), I::in, _J::in, _K::unused, _L::in, O::out) :-
|
||||
% I is used in this clause.
|
||||
% J is unused in this clause.
|
||||
% K is unused in this clause, but this is expected.
|
||||
% L is unused in this clause.
|
||||
T = f2(C),
|
||||
( if char.is_alpha(C) then
|
||||
O = I
|
||||
else
|
||||
fail
|
||||
).
|
||||
@@ -1,4 +1,4 @@
|
||||
unused_args_test.m:013: In predicate `unused_args_test.recursive'/3:
|
||||
unused_args_test.m:013: In predicate [38;5;87m`recursive'/3:[39;49m
|
||||
unused_args_test.m:013: warning: argument [38;5;87m1[39;49m is [38;5;203munused.[39;49m
|
||||
unused_args_test.m:015: In predicate `unused_args_test.nonrecursive'/1:
|
||||
unused_args_test.m:015: In predicate [38;5;87m`nonrecursive'/1:[39;49m
|
||||
unused_args_test.m:015: warning: argument [38;5;87m1[39;49m is [38;5;203munused.[39;49m
|
||||
|
||||
Reference in New Issue
Block a user