Files
mercury/compiler/parse_error.m
Zoltan Somogyi 307b1dc148 Split up error_util.m into five modules.
compiler/error_spec.m:
    This new module contains the part of the old error_util.m that defines
    the error_spec type, and some functions that can help construct pieces
    of error_specs. Most modules of the compiler that deal with errors
    will need to import only this part of the old error_util.m.

    This change also renames the format_component type to format_piece,
    which matches our long-standing naming convention for variables containing
    (lists of) values of this type.

compiler/write_error_spec.m:
    This new module contains the part of the old error_util.m that
    writes out error specs, and converts them to strings.

    This diff marks as obsolete the versions of predicates that
    write out error specs to the current output stream, without
    *explicitly* specifying the intended stream.

compiler/error_sort.m:
    This new module contains the part of the old error_util.m that
    sorts lists of error specs and error msgs.

compiler/error_type_util.m:
    This new module contains the part of the old error_util.m that
    convert types to format_pieces that generate readable output.

compiler/parse_tree.m:
compiler/notes/compiler_design.html:
    Include and document the new modules.

compiler/error_util.m:
    The code remaining in the original error_util.m consists of
    general utility predicates and functions that don't fit into
    any of the modules above.

    Delete an unneeded pair of I/O states from the argument list
    of a predicate.

compiler/file_util.m:
    Move the unable_to_open_file predicate here from error_util.m,
    since it belongs here. Mark another predicate that writes
    to the current output stream as obsolete.

compiler/hlds_error_util.m:
    Mark two predicates that wrote out error_spec to the current output
    stream as obsolete, and add versions that take an explicit output stream.

compiler/Mercury.options:
    Compile the modules that call the newly obsoleted predicates
    with --no-warn-obsolete, for the time being.

compiler/*.m:
    Conform to the changes above, mostly by updating import_module
    declarations, and renaming format_component to format_piece.
2022-10-12 20:50:16 +11:00

265 lines
10 KiB
Mathematica

%-----------------------------------------------------------------------------e
% vim: ft=mercury ts=4 sw=4 et
%-----------------------------------------------------------------------------e
% Copyright (C) 2014 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.
%---------------------------------------------------------------------------%
:- module parse_tree.parse_error.
:- interface.
:- import_module parse_tree.error_spec.
:- import_module io.
:- import_module list.
:- import_module set.
% 1. We obviously cannot process the module if we could not read it.
%
% 2. If the module contains some bad ":- module" declarations, then
% the identity of the module that contains any items following
% such declarations will be in doubt. The compiler can guess the
% identity of the module intended by the programmer only by chance,
% and any mistake will typically lead to an avalanche of misleading
% errors.
%
% 3. If the module contains some bad ":- end_module" declarations,
% then the same is true for any items following those declarations.
%
% Items follow most ":- module" declarations, but not most ":- end_module"
% declarations, and if no items follow a bad ":- end_module" declaration,
% then the badness of the declaration has no further consequences.
% It would therefore be worthwhile making a distinction between bad
% ":- end_module" declarations that have items following them, and those
% that don't. Unfortunately, since each item is processed independently,
% that is not trivial to do.
%
:- type fatal_read_module_error
---> frme_could_not_open_file
% We could not open the specified file.
; frme_could_not_read_file
% We could open the specified file, but could not read its
% contents.
; frme_bad_submodule_start
% We encountered a declaration for the start of a submodule,
% but the name of the new submodule is not that can be immediately
% nested inside the current module.
% NOTE: This error can happen only for submodules of the main
% module in the file. For the main module itself, we would generate
% the rme_unexpected_module_name error.
; frme_bad_module_end.
% We encountered a declaration for the end of a module,
% but the name of the ended module is not the name of the
% until-then-current module.
% NOTE: This error can happen both for the main module of the
% file and for its submodules.
% This type represents the kinds of nonfatal errors that can happen
% when we read in a Mercury module (which will be either a source file,
% or an interface file).
%
:- type nonfatal_read_module_error
---> rme_unexpected_module_name
% The file starts with a module declaration for a module
% other than the one we expected.
; rme_no_module_decl_at_start
% The file does not start with a module declaration at all.
; rme_no_section_decl_at_start
% The module does not start with either an interface or an
% implementation section marker.
; rme_end_module_not_at_end_of_src
% The source code of a module has at least one term
% after the end_module marker for the main module.
; rme_unexpected_term_in_int_or_opt
% The interface or optimization file of a module has at least one
% term that is not expected in such a file.
; rme_could_not_read_term
; rme_could_not_parse_item
% When we attempted to read an item from the file, we got a failure
% either in the first stage of parsing (reading in a term), or
% the second stage of parsing (converting that term to an item).
% Since the rest of the compiler should not care whether parsing
% is done in one or two stages, it should treat both these errors
% the same. We distinguish them only for completeness.
; rme_nec.
% A read module error that is Not Elsewhere Classified, i.e.
% is not one of the error kinds listed above.
%
% Before the change away from read_module_errors being just a set
% of read_module_error values, we used to not include these errors
% in the set at all, which could lead to an empty set of
% read_module_error values being paired with a nonempty list
% of severity_error error_specs.
% This type represents the set of errors that were encountered
% during an attempt to read in a Mercury module's source file,
% interface file, or optimization file.
%
% There are two kinds of tests that code will typically perform
% on values of this type.
%
% 1. Are there any errors?
% 2. Are there any FATAL errors?
%
% You can use there_are_no_errors and there_are_some_errors for
% the first kind of test, and a direct invocation of an emptiness test
% on the rm_fatal_errors field for the second kind.
%
:- type read_module_errors
---> read_module_errors(
% The fatal errors we have encountered, and their messages.
% All these error_specs should have severity_error.
rm_fatal_errors :: set(fatal_read_module_error),
rm_fatal_error_specs :: list(error_spec),
% The nonfatal errors we have encountered, and their messages.
% All these error_specs should have severity_error.
rm_nonfatal_errors :: set(nonfatal_read_module_error),
rm_nonfatal_error_specs :: list(error_spec),
% The warnings we have encountered. All these should have
% severity levels *below* severity_error.
rm_warning_specs :: list(error_spec)
).
%---------------------%
% Return a structure that records no errors (so far).
%
:- func init_read_module_errors = read_module_errors.
%---------------------%
% Add a fatal error, and its message.
%
:- pred add_fatal_error(fatal_read_module_error::in, list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
% Add a nonfatal error, and its message.
%
:- pred add_nonfatal_error(nonfatal_read_module_error::in,
list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
% If there are any error_specs in the input list,
% record them as representing a not-elsewhere-classified nonfatal error.
%
:- pred add_any_nec_errors(list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
% Add some warning messages.
%
:- pred add_warning(list(error_spec)::in,
read_module_errors::in, read_module_errors::out) is det.
%---------------------%
% Succeed if the read_module_errors structure records zero errors.
% (Though it may contain warnings.)
%
:- pred there_are_no_errors(read_module_errors::in) is semidet.
% Succeed if the read_module_errors structure records at least one error.
%
:- pred there_are_some_errors(read_module_errors::in) is semidet.
%---------------------%
% Return all the error_specs in the argument, regardless of severity.
%
:- func get_read_module_specs(read_module_errors) = list(error_spec).
%---------------------%
:- pred io_error_to_error_spec(string::in, error_spec::out,
io::di, io::uo) is det.
:- pred io_error_to_read_module_errors(string::in, fatal_read_module_error::in,
read_module_errors::out, io::di, io::uo) is det.
%---------------------------------------------------------------------------%
:- implementation.
%---------------------------------------------------------------------------%
init_read_module_errors = read_module_errors(set.init, [], set.init, [], []).
%---------------------%
add_fatal_error(Error, Specs, Errors0, Errors) :-
Errors0 = read_module_errors(FatalErrors0, FatalSpecs0,
NonFatalErrors, NonFatalSpecs, WarningSpecs),
set.insert(Error, FatalErrors0, FatalErrors),
FatalSpecs = Specs ++ FatalSpecs0,
Errors = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs).
add_nonfatal_error(Error, Specs, Errors0, Errors) :-
Errors0 = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors0, NonFatalSpecs0, WarningSpecs),
set.insert(Error, NonFatalErrors0, NonFatalErrors),
NonFatalSpecs = Specs ++ NonFatalSpecs0,
Errors = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs).
add_any_nec_errors(Specs, !Errors) :-
(
Specs = []
;
Specs = [_ | _],
add_nonfatal_error(rme_nec, Specs, !Errors)
).
add_warning(Specs, Errors0, Errors) :-
Errors0 = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs0),
WarningSpecs = Specs ++ WarningSpecs0,
Errors = read_module_errors(FatalErrors, FatalSpecs,
NonFatalErrors, NonFatalSpecs, WarningSpecs).
%---------------------%
there_are_no_errors(Errors) :-
Errors = read_module_errors(FatalErrors, _, NonFatalErrors, _, _),
set.is_empty(FatalErrors),
set.is_empty(NonFatalErrors).
there_are_some_errors(Errors) :-
Errors = read_module_errors(FatalErrors, _, NonFatalErrors, _, _),
( set.is_non_empty(FatalErrors)
; set.is_non_empty(NonFatalErrors)
).
%---------------------%
get_read_module_specs(Errors) = Specs :-
Errors = read_module_errors(_FatalErrors, FatalSpecs,
_NonFatalErrors, NonFatalSpecs, WarningSpecs),
Specs = FatalSpecs ++ NonFatalSpecs ++ WarningSpecs.
%---------------------%
io_error_to_error_spec(ErrorMsg, Spec, !IO) :-
io.progname_base("mercury_compile", ProgName, !IO),
Pieces = [fixed(ProgName), suffix(":"), words(ErrorMsg), nl],
Spec = simplest_no_context_spec($pred, severity_error, phase_read_files,
Pieces).
io_error_to_read_module_errors(ErrorMsg, FatalError, Errors, !IO) :-
io_error_to_error_spec(ErrorMsg, Spec, !IO),
Errors0 = init_read_module_errors,
add_fatal_error(FatalError, [Spec], Errors0, Errors).
%---------------------------------------------------------------------------%
:- end_module parse_tree.parse_error.
%---------------------------------------------------------------------------%