mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 09:23:44 +00:00
compiler/analysis.m:
compiler/analysis.file.m:
Instead of throwing an exception when we find an unexpected version
number or an unrecognized item in a file, construct an error_spec
describing the problem, add it to the list, and continue processing,
if this is possible.
In some places, pass around the information needed for the construction
of meaningful error messages.
Note a limitation of the new approach to error handling (that it inherits
from the old approach).
Instead of reading in each item in analysis request/result files
separately, read in their whole files all at once, and just parse it
item by item.
Stop marking module_cmds as an unwanted dependency; it defines
undate_interface_return_changed, which is required for this module's job.
Give some predicates more meaningful names.
compiler/error_spec.m:
Fix some documentation rot.
compiler/mercury_compile_main.m:
compiler/mercury_compile_middle_passes.m:
Act on the error_specs that the analysis package can now return.
806 lines
32 KiB
Mathematica
806 lines
32 KiB
Mathematica
%---------------------------------------------------------------------------%
|
|
% vim: ft=mercury ts=4 sw=4 et
|
|
%---------------------------------------------------------------------------%
|
|
% Copyright (C) 1997-2012 The University of Melbourne.
|
|
% This file may only be copied under the terms of the GNU General
|
|
% Public License - see the file COPYING in the Mercury distribution.
|
|
%---------------------------------------------------------------------------%
|
|
%
|
|
% File: error_spec.m.
|
|
% Main author: zs.
|
|
%
|
|
% This module defines the error_spec structure for representing
|
|
% diagnostic messages, and utility predicates and functions that can help
|
|
% create error_specs.
|
|
%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- module parse_tree.error_spec.
|
|
:- interface.
|
|
|
|
:- import_module libs.
|
|
:- import_module libs.globals.
|
|
:- import_module libs.options.
|
|
:- import_module mdbcomp.
|
|
:- import_module mdbcomp.prim_data.
|
|
:- import_module mdbcomp.sym_name.
|
|
:- import_module parse_tree.prog_data.
|
|
|
|
:- import_module bool.
|
|
:- import_module edit_seq.
|
|
:- import_module io.
|
|
:- import_module list.
|
|
:- import_module maybe.
|
|
:- import_module set.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Every distinct problem should generate a single error specification,
|
|
% whose general form is error_spec(Id, Severity, Phase, Msgs).
|
|
% The second, third and fourth fields of this term state respectively
|
|
%
|
|
% - the severity of the problem (so that we can update the exit status
|
|
% of the compiler accordingly);
|
|
% - which phase of the compiler found the problem (since later phases
|
|
% may wish to suppress some problem reports if some specific earlier phases
|
|
% found problems, e.g. when a missing clause could be caused
|
|
% by a syntax error); and
|
|
% - a specification of what to print.
|
|
%
|
|
% In most cases, the "what to print" will be a single message for a single
|
|
% context. However, we may want to print messages for several contexts.
|
|
% For example, when reporting a duplicate declaration, we want to report
|
|
% this fact in the duplicate declaration's context, while printing another
|
|
% message giving the original declaration's context.
|
|
%
|
|
% simplest_spec(Id, Severity, Phase, Context, Pieces) is a shorthand for
|
|
% (and equivalent in every respect to) error_spec(Id, Severity, Phase,
|
|
% [simple_msg(Context, always(Pieces)])]).
|
|
%
|
|
% simplest_no_context_spec(Id, Severity, Phase, Pieces) is shorthand for
|
|
% (and equivalent in every respect to) error_spec(Id, Severity, Phase,
|
|
% error_msg(maybe.no, treat_based_on_posn, 0, [always(Pieces)])).
|
|
%
|
|
% conditional_spec(Id, Option, MatchValue, Severity, Phase, Msgs) is intended
|
|
% to represent the error specification given by its last three fields
|
|
% *iff* Option has the value MatchValue. If Option is *not* MatchValue,
|
|
% it asks for nothing to be printed, and for the exit status to be left alone.
|
|
%
|
|
% The Id field, which is present in all these alternatives, is totally
|
|
% ignored when printing error_specs. Its job is something completely different:
|
|
% helping developers track down where in the source code each error_spec
|
|
% was constructed. Without the id fields, if developers wants to know this,
|
|
% e.g. because they do not want the message printed, or because there is
|
|
% a problem with its wording, they have to grep for some words in the message.
|
|
% However, grepping for a single word will usually get many false hits,
|
|
% while grepping for two or more consecutive words in the message may miss
|
|
% the code generating the message, because in that code, some of those
|
|
% consecutive words are on different lines. On the other hand, if every
|
|
% place that constructs an error_spec, of any of these three varieties,
|
|
% fills in the id field with $pred, then finding the right place is easy:
|
|
% just specify the developer-only option --print-error-spec-id, and
|
|
% the identity of the predicate or function that generated each error_spec
|
|
% will be output just after the messages in that error_spec. Even if the
|
|
% predicate or function that this identifies has several pieces of code
|
|
% that construct error_specs, the scope in which you have to search for
|
|
% the one you are looking for will be easily manageable.
|
|
|
|
:- type error_spec
|
|
---> error_spec(
|
|
error_id :: string,
|
|
error_severity :: error_severity,
|
|
error_phase :: error_phase,
|
|
error_msgs :: list(error_msg)
|
|
)
|
|
; simplest_spec(
|
|
simp_id :: string,
|
|
simp_spec_severity :: error_severity,
|
|
simp_spec_phase :: error_phase,
|
|
simp_spec_context :: prog_context,
|
|
simp_spec_pieces :: list(format_piece)
|
|
)
|
|
; simplest_no_context_spec(
|
|
simpnc_id :: string,
|
|
simpnc_spec_severity :: error_severity,
|
|
simpnc_spec_phase :: error_phase,
|
|
simpnc_spec_pieces :: list(format_piece)
|
|
)
|
|
; conditional_spec(
|
|
cond_id :: string,
|
|
cond_spec_option :: option,
|
|
cond_spec_value :: bool,
|
|
|
|
cond_spec_severity :: error_severity,
|
|
cond_spec_phase :: error_phase,
|
|
cond_spec_msgs :: list(error_msg)
|
|
).
|
|
|
|
% An error_spec that is *intended* to contain a warning,
|
|
%
|
|
:- type warning_spec == error_spec.
|
|
|
|
% Many operations in the compiler may either succeed or fail.
|
|
% When they succeed, they return some result(s); when they don't,
|
|
% they return one or more errors.
|
|
%
|
|
:- type maybe_error_specs(T)
|
|
---> ok_no_spec(T)
|
|
; error_specs(error_spec, list(error_spec)).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type error_severity
|
|
---> severity_error
|
|
% Always set the exit status to indicate an error.
|
|
|
|
; severity_warning
|
|
% Only set the exit status to indicate an error if --halt-at-warn
|
|
% is enabled.
|
|
|
|
; severity_informational
|
|
% Don't set the exit status to indicate an error.
|
|
|
|
; severity_conditional(
|
|
% If the given boolean option has the given value, then the actual
|
|
% severity is given by the third argument; if it has the other
|
|
% value, then the actual severity is given by the fourth argument.
|
|
% If the fourth argument is `no', then the error_spec shouldn't
|
|
% actually print anything if cond_option doesn't have the value
|
|
% in cond_option_value.
|
|
|
|
cond_option :: option,
|
|
cond_option_value :: bool,
|
|
cond_if_match :: error_severity,
|
|
cond_if_no_match :: maybe(error_severity)
|
|
).
|
|
|
|
:- type actual_severity
|
|
---> actual_severity_error
|
|
; actual_severity_warning
|
|
; actual_severity_informational.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type error_phase
|
|
---> phase_options
|
|
; phase_check_libs
|
|
; phase_make_target
|
|
; phase_read_files
|
|
; phase_module_name
|
|
; phase_term_to_parse_tree
|
|
% Some errors in check_type_inst_mode_defns.m report an invalid type, ...
|
|
; phase_type_inst_mode_check_invalid_type
|
|
% some report an invalid inst or mode, ...
|
|
; phase_type_inst_mode_check_invalid_inst_mode
|
|
% and some do neither.
|
|
; phase_type_inst_mode_check
|
|
; phase_type_repn
|
|
; phase_parse_tree_to_hlds
|
|
; phase_expand_types
|
|
; phase_type_check
|
|
; phase_inst_check
|
|
; phase_polymorphism
|
|
; phase_mode_check(mode_report_control)
|
|
; phase_purity_check
|
|
; phase_detism_check
|
|
; phase_fact_table_check
|
|
; phase_oisu_check
|
|
; phase_simplify(mode_report_control)
|
|
; phase_direct_arg_in_out
|
|
; phase_style
|
|
; phase_dead_code
|
|
; phase_termination_analysis
|
|
; phase_accumulator_intro
|
|
; phase_auto_parallelism
|
|
; phase_interface_gen
|
|
; phase_code_gen.
|
|
|
|
:- type mode_report_control
|
|
---> report_in_any_mode
|
|
; report_only_if_in_all_modes.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% An error message may have several components that may be printed under
|
|
% different circumstances. Some components are always printed; some are
|
|
% printed only if specific options have specific values. When an error
|
|
% specification is printed, we concatenate the list of all the
|
|
% format_pieces that should be printed. If this yields the empty list,
|
|
% we print nothing. Otherwise, we give them all to write_error_pieces to print
|
|
% out.
|
|
%
|
|
% When we print an error message in a list of error messages, we normally
|
|
% treat the first line of the first message differently than the rest:
|
|
% we separate it from the context by one space, whereas following lines
|
|
% are separated by three spaces. You can request that the first line of
|
|
% a message be treated as it were the first, even if it is not, by setting
|
|
% the error_treat_as_first field to "always_treat_as_first". You can also
|
|
% request that the pieces in a message be given extra indentation by setting
|
|
% the error_extra_indent field to a strictly positive value.
|
|
%
|
|
% The term simple_msg(Context, Components) is a shorthand for (and equivalent
|
|
% in every respect to) the term error_msg(yes(Context), treat_based_on_posn,
|
|
% 0, Components).
|
|
%
|
|
% The term simplest_msg(Context, Pieces) is a shorthand for (and equivalent
|
|
% in every respect to) the term simple_msg(Context, [always(Pieces)]).
|
|
|
|
:- type maybe_always_treat_as_first
|
|
---> always_treat_as_first
|
|
; treat_based_on_posn.
|
|
|
|
:- type error_msg
|
|
---> simplest_msg(
|
|
simplest_context :: prog_context,
|
|
simplest_pieces :: list(format_piece)
|
|
)
|
|
; simplest_no_context_msg(
|
|
simplestnc_pieces :: list(format_piece)
|
|
)
|
|
; simple_msg(
|
|
simple_context :: prog_context,
|
|
simple_components :: list(error_msg_component)
|
|
)
|
|
; error_msg(
|
|
error_context :: maybe(prog_context),
|
|
error_treat_as_first :: maybe_always_treat_as_first,
|
|
error_extra_indent :: int,
|
|
error_components :: list(error_msg_component)
|
|
).
|
|
|
|
:- type verbose_always_or_once
|
|
---> verbose_always
|
|
; verbose_once.
|
|
% Message components marked as verbose_once should be printed
|
|
% just once.
|
|
|
|
:- type error_msg_component
|
|
---> always(list(format_piece))
|
|
% Print these components under all circumstances.
|
|
|
|
; option_is_set(option, bool, list(error_msg_component))
|
|
% Print the embedded components only if the specified boolean
|
|
% option has the specified value.
|
|
|
|
; verbose_only(verbose_always_or_once, list(format_piece))
|
|
% Print these components only if --verbose-errors is specified.
|
|
% If it is not specified, set the flag that triggers the printing
|
|
% of the message reminding the user about --verbose-errors.
|
|
% In addition, if the first field is verbose_once, then disable
|
|
% all but the first printing of the message even if
|
|
% --verbose-errors is specified.
|
|
|
|
; verbose_and_nonverbose(list(format_piece), list(format_piece))
|
|
% If --verbose-errors is specified, print the first set of
|
|
% components. If it is not specified, print the second set,
|
|
% and set the flag that triggers the printing of the message
|
|
% reminding the user about --verbose-errors. The verbose part
|
|
% is implicitly verbose_always.
|
|
|
|
; some [T] ( print_anything(T) => print_anything(T) ).
|
|
% This alternative allows the caller to specify an arbitrary thing
|
|
% to be printed at any point in the sequence. Since things printed
|
|
% this way aren't formatted as error messages should be (context
|
|
% at start etc), this capability is intended only for messages
|
|
% that help debug the compiler itself.
|
|
|
|
:- typeclass print_anything(T) where [
|
|
pred print_anything(T::in, io::di, io::uo) is det
|
|
].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- type format_piece
|
|
---> invis_order_default_start(int)
|
|
% Prints nothing. If the compiler generates two different specs
|
|
% for the same context that we intend to appear in a specific
|
|
% order, even though it may not be the order that sorting those
|
|
% specs would normally give, we can add one of these to the
|
|
% start of each error_spec, with the order of the numbers
|
|
% inside these invis orders controlling the final order
|
|
% of the error_specs.
|
|
%
|
|
% This component sorts before other components that do not
|
|
% specify such an ordinal number. The invis_order_default_end
|
|
% component sorts after them. By choosing to use one or the other,
|
|
% users of this type can control sorting with respect to
|
|
% error messages generated in places in the code they do not
|
|
% control.
|
|
|
|
; fixed(string)
|
|
% This string should appear in the output in one piece, as it is.
|
|
|
|
; quote(string)
|
|
% Surround the string with `' quotes, then treat as fixed.
|
|
|
|
; int_fixed(int)
|
|
; int_name(int)
|
|
% Convert the integer to a string, then treat as fixed.
|
|
% int_fixed always generates numerals, such as 1, 2, 3 etc,
|
|
% while int_name generates one, two, three etc up to ten,
|
|
% then switches back to numerals starting with 11.
|
|
|
|
; nth_fixed(int)
|
|
% Convert the integer to a string, such as "first", "second",
|
|
% "third", tenth, 11th and so on, and then treat as fixed.
|
|
|
|
; lower_case_next_if_not_first
|
|
% If this is the first component, ignore it. If this is not
|
|
% the first component, lower case the initial letter of the
|
|
% next component. There is no effect if the next component
|
|
% does not exist or does not start with an upper case letter.
|
|
|
|
; treat_next_as_first
|
|
% For the purpose of the test done by lower_case_next_if_not_first,
|
|
% treat the next component as the first, even if it isn't.
|
|
|
|
; prefix(string)
|
|
% This string should appear in the output in one piece, as it is,
|
|
% inserted directly before the next format_piece, without
|
|
% any intervening space.
|
|
|
|
; suffix(string)
|
|
% This string should appear in the output in one piece, as it is,
|
|
% appended directly after the previous format_piece, without
|
|
% any intervening space.
|
|
|
|
; words(string)
|
|
% This string contains words separated by white space. The words
|
|
% should appear in the output in the given order, but the white
|
|
% space may be rearranged and line breaks may be inserted.
|
|
|
|
; words_quote(string)
|
|
% Surround the string with `' quotes, then treat as words.
|
|
|
|
; qual_sym_name(sym_name)
|
|
; unqual_sym_name(sym_name)
|
|
% The output should contain the string form of the sym_name,
|
|
% surrounded by `' quotes.
|
|
|
|
; name_arity(name_arity)
|
|
% The output should contain the name, surrounded by `' quotes,
|
|
% followed by '/' and the arity.
|
|
|
|
; qual_sym_name_arity(sym_name_arity)
|
|
; unqual_sym_name_arity(sym_name_arity)
|
|
% The output should contain the string form of the sym_name,
|
|
% surrounded by `' quotes, followed by '/' and the arity.
|
|
|
|
; qual_pf_sym_name_pred_form_arity(pf_sym_name_arity)
|
|
; unqual_pf_sym_name_pred_form_arity(pf_sym_name_arity)
|
|
; qual_pf_sym_name_user_arity(pred_pf_name_arity)
|
|
; unqual_pf_sym_name_user_arity(pred_pf_name_arity)
|
|
% The output should contain the string form of the sym_name,
|
|
% surrounded by `' quotes, followed by '/' and the arity, but
|
|
% - precede them with either "predicate" or "function", and
|
|
% - for functions, use their *user-visible* arity, which does not
|
|
% count the function result.
|
|
%
|
|
% With the forms taking a pf_sym_name_arity argument, the
|
|
% pf_sym_name_arity contains a pred_form_arity that we convert
|
|
% to the user visible arity for printing. With the forms taking
|
|
% a pred_pf_name_arity argument, the pf_sym_name_arity contains
|
|
% a user_arity that we print unchanged.
|
|
|
|
; qual_type_ctor(type_ctor)
|
|
; unqual_type_ctor(type_ctor)
|
|
% The output should contain the string form of the type_ctor,
|
|
% surrounded by `' quotes, followed by '/' and the arity.
|
|
|
|
; qual_inst_ctor(inst_ctor)
|
|
; unqual_inst_ctor(inst_ctor)
|
|
% The output should contain the string form of the inst_ctor,
|
|
% surrounded by `' quotes, followed by '/' and the arity.
|
|
|
|
; qual_mode_ctor(mode_ctor)
|
|
; unqual_mode_ctor(mode_ctor)
|
|
% The output should contain the string form of the mode_ctor,
|
|
% surrounded by `' quotes, followed by '/' and the arity.
|
|
|
|
; qual_class_id(class_id)
|
|
; unqual_class_id(class_id)
|
|
% The output should contain the string form of the class_id,
|
|
% surrounded by `' quotes, followed by '/' and the arity.
|
|
|
|
; qual_cons_id_and_maybe_arity(cons_id)
|
|
; unqual_cons_id_and_maybe_arity(cons_id)
|
|
% If the cons_id is a cons_id for a builtin type, strip the
|
|
% builtin qualifier (or all qualifier) from it, and output
|
|
% the result. If the cons_id is for a du type, output its name
|
|
% in quotes, followed by '/' and its arity.
|
|
|
|
; qual_top_ctor_of_type(mer_type)
|
|
% The top level type constructor of the given type,
|
|
% which must have one (i.e. must not be a variable).
|
|
|
|
; p_or_f(pred_or_func)
|
|
% Output the string "predicate" or "function" as appropriate.
|
|
|
|
; purity_desc(purity)
|
|
% Output the string "pure", "semipure" or "impure" as appropriate.
|
|
|
|
; a_purity_desc(purity)
|
|
% Output the string "a pure", "a semipure" or "an impure"
|
|
% as appropriate.
|
|
|
|
; decl(string)
|
|
% Prefix the string with ":- ", surround it with single quotes,
|
|
% and then treat as fixed.
|
|
|
|
; pragma_decl(string)
|
|
% As above, but prefix the string with ":- pragma ".
|
|
|
|
; left_paren_maybe_nl_inc(string, lp_piece_kind)
|
|
; maybe_nl_dec_right_paren(string, rp_piece_kind)
|
|
% These two pieces are intended to help implement messages
|
|
% that should be formatted to look like either
|
|
%
|
|
% aaa(bbb, ccc)
|
|
%
|
|
% if there is space on the current line, or to look like
|
|
%
|
|
% aaa(
|
|
% bbb,
|
|
% ccc
|
|
% )
|
|
%
|
|
% if there isn't.
|
|
%
|
|
% The piece sequence that would yield the above would be
|
|
%
|
|
% fixed("aaa")
|
|
% left_paren_maybe_nl_inc("(", lp_suffix)
|
|
% fixed("bbb"),
|
|
% suffix(","),
|
|
% nl,
|
|
% fixed("ccc"),
|
|
% maybe_nl_dec_right_paren(")", rp_plain)
|
|
%
|
|
% The left_paren_maybe_nl_inc adds the given string to the
|
|
% text, followed by an nl_indent(1). The maybe_nl_dec_right_paren
|
|
% adds an nl_indent(-1) to the text, followed by the given string.
|
|
% The left parenthesis may be added to the previous piece
|
|
% as a suffix, or not; the right parenthesis may be added
|
|
% to the next piece as a prefix, or not. The strings are usually
|
|
% "(" and ")", but they could also be "{" and "}", or "[" and "]",
|
|
% or anything else.
|
|
%
|
|
% The "maybe" is there in the names of these pieces because
|
|
% these pieces expressly tell the code of write_error_pieces.m
|
|
% to delete both these indent-incrementing/decrementing newlines,
|
|
% and all other newlines between the left_paren_maybe_nl_inc
|
|
% and its matching maybe_nl_dec_right_paren, provided the text
|
|
% between them fits in the space available on the line.
|
|
% Note that the size of the space depends on both the length
|
|
% of the context printed at the start of the line, and on the
|
|
% indent printed after the context, which may or may not be
|
|
% available to the code constructing these pieces.
|
|
%
|
|
% These pieces should always be used in left/right pairs,
|
|
% and should always be properly nested.
|
|
|
|
; nl
|
|
% Insert a line break if there has been text output since
|
|
% the last line break.
|
|
|
|
; nl_indent_delta(int)
|
|
% Act as nl, but also add the given integer (which should be a
|
|
% small positive or negative integer) to the current indent level.
|
|
|
|
; blank_line
|
|
% Create a blank line.
|
|
|
|
; invis_order_default_end(int).
|
|
% See the documentation of invis_order_default_start above.
|
|
|
|
:- type lp_piece_kind
|
|
---> lp_plain
|
|
% The left parenthesis should be added to the previous pieces
|
|
% as if it were in a fixed(...) piece.
|
|
; lp_suffix.
|
|
% The left parenthesis should be added to the previous pieces
|
|
% as if it were in a suffix(...) piece.
|
|
|
|
:- type rp_piece_kind
|
|
---> rp_plain
|
|
% The right parenthesis should be added to the following pieces
|
|
% as if it were in a fixed(...) piece.
|
|
; rp_prefix.
|
|
% The right parenthesis should be added to the following pieces
|
|
% as if it were in a prefix(...) piece.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Wrap words() around a string.
|
|
%
|
|
:- func string_to_words_piece(string) = format_piece.
|
|
|
|
% Convert a list of strings into a list of format_pieces
|
|
% separated by commas, with the last two elements separated by `and'.
|
|
%
|
|
:- func list_to_pieces(list(string)) = list(format_piece).
|
|
|
|
% Convert a list of strings into a list of format_pieces
|
|
% separated by commas. Even the last pair of strings will be
|
|
% separated by commas.
|
|
%
|
|
:- func strict_list_to_pieces(list(string)) = list(format_piece).
|
|
|
|
% As list_to_pieces, but surround each string by `' quotes.
|
|
%
|
|
:- func list_to_quoted_pieces(list(string)) = list(format_piece).
|
|
|
|
% As above, but with the last two elements separated by `or'.
|
|
%
|
|
:- func list_to_quoted_pieces_or(list(string)) = list(format_piece).
|
|
|
|
% Convert a list of lists of format_pieces into a list of
|
|
% format_pieces separated by commas, with the last two elements
|
|
% separated by the first argument as a word.
|
|
%
|
|
:- func component_lists_to_pieces(string, list(list(format_piece))) =
|
|
list(format_piece).
|
|
|
|
% Convert a list of lists of format_pieces into a list of
|
|
% format_pieces separated by commas. Even the last pair of lists
|
|
% will be separated by commas.
|
|
%
|
|
:- func strict_component_lists_to_pieces(list(list(format_piece))) =
|
|
list(format_piece).
|
|
|
|
% Convert a list of format_pieces into a list of format_pieces
|
|
% separated by commas, with the last two elements separated
|
|
% by the first argument as a word.
|
|
%
|
|
:- func component_list_to_pieces(string, list(format_piece)) =
|
|
list(format_piece).
|
|
|
|
% Convert a list of format_pieces into a list of format_pieces
|
|
% separated by commas. Even the last pair of list elements will be
|
|
% separated by commas.
|
|
%
|
|
:- func strict_component_list_to_pieces(list(format_piece)) =
|
|
list(format_piece).
|
|
|
|
% component_list_to_line_pieces(Lines, Final):
|
|
%
|
|
% Convert Lines, a list of lines (each given by a list of format_pieces
|
|
% *without* a final nl) into a condensed list of format_pieces
|
|
% in which adjacent lines are separated by commas and newlines.
|
|
% What goes after the end of the last line is not a comma, but
|
|
% the value of Final.
|
|
%
|
|
:- func component_list_to_line_pieces(list(list(format_piece)),
|
|
list(format_piece)) = list(format_piece).
|
|
|
|
% choose_number(List, Singular, Plural) = Form
|
|
%
|
|
% Choose between a singular version and a plural version of something,
|
|
% based on the length of a list. Chooses the plural if the list is empty.
|
|
%
|
|
:- func choose_number(list(T), U, U) = U.
|
|
|
|
% is_or_are(List) throws an exception if the list is empty, returns "is"
|
|
% if the list is a singleton, and otherwise returns "are".
|
|
%
|
|
:- func is_or_are(list(T)) = string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
% Convert the output of find_change_hunks from library/edit_seq.m
|
|
% to a diff we can include in error messages.
|
|
%
|
|
:- pred change_hunk_to_pieces(change_hunk(string)::in,
|
|
list(format_piece)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- func describe_sym_name(sym_name) = string.
|
|
|
|
:- func describe_sym_name_arity(sym_name_arity) = string.
|
|
|
|
% Put `' quotes around the given string.
|
|
%
|
|
:- func add_quotes(string) = string.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- pred extract_spec_phase(error_spec::in, error_phase::out) is det.
|
|
|
|
:- pred extract_spec_msgs(globals::in, error_spec::in,
|
|
list(error_msg)::out) is det.
|
|
|
|
:- pred accumulate_contexts(error_spec::in,
|
|
set(prog_context)::in, set(prog_context)::out) is det.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
%---------------------------------------------------------------------------%
|
|
|
|
:- implementation.
|
|
|
|
:- import_module require.
|
|
:- import_module string.
|
|
:- import_module term_context.
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
string_to_words_piece(Str) = words(Str).
|
|
|
|
list_to_pieces([]) = [].
|
|
list_to_pieces([Elem]) = [fixed(Elem)].
|
|
list_to_pieces([Elem1, Elem2]) = [fixed(Elem1), words("and"), fixed(Elem2)].
|
|
list_to_pieces([Elem1, Elem2, Elem3 | Elems]) =
|
|
[fixed(Elem1 ++ ",") | list_to_pieces([Elem2, Elem3 | Elems])].
|
|
|
|
strict_list_to_pieces([]) = [].
|
|
strict_list_to_pieces([Elem]) = [fixed(Elem)].
|
|
strict_list_to_pieces([Elem1, Elem2 | Elems]) =
|
|
[fixed(Elem1 ++ ",") | strict_list_to_pieces([Elem2 | Elems])].
|
|
|
|
list_to_quoted_pieces([]) = [].
|
|
list_to_quoted_pieces([Elem]) = [quote(Elem)].
|
|
list_to_quoted_pieces([Elem1, Elem2]) =
|
|
[quote(Elem1), words("and"), quote(Elem2)].
|
|
list_to_quoted_pieces([Elem1, Elem2, Elem3 | Elems]) =
|
|
[quote(Elem1), suffix(",") |
|
|
list_to_quoted_pieces([Elem2, Elem3 | Elems])].
|
|
|
|
list_to_quoted_pieces_or([]) = [].
|
|
list_to_quoted_pieces_or([Elem]) = [quote(Elem)].
|
|
list_to_quoted_pieces_or([Elem1, Elem2]) =
|
|
[quote(Elem1), words("or"), quote(Elem2)].
|
|
list_to_quoted_pieces_or([Elem1, Elem2, Elem3 | Elems]) =
|
|
[quote(Elem1), suffix(",") |
|
|
list_to_quoted_pieces_or([Elem2, Elem3 | Elems])].
|
|
|
|
component_lists_to_pieces(_, []) = [].
|
|
component_lists_to_pieces(_, [Comps]) = Comps.
|
|
component_lists_to_pieces(LastSep, [Comps1, Comps2]) =
|
|
Comps1 ++ [words(LastSep)] ++ Comps2.
|
|
component_lists_to_pieces(LastSep, [Comps1, Comps2, Comps3 | Comps]) =
|
|
Comps1 ++ [suffix(",")]
|
|
++ component_lists_to_pieces(LastSep, [Comps2, Comps3 | Comps]).
|
|
|
|
strict_component_lists_to_pieces([]) = [].
|
|
strict_component_lists_to_pieces([Comps]) = Comps.
|
|
strict_component_lists_to_pieces([Comps1, Comps2 | Comps]) =
|
|
Comps1 ++ [suffix(",")]
|
|
++ strict_component_lists_to_pieces([Comps2 | Comps]).
|
|
|
|
component_list_to_pieces(_, []) = [].
|
|
component_list_to_pieces(_, [Comp]) = [Comp].
|
|
component_list_to_pieces(LastSep, [Comp1, Comp2]) =
|
|
[Comp1, words(LastSep), Comp2].
|
|
component_list_to_pieces(LastSep, [Comp1, Comp2, Comp3 | Comps]) =
|
|
[Comp1, suffix(",")]
|
|
++ component_list_to_pieces(LastSep, [Comp2, Comp3 | Comps]).
|
|
|
|
strict_component_list_to_pieces([]) = [].
|
|
strict_component_list_to_pieces([Comp]) = [Comp].
|
|
strict_component_list_to_pieces([Comp1, Comp2 | Comps]) =
|
|
[Comp1, suffix(",")]
|
|
++ strict_component_list_to_pieces([Comp2 | Comps]).
|
|
|
|
component_list_to_line_pieces([], _) = [].
|
|
component_list_to_line_pieces([Comps], Final) = Comps ++ Final.
|
|
component_list_to_line_pieces([Comps1, Comps2 | CompLists], Final) =
|
|
Comps1 ++ [suffix(","), nl]
|
|
++ component_list_to_line_pieces([Comps2 | CompLists], Final).
|
|
|
|
choose_number([], _Singular, Plural) = Plural.
|
|
choose_number([_], Singular, _Plural) = Singular.
|
|
choose_number([_, _ | _], _Singular, Plural) = Plural.
|
|
|
|
is_or_are([]) = "" :-
|
|
unexpected($pred, "[]").
|
|
is_or_are([_]) = "is".
|
|
is_or_are([_, _ | _]) = "are".
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
change_hunk_to_pieces(ChangeHunk, ChangeHunkPieces) :-
|
|
ChangeHunk = change_hunk(StartA, LenA, StartB, LenB, Diffs),
|
|
string.format("@@ -%d,%d +%d,%d @@",
|
|
[i(StartA), i(LenA), i(StartB), i(LenB)], HeaderStr),
|
|
HeaderPieces = [fixed(HeaderStr), nl],
|
|
list.map(diff_seq_line_to_pieces, Diffs, DiffPieceLists),
|
|
list.condense([HeaderPieces | DiffPieceLists], ChangeHunkPieces).
|
|
|
|
:- pred diff_seq_line_to_pieces(diff(string)::in, list(format_piece)::out)
|
|
is det.
|
|
|
|
diff_seq_line_to_pieces(Diff, Pieces) :-
|
|
(
|
|
Diff = unchanged(Str),
|
|
Line = " " ++ Str
|
|
;
|
|
Diff = deleted(Str),
|
|
Line = "-" ++ Str
|
|
;
|
|
Diff = inserted(Str),
|
|
Line = "+" ++ Str
|
|
),
|
|
Pieces = [fixed(Line), nl].
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
describe_sym_name(SymName) =
|
|
string.format("`%s'", [s(sym_name_to_string(SymName))]).
|
|
|
|
describe_sym_name_arity(sym_name_arity(SymName, Arity)) =
|
|
string.format("`%s'/%d", [s(sym_name_to_string(SymName)), i(Arity)]).
|
|
|
|
add_quotes(Str) = "`" ++ Str ++ "'".
|
|
|
|
%---------------------------------------------------------------------------%
|
|
|
|
extract_spec_phase(Spec, Phase) :-
|
|
(
|
|
Spec = error_spec(_, _, Phase, _)
|
|
;
|
|
Spec = simplest_spec(_, _, Phase, _, _)
|
|
;
|
|
Spec = simplest_no_context_spec(_, _, Phase, _)
|
|
;
|
|
Spec = conditional_spec(_, _, _, _, Phase, _)
|
|
).
|
|
|
|
extract_spec_msgs(Globals, Spec, Msgs) :-
|
|
(
|
|
Spec = error_spec(_Id, _Severity, _Phase, Msgs)
|
|
;
|
|
Spec = simplest_spec(_Id, _Severity, _Phase, Context, Pieces),
|
|
Msgs = [simplest_msg(Context, Pieces)]
|
|
;
|
|
Spec = simplest_no_context_spec(_Id, _Severity, _Phase, Pieces),
|
|
Msgs = [simplest_no_context_msg(Pieces)]
|
|
;
|
|
Spec = conditional_spec(_Id, Option, MatchValue, _Severity, _Phase,
|
|
Msgs0),
|
|
globals.lookup_bool_option(Globals, Option, Value),
|
|
( if Value = MatchValue then
|
|
Msgs = Msgs0
|
|
else
|
|
Msgs = []
|
|
)
|
|
).
|
|
|
|
accumulate_contexts(Spec, !Contexts) :-
|
|
(
|
|
( Spec = error_spec(_, _, _, Msgs)
|
|
; Spec = conditional_spec(_, _, _, _, _, Msgs)
|
|
),
|
|
list.foldl(accumulate_contexts_in_msg, Msgs, !Contexts)
|
|
;
|
|
Spec = simplest_spec(_, _, _, Context, _),
|
|
set.insert(Context, !Contexts)
|
|
;
|
|
Spec = simplest_no_context_spec(_, _, _, _)
|
|
).
|
|
|
|
:- pred accumulate_contexts_in_msg(error_msg::in,
|
|
set(prog_context)::in, set(prog_context)::out) is det.
|
|
|
|
accumulate_contexts_in_msg(Msg, !Contexts) :-
|
|
(
|
|
Msg = simplest_no_context_msg(_)
|
|
;
|
|
( Msg = simplest_msg(Context, _)
|
|
; Msg = simple_msg(Context, _)
|
|
),
|
|
set.insert(Context, !Contexts)
|
|
;
|
|
Msg = error_msg(MaybeContext, _, _, _),
|
|
(
|
|
MaybeContext = no
|
|
;
|
|
MaybeContext = yes(Context),
|
|
set.insert(Context, !Contexts)
|
|
)
|
|
).
|
|
|
|
%---------------------------------------------------------------------------%
|
|
:- end_module parse_tree.error_spec.
|
|
%---------------------------------------------------------------------------%
|