9 Commits

Author SHA1 Message Date
Zoltan Somogyi
a14544b7e7 Rationalize the code expanding @file cmd line args.
compiler/mercury_compile_args.m:
    Instead of writing out any error messages (and setting the exit status)
    in different ways in different circumstances, return all error messages
    to our caller, as error_specs, and the option_table needed to print
    those error_specs out.

    Simplify the code that actually does the expansion of @file arguments,
    both by invoking higher level primitives than were available when
    the original code was written, and by avoiding repeatedly putting
    arguments info ok/1 wrappers and taking them out again.

    Change the argument list of setup_all_args to delete the ErrorStream
    argument and pass only ProgressStream, because its only caller always
    passes the *same* stream as both arguments.

compiler/mercury_compile_main.m:
    Print the error messages that mercury_compile_args.m now returns.
    Standardize on printing "mmc:" before those messages to identify
    the program reporting those errors. (The old code could print any
    one of "mercury_compile:", "mmc:", the name of the executable,
    or nothing.)

compiler/file_util.m:
    Add two utility functions for the new code in mercury_compile_args.m.

compiler/handle_options.m:
    Add an XXX.

tests/invalid/invalid_mllibs.err_exp:
tests/invalid_make_int/bad_color.int_err_exp:
tests/invalid_options_file/inf_incl_direct.err_exp:
tests/invalid_options_file/inf_incl_indirect.err_exp:
tests/invalid_options_file/no_assign.err_exp:
tests/invalid_options_file/no_var.err_exp:
tests/invalid_options_file/nonexistent_file.err_exp:
tests/invalid_options_file/undefined_var.err_exp:
tests/invalid_options_file/unterminated_string.err_exp:
tests/invalid_options_file/unterminated_var.err_exp:
    Consistently expect the "mmc:" prefix before the error messages
    now printed by mercury_compile_main.m.
2026-04-03 07:06:46 +11:00
Zoltan Somogyi
e84eae190f Improve diagnostics about options files.
compiler/options_file.m:
    Include the word "error" in two error messages.

tests/invalid_options_file/Mmakefile:
    Document why enabling color in this test directory would not work.

tests/invalid_options_file/no_assign.err_exp:
tests/invalid_options_file/nonexistent_file.err_exp:
    Expect updated diagnostics.
2024-06-13 16:43:27 +10:00
Zoltan Somogyi
a98cca7fb3 Fix case error in an error message.
compiler/options_file.m:
    As above. Also make the error message conform to our usual standard
    scheme: expected X, got Y.

tests/invalid_options_file/no_assign.err_exp:
    Update the expected error message.
2023-07-12 03:02:59 +02:00
Julien Fischer
c16f53c0a3 Fix some test failures on Windows.
tests/invalid_options_file/inf_incl_direct.err_exp2:
tests/invalid_options_file/inf_incl_indirect.err_exp2:
tests/invalid_options_file/nonexistent_file.err_exp2:
     Handle the case where "\" is used as the directory separator.

tests/invalid_options_file/inf_incl_direct.m:
tests/invalid_options_file/inf_incl_indirect.m:
tests/invalid_options_file/nonexistent_file.m:
    Document what each of the .err_exp files corresponds to.
2023-07-08 16:49:20 +10:00
Zoltan Somogyi
adac4e462b Fix (inconsequential) bad module name. 2023-06-19 01:15:32 +02:00
Zoltan Somogyi
297dab88f8 Construct error messages using more readable code.
compiler/file_util.m:
compiler/find_module.m:
    As above.

    Also, put quotes around directory names where that wasn't done before.

tests/invalid_options_file/nonexistent_file.err_exp:
    Expect quotes around a directory name.
2022-04-29 04:40:09 +10:00
Zoltan Somogyi
adf6c55847 Shut up mmake actions for check_namespace.
This reduces the size of the output of tools/bootcheck by 3700+ lines,
or about 25%.

Mmake.common.in:
    Don't print the actions implementing namespace cleanliness checks.
    To allow the attribution of any violations of the namespace rules,
    print the name of the C module before any list of detected
    nonallowed symbols.

    To avoid mmake printing the actions for creating the .o files
    that some of the check_namespace actions later check, rename
    the affected object files .pseudo_o files, so that we can specify
    a rule for them that is a copy of the rule for .o files, differing
    only in not printing the compilation command.

    Mark the files involved in check_namespace actions as dependencies
    of .SECONDARY, which means that mmake does not automatically delete them
    after building them as intermediate files. The reason for this is that
    there is no way to tell make to delete intermediate files *silently*,
    i.e. without writing out the rm command that deletes them.
    To make up for this, tools/bootcheck now cleans up each directory
    immediately after "mmake check_namespace" with "mmake clean_check",
    which invokes mmake rules that do not print the rm commands.

    This change does have the effect that these intermediate files *will*
    hang around if the check_namespace target is every invoked manually.
    However,

    - we just about never run check_namespace in a directory manually, and
    - when we do, a simple "mmake clean_check" will do the required cleanup.

scripts/Mmake.rules:
    Move the vim tag line to its usual place at the top.

    Replace old-school rules such as .m.err with their modern equivalents
    (such as %.err: %.m).

scripts/Mmakefile:
    Instead of printing the rules that make test_mdbrc, print only a
    "making test_mdbrc" message.

runtime/Mmakefile:
    Conform to the change of the name of a make variable in Mmake.common.in.

ssdb/Mmakefile:
    Fix an old bug that something else in this diff tickled: make the
    .depend target of each main module depend on SSDB_FLAGS, *not* just
    the phony general "depend" target. This was a bug because tools/bootcheck

    - copied across to stage 2 ONLY SSDB_FLAGS.in, and NOT SSDB_FLAGS,

    - did NOT explicitly make SSDB_FLAGS from SSDB_FLAGS.in, even though
      pretty much invocations of the Mercury compiler in this directory
      have "--flags SSDB_FLAGS" as an implicit argument, and then

    - built dependencies in the ssdb directory by invoking the top
      Mmakefile's dep_ssdb target, which (indirectly) invokes
      $(SSDB_LIB_NAME).depend.

    Due to all the above, I don't actually know how tools/bootcheck
    could ever build stage2/ssdb until now :-(

tools/bootcheck:
    Invoke "mmake clean_check" after each "mmake check_namespace".

    Change the code that explicitly builds the directory-specific
    X_FLAGS file in each directory (which is invoked only when using
    mmc --make) to actually build all such files, when previously
    it built only a subset.

tests/invalid/Mmakefile:
tests/invalid_nodepend/Mmakefile:
tests/invalid_onlydepend/Mmakefile:
tests/invalid_options_file/Mmakefile:
tests/invalid_purity/Mmakefile:
tests/invalid_submodules/Mmakefile:
tests/stm/Mmakefile:
    Fix an unintended consequence of replacing the .m.err rule in
    scripts/Mmake.rules with %.err: %.m, which is that the %.err: %.m
    rules in these mmakefiles became ineffective, because they appear
    in the makefile we construct *after* the rule in scripts/Mmake.rules,
    which specify a different action (the rules here return a nonzero
    status in the *absence* of failure, which would be ridiculous
    for the rule in scripts/Mmake.rules). Apparently, the %.err: %.m rules
    overrode the rule in scripts/Mmake.rules while it had the old form,
    but do not do so now it has the new form.

    The fix is to make replace all the "%.err: %.m" rules in these Mmakefiles
    with "$(PROGS:%=%.err): %.err: %.m" rules, which specify that they
    override the generic rule for the .err files of the test cases
    in each directory.

    In invalid_purity/Mmakefile, fix a bug: -nodepend suffixes make sense
    in only in the name of a *test*, not the name of a *program*, so
    move such a suffix from a program name to a test name. Without this,
    the program's .err file would be included in the list of .err files
    to which the ".err: .m" rule applies under the wrong name.

    In invalid_submodules/Mmakefile, fix the misleading names of some
    make variables, and fix a misspelt directory name.

    Standardize on "$(PROGS:%=%.err)" notation, replacing earlier instances
    of "$(addsuffix .err,$(PROGS))". The reason for this is that when I tried
    using "$addsuffix .int_err,$(PROGS))" in tests/invalid/invalid_make_int,
    it did not work. (A google search on "gnu make addsuffix" did not yield
    any clues as to why. Maybe you can only add suffixes that do not contain
    underscores?)
2022-01-24 17:38:35 +11:00
Zoltan Somogyi
1676d74e87 diff --git a/compiler/options_file.m b/compiler/options_file.m
index f3a6ee999..e9675c97d 100644
--- a/compiler/options_file.m
+++ b/compiler/options_file.m
@@ -130,12 +130,14 @@
 :- import_module parse_tree.
 :- import_module parse_tree.error_util.

+:- import_module assoc_list.
 :- import_module bool.
 :- import_module char.
 :- import_module dir.
 :- import_module int.
 :- import_module one_or_more.
 :- import_module map.
+:- import_module pair.
 :- import_module require.
 :- import_module std_util.
 :- import_module string.
@@ -195,17 +197,15 @@ read_options_file_set_params(OptionSearchDirs, OptionsFile,
         IsOptionsFileOptional = options_file_must_exist
     ),
     SearchInfo = search_info(MaybeDirName, MaybeSearch),
-    MaybeContext = no,
-    read_options_file_params(SearchInfo, MaybeContext, IsOptionsFileOptional,
+    read_options_file_params(SearchInfo, pre_stack_base, IsOptionsFileOptional,
         OptionsFile, !Variables, !IOSpecs, !ParseSpecs, !UndefSpecs, !IO).

 %---------------------%

 read_named_options_file(OptionsPathName, !Variables, Specs, UndefSpecs, !IO) :-
     SearchInfo = search_info(no, no_search),
-    MaybeContext = no,
-    read_options_file_params(SearchInfo, MaybeContext, options_file_must_exist,
-        OptionsPathName, !Variables,
+    read_options_file_params(SearchInfo, pre_stack_base,
+        options_file_must_exist, OptionsPathName, !Variables,
         [], IOSpecs, [], ParseSpecs, [], UndefSpecs, !IO),
     Specs = IOSpecs ++ ParseSpecs.

@@ -261,25 +261,63 @@ read_args_file(OptionsFile, MaybeMCFlags, Specs, UndefSpecs, !IO) :-
     --->    options_file_need_not_exist
     ;       options_file_must_exist.

+    % The inclusion stack records, for the options file being processed,
+    % which other options files, if any, contained the include directives
+    % that lead to it being read. We use it to detect circular inclusions.
+:- type incl_stack
+    --->    incl_stack_base(
+                % The file named here is either read automatically by
+                % the compiler (e.g. Mercury.options) or its reading
+                % was requested by the user via an --options-file
+                % compiler option.
+                file_name
+            )
+    ;       incl_stack_nested(
+                % We read the file named here in response to an "include"
+                % directive.
+                file_name,
+
+                % The context of that include directive.
+                term.context,
+
+                % The "provenance" of the file that contains that include
+                % directive.
+                incl_stack
+            ).
+
+    % The pre_incl_stack is a version of the incl_stack *before* file_util.m
+    % finds the full pathname of a possibly-searched-for options file for us.
+:- type pre_incl_stack
+    --->    pre_stack_base
+    ;       pre_stack_nested(term.context, incl_stack).
+
 :- pred read_options_file_params(search_info::in,
-    maybe(term.context)::in, is_options_file_optional::in,
+    pre_incl_stack::in, is_options_file_optional::in,
     string::in, options_variables::in, options_variables::out,
     list(error_spec)::in, list(error_spec)::out,
     list(error_spec)::in, list(error_spec)::out,
     list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det.

-read_options_file_params(SearchInfo, MaybeContext, IsOptionsFileOptional,
+read_options_file_params(SearchInfo, PreStack0, IsOptionsFileOptional,
         OptionsPathName, !Variables,
         !IOSpecs, !ParseSpecs, !UndefSpecs, !IO) :-
     ( if OptionsPathName = "-" then
-        % Read from standard input.
-        trace [compiletime(flag("options_file_debug")), io(!TIO)] (
-            io.write_string("Reading options file from stdin...", !TIO)
-        ),
-        SearchInfo = search_info(_MaybeDirName, Search),
-        SubSearchInfo = search_info(yes(dir.this_directory), Search),
-        read_options_lines(SubSearchInfo, io.stdin_stream, "stdin", 1,
-            !Variables, !IOSpecs, !ParseSpecs, !UndefSpecs, !IO)
+        check_include_for_infinite_recursion(PreStack0, "-", CheckResult),
+        (
+            CheckResult = include_ok(InclStack0),
+            % Read from standard input.
+            trace [compiletime(flag("options_file_debug")), io(!TIO)] (
+                io.write_string("Reading options file from stdin...", !TIO)
+            ),
+            SearchInfo = search_info(_MaybeDirName, Search),
+            SubSearchInfo = search_info(yes(dir.this_directory), Search),
+            read_options_lines(SubSearchInfo, InclStack0,
+                io.stdin_stream, "stdin", 1, !Variables,
+                !IOSpecs, !ParseSpecs, !UndefSpecs, !IO)
+        ;
+            CheckResult = include_error(CheckSpec),
+            !:ParseSpecs = [CheckSpec | !.ParseSpecs]
+        )
     else
         trace [compiletime(flag("options_file_debug")), io(!TIO)] (
             io.format("Searching for options file %s",
@@ -329,22 +367,31 @@ read_options_file_params(SearchInfo, MaybeContext, IsOptionsFileOptional,
         (
             MaybeDirAndStream =
                 ok(path_name_and_stream(FoundDir, FoundStream)),
-            trace [compiletime(flag("options_file_debug")), io(!TIO)] (
-                io.format("Reading options file %s",
-                    [s(FoundDir/FileToFind)], !TIO)
-            ),
+            check_include_for_infinite_recursion(PreStack0,
+                FoundDir / FileToFind, CheckResult),
+            (
+                CheckResult = include_ok(InclStack0),
+                trace [compiletime(flag("options_file_debug")), io(!TIO)] (
+                    io.format("Reading options file %s",
+                        [s(FoundDir/FileToFind)], !TIO)
+                ),

-            % XXX Instead of setting and unsetting the input stream,
-            % we should simply pass FoundStream to read_options_lines.
-            % However, when I (zs) tried that, I quickly found that
-            % the call tree of read_options_lines includes many predicates
-            % for which it is not at all clear whether they *intend*
-            % to read from a current standard input that originates as
-            % FoundStream, or they just *happen* to do so.
-
-            SubSearchInfo = search_info(yes(FoundDir), Search),
-            read_options_lines(SubSearchInfo, FoundStream, FileToFind, 1,
-                !Variables, !IOSpecs, !ParseSpecs, !UndefSpecs, !IO),
+                % XXX Instead of setting and unsetting the input stream,
+                % we should simply pass FoundStream to read_options_lines.
+                % However, when I (zs) tried that, I quickly found that
+                % the call tree of read_options_lines includes many predicates
+                % for which it is not at all clear whether they *intend*
+                % to read from a current standard input that originates as
+                % FoundStream, or they just *happen* to do so.
+
+                SubSearchInfo = search_info(yes(FoundDir), Search),
+                read_options_lines(SubSearchInfo, InclStack0,
+                    FoundStream, FileToFind, 1, !Variables,
+                    !IOSpecs, !ParseSpecs, !UndefSpecs, !IO)
+            ;
+                CheckResult = include_error(CheckSpec),
+                !:ParseSpecs = [CheckSpec | !.ParseSpecs]
+            ),
             io.close_input(FoundStream, !IO)
         ;
             MaybeDirAndStream = error(Error),
@@ -359,6 +406,13 @@ read_options_file_params(SearchInfo, MaybeContext, IsOptionsFileOptional,
                 else
                     ErrorFile = FileToFind
                 ),
+                (
+                    PreStack0 = pre_stack_base,
+                    MaybeContext = no
+                ;
+                    PreStack0 = pre_stack_nested(Context, _),
+                    MaybeContext = yes(Context)
+                ),
                 Spec = error_spec($pred, severity_error, phase_read_files,
                     [error_msg(MaybeContext, treat_as_first, 0,
                         [always([words("Cannot open options file"),
@@ -374,6 +428,99 @@ read_options_file_params(SearchInfo, MaybeContext, IsOptionsFileOptional,
         io.write_string("done.\n", !TIO)
     ).

+%---------------------%
+
+:- type include_check_result
+    --->    include_ok(incl_stack)
+    ;       include_error(error_spec).
+
+:- pred check_include_for_infinite_recursion(pre_incl_stack::in,
+    file_name::in, include_check_result::out) is det.
+
+check_include_for_infinite_recursion(PreStack0, PathName, Result) :-
+    (
+        PreStack0 = pre_stack_base,
+        InclStack = incl_stack_base(PathName),
+        Result = include_ok(InclStack)
+    ;
+        PreStack0 = pre_stack_nested(Context, InclStack0),
+        ( if
+            pathname_occurs_in_incl_stack(InclStack0, PathName, Context, Spec)
+        then
+            Result = include_error(Spec)
+        else
+            InclStack = incl_stack_nested(PathName, Context, InclStack0),
+            Result = include_ok(InclStack)
+        )
+    ).
+
+:- pred pathname_occurs_in_incl_stack(incl_stack::in, file_name::in,
+    term.context::in, error_spec::out) is semidet.
+
+pathname_occurs_in_incl_stack(InclStack0, PathName, Context, Spec) :-
+    (
+        InclStack0 = incl_stack_base(StackPathName0),
+        ( if PathName = StackPathName0 then
+            Pieces = [words("Error: options file"), quote(PathName),
+                words("includes itself."), nl],
+            Spec = simplest_spec($pred, severity_error, phase_read_files,
+                Context, Pieces)
+        else
+            fail
+        )
+    ;
+        InclStack0 = incl_stack_nested(StackPathName0, Context0, InclStack1),
+        ( if PathName = StackPathName0 then
+            Pieces = [words("Error: options file"), quote(PathName),
+                words("includes itself."), nl],
+            Spec = simplest_spec($pred, severity_error, phase_read_files,
+                Context, Pieces)
+        else
+            ( if
+                pathname_occurs_in_incl_stack_2(InclStack1, PathName,
+                    [StackPathName0 - Context0], TopDownIncludes)
+            then
+                TopPathName - TopContext = list.det_head(TopDownIncludes),
+                MainPieces = [words("Error: options file"), quote(TopPathName),
+                    words("indirectly includes itself through"),
+                    words("the following chain of include directives."), nl],
+                MainMsg = simplest_msg(TopContext, MainPieces),
+                InclMsgs = list.map(include_context_msg, TopDownIncludes),
+                LastMsg = include_context_msg(PathName - Context),
+                Spec = error_spec($pred, severity_error, phase_read_files,
+                    [MainMsg | InclMsgs] ++ [LastMsg])
+            else
+                fail
+            )
+        )
+    ).
+
+:- pred pathname_occurs_in_incl_stack_2(incl_stack::in, file_name::in,
+    assoc_list(file_name, term.context)::in,
+    assoc_list(file_name, term.context)::out) is semidet.
+
+pathname_occurs_in_incl_stack_2(InclStack0, PathName, !TopDownIncludes) :-
+    (
+        InclStack0 = incl_stack_base(StackPathName0),
+        PathName = StackPathName0
+    ;
+        InclStack0 = incl_stack_nested(StackPathName0, Context0, InclStack1),
+        !:TopDownIncludes = [StackPathName0 - Context0 | !.TopDownIncludes],
+        ( if PathName = StackPathName0 then
+            true
+        else
+            pathname_occurs_in_incl_stack_2(InclStack1, PathName,
+                !TopDownIncludes)
+        )
+    ).
+
+:- func include_context_msg(pair(file_name, term.context)) = error_msg.
+
+include_context_msg(FileName - Context) = Msg :-
+    Pieces = [words("The include directive for"), quote(FileName),
+        words("here."), nl],
+    Msg = simplest_msg(Context, Pieces).
+
 %---------------------------------------------------------------------------%

 :- type maybe_is_first
@@ -387,15 +534,15 @@ read_options_file_params(SearchInfo, MaybeContext, IsOptionsFileOptional,

 %---------------------------------------------------------------------------%

-:- pred read_options_lines(search_info::in,
+:- pred read_options_lines(search_info::in, incl_stack::in,
     io.text_input_stream::in, file_name::in, int::in,
     options_variables::in, options_variables::out,
     list(error_spec)::in, list(error_spec)::out,
     list(error_spec)::in, list(error_spec)::out,
     list(error_spec)::in, list(error_spec)::out, io::di, io::uo) is det.

-read_options_lines(SearchInfo, InStream, FileName, LineNumber0, !Variables,
-        !IOSpecs, !ParseSpecs, !UndefSpecs, !IO) :-
+read_options_lines(SearchInfo, InclStack0, InStream, FileName, LineNumber0,
+        !Variables, !IOSpecs, !ParseSpecs, !UndefSpecs, !IO) :-
     read_options_line(InStream, FileName, LineNumber0, LineNumber1,
         LineResult, !IO),
     (
@@ -425,9 +572,10 @@ read_options_lines(SearchInfo, InStream, FileName, LineNumber0, !Variables,
                     (
                         MaybeIncludedFileNames = ok(IncludedFileNames),
                         Context = term.context(FileName, LineNumber0),
+                        PreStack1 = pre_stack_nested(Context, InclStack0),
                         list.foldl5(
                             read_options_file_params(SearchInfo,
-                                yes(Context), IsOptionsFileOptional),
+                                PreStack1, IsOptionsFileOptional),
                             IncludedFileNames, !Variables,
                             !IOSpecs, !ParseSpecs, !UndefSpecs, !IO)
                     ;
@@ -443,8 +591,9 @@ read_options_lines(SearchInfo, InStream, FileName, LineNumber0, !Variables,
             )
         ),
         LineNumber2 = LineNumber1 + 1,
-        read_options_lines(SearchInfo, InStream, FileName, LineNumber2,
-            !Variables, !IOSpecs, !ParseSpecs, !UndefSpecs, !IO)
+        read_options_lines(SearchInfo, InclStack0, InStream,
+            FileName, LineNumber2, !Variables,
+            !IOSpecs, !ParseSpecs, !UndefSpecs, !IO)
     ;
         LineResult = pr_error(Spec),
         !:IOSpecs = [Spec | !.IOSpecs]
diff --git a/tests/invalid_options_file/Mmakefile b/tests/invalid_options_file/Mmakefile
index 3642a03cc..b6965ae16 100644
--- a/tests/invalid_options_file/Mmakefile
+++ b/tests/invalid_options_file/Mmakefile
@@ -7,6 +7,8 @@ THIS_DIR = invalid_options_file
 MAYBE_J1 =

 PROGS = \
+	inf_incl_direct \
+	inf_incl_indirect \
 	no_assign \
 	no_var \
 	nonexistent_file \
diff --git a/tests/invalid_options_file/inf_incl_direct.err_exp b/tests/invalid_options_file/inf_incl_direct.err_exp
index e69de29bb..4466b9dee 100644
--- a/tests/invalid_options_file/inf_incl_direct.err_exp
+++ b/tests/invalid_options_file/inf_incl_direct.err_exp
@@ -0,0 +1,3 @@
+inf_incl_direct.options_file:002: Error: options file
+inf_incl_direct.options_file:002:   `./inf_incl_direct.options_file' includes
+inf_incl_direct.options_file:002:   itself.
diff --git a/tests/invalid_options_file/inf_incl_direct.m b/tests/invalid_options_file/inf_incl_direct.m
index e69de29bb..64ae69ad8 100644
--- a/tests/invalid_options_file/inf_incl_direct.m
+++ b/tests/invalid_options_file/inf_incl_direct.m
@@ -0,0 +1,16 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+
+:- module infinite_include_direct.
+
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+:- implementation.
+
+main(!IO) :-
+    io.write_string("Hello, world.\n", !IO).
diff --git a/tests/invalid_options_file/inf_incl_direct.options_file b/tests/invalid_options_file/inf_incl_direct.options_file
index e69de29bb..432f30bd1 100644
--- a/tests/invalid_options_file/inf_incl_direct.options_file
+++ b/tests/invalid_options_file/inf_incl_direct.options_file
@@ -0,0 +1,2 @@
+MCFLAGS += -V
+include inf_incl_direct.options_file
diff --git a/tests/invalid_options_file/inf_incl_indirect.err_exp b/tests/invalid_options_file/inf_incl_indirect.err_exp
index e69de29bb..88527df26 100644
--- a/tests/invalid_options_file/inf_incl_indirect.err_exp
+++ b/tests/invalid_options_file/inf_incl_indirect.err_exp
@@ -0,0 +1,23 @@
+inf_incl_indirect.options_file:002: Error: options file
+inf_incl_indirect.options_file:002:   `./inf_incl_indirect.options_file_a'
+inf_incl_indirect.options_file:002:   indirectly includes itself through the
+inf_incl_indirect.options_file:002:   following chain of include directives.
+inf_incl_indirect.options_file:002:   The include directive for
+inf_incl_indirect.options_file:002:   `./inf_incl_indirect.options_file_a'
+inf_incl_indirect.options_file:002:   here.
+inf_incl_indirect.options_file_a:002:   The include directive for
+inf_incl_indirect.options_file_a:002:   `./inf_incl_indirect.options_file_b'
+inf_incl_indirect.options_file_a:002:   here.
+inf_incl_indirect.options_file_b:002:   The include directive for
+inf_incl_indirect.options_file_b:002:   `./inf_incl_indirect.options_file'
+inf_incl_indirect.options_file_b:002:   here.
+inf_incl_indirect.options_file:003: Error: options file
+inf_incl_indirect.options_file:003:   `./inf_incl_indirect.options_file_b'
+inf_incl_indirect.options_file:003:   indirectly includes itself through the
+inf_incl_indirect.options_file:003:   following chain of include directives.
+inf_incl_indirect.options_file:003:   The include directive for
+inf_incl_indirect.options_file:003:   `./inf_incl_indirect.options_file_b'
+inf_incl_indirect.options_file:003:   here.
+inf_incl_indirect.options_file_b:002:   The include directive for
+inf_incl_indirect.options_file_b:002:   `./inf_incl_indirect.options_file'
+inf_incl_indirect.options_file_b:002:   here.
diff --git a/tests/invalid_options_file/inf_incl_indirect.m b/tests/invalid_options_file/inf_incl_indirect.m
index e69de29bb..64ae69ad8 100644
--- a/tests/invalid_options_file/inf_incl_indirect.m
+++ b/tests/invalid_options_file/inf_incl_indirect.m
@@ -0,0 +1,16 @@
+%---------------------------------------------------------------------------%
+% vim: ts=4 sw=4 et ft=mercury
+%---------------------------------------------------------------------------%
+
+:- module infinite_include_direct.
+
+:- interface.
+
+:- import_module io.
+
+:- pred main(io::di, io::uo) is det.
+
+:- implementation.
+
+main(!IO) :-
+    io.write_string("Hello, world.\n", !IO).
diff --git a/tests/invalid_options_file/inf_incl_indirect.options_file b/tests/invalid_options_file/inf_incl_indirect.options_file
index e69de29bb..a3a3ec858 100644
--- a/tests/invalid_options_file/inf_incl_indirect.options_file
+++ b/tests/invalid_options_file/inf_incl_indirect.options_file
@@ -0,0 +1,3 @@
+MCFLAGS += -V
+include inf_incl_indirect.options_file_a
+include inf_incl_indirect.options_file_b
diff --git a/tests/invalid_options_file/inf_incl_indirect.options_file_a b/tests/invalid_options_file/inf_incl_indirect.options_file_a
index e69de29bb..bc0583dc9 100644
--- a/tests/invalid_options_file/inf_incl_indirect.options_file_a
+++ b/tests/invalid_options_file/inf_incl_indirect.options_file_a
@@ -0,0 +1,2 @@
+MCFLAGS += -A
+include inf_incl_indirect.options_file_b
diff --git a/tests/invalid_options_file/inf_incl_indirect.options_file_b b/tests/invalid_options_file/inf_incl_indirect.options_file_b
index e69de29bb..e7b843ab1 100644
--- a/tests/invalid_options_file/inf_incl_indirect.options_file_b
+++ b/tests/invalid_options_file/inf_incl_indirect.options_file_b
@@ -0,0 +1,2 @@
+MCFLAGS += -B
+include inf_incl_indirect.options_file
2020-06-13 00:26:57 +10:00
Zoltan Somogyi
ac50b3cbd1 Do not use exceptions in options_file.m.
And add tests for how the compiler handles both valid and invalid
options files.

compiler/options_file.m:
    This diff rewrites options_file.m in a straightforward, direct style that
    returns indications of errors as error_specs rather than as exceptions.
    A recent diff started on this task; this diff finishes it.

    The new approach has several advantages.

    - The control flow is much simpler, and therefore more understandable.
      Correctness arguments for propositions such as "this code closes
      all the file streams that it opens" are now much simpler to make.

    - We now report errors using error_specs, which contain context
      information, while previously, each error was described only
      by a string, without context info.

    - Once we detect and report one error, we can continue to read the
      rest of the input. This allows a single compiler invocation to find
      and report several errors, not just the first.

    - Since we now return the gathered set of error_specs instead of printing
      them, the predicates of this file don't have to take globals structures
      as arguments, which allows our callers to avoid constructing those
      structures.

    - Deep profiling, which cannot handle exceptions, now works on
      the code of this module.

    Change over to using trace goals for debugging prints, since continuing
    to use debug_make_msg would require a globals structure.

    Add an XXX on a likely bug.

    Add a mechanism for writing out a database of variable names and values.

compiler/mercury_compile_main.m:
    Conform to the changes in options_file.m. Document where exactly
    we could avoid constructing a globals just for options_file.m.

    If the right option is given, get options_file to write out the database
    of variable names and values it has just read in, to enable the
    functionality of this module to be tested.

compiler/options.m:
doc/user_guide.texi:
    Add a new developer option, --dump-options-file, to control the above.

compiler/make.build.m:
compiler/make.m:
compiler/make.program_target.m:
    Conform to the changes in options_file.m.

compiler/file_util.m:
    Fix an error message.

tests/Mmakefile:
tools/bootcheck:
    List options_file and invalid_options_file as two new test directories.

    Fix a command in bootcheck.

tests/options_file/Mmakefile:
    Add a mechanism for testing whether options_file.m builds mapping
    from make variable names to values that we expect.

tests/Mmake.common:
    Provide a mechanism for comparing dumped options_files against
    their expected contents, for use by tests/invalid_options_file/Mmakefile.

    Fix a comment.

tests/options_file/basic_test.m:
tests/options_file/basic_test.optfile_exp:
tests/options_file/basic_test.options_file:
tests/options_file/basic_test.options_file.sub0:
tests/options_file/basic_test.options_file.sub1:
    A simple test case for exercising all the usual options_file constructs.

tests/invalid_options_file/Mmakefile:
    Add a mechanism for testing whether options_file.m generates
    the error messages we expect for various kinds of errors in options files.

tests/invalid_options_file/no_assign.{m,options_file,err_exp}:
tests/invalid_options_file/no_var.{m,options_file,err_exp}:
tests/invalid_options_file/nonexistent_file.{m,options_file,err_exp}:
tests/invalid_options_file/undefined_var.{m,options_file,err_exp}:
tests/invalid_options_file/unterminated_string.{m,options_file,err_exp}:
tests/invalid_options_file/unterminated_var.{m,options_file,err_exp}:
    Six test cases to test six different kinds of errors that can be
    detected by options_file.m.
2020-06-12 04:14:00 +10:00