[m-rev.] for review: improve error messages for missing foreign values in foreign_enum pragmas

Julien Fischer jfischer at opturion.com
Tue Jun 7 11:41:46 AEST 2016


For review by anyone.

Improve error messages for missing foreign values in foreign_enum pragmas.

Currently, if a foreign_enum pragma is missing cases in the constructor -
foreign value mapping we report that there are some missing foreign values but
do not list the affected constructors unless --verbose-error-messages is
enabled.  For cases where there are a small number of missing foreign values
this is a bit irritating as you need to recompile to find out what they are.
Change the compiler so the error messsage lists upto the first 10 constructors
that do not have a foreign value.
(If --verbose-error-messages is enabled then we list all of them as before.)

compiler/add_foreign_enum.m:
     Make the above change.

     In error messages for foreign_enum and foreign_export_enum pragmas, add
     the word "type" before the name that is printed in error messages.

tests/invalid/Mmakefile:
tests/invalid/Mercury.options:
tests/fe_unmapped_nonverbose.{m,err_exp}:
tests/fe_napped_verbose.{m,err_exp}:
     Test the new error messages for missing constructors in the constructor -
     foreign value mapping.

tests/invalid/ee_invalid.err_exp:
tests/invalid/foreign_enum_import.err_exp:
tests/invalid/foreign_enum_invalid.err_exp:
     Conform to the above changes.

Julien.

diff --git a/compiler/add_foreign_enum.m b/compiler/add_foreign_enum.m
index 0c61d65..e078406 100644
--- a/compiler/add_foreign_enum.m
+++ b/compiler/add_foreign_enum.m
@@ -48,7 +48,7 @@ add_pragma_foreign_export_enum(FEEInfo, _TypeStatus, Context,
      TypeCtor = type_ctor(TypeName, TypeArity),
      module_info_get_type_table(!.ModuleInfo, TypeTable),
      ContextPieces = [words("In"), pragma_decl("foreign_export_enum"),
-        words("declaration for"),
+        words("declaration for type"),
          sym_name_and_arity(sym_name_arity(TypeName, TypeArity)),
          suffix(":"), nl],
      ( if
@@ -370,7 +370,7 @@ add_pragma_foreign_enum(FEInfo, PragmaStatus, Context, !ModuleInfo, !Specs) :-
      TypeCtor = type_ctor(TypeName, TypeArity),
      module_info_get_type_table(!.ModuleInfo, TypeTable0),
      ContextPieces = [words("In"), pragma_decl("foreign_enum"),
-        words("declaration for"),
+        words("declaration for type"),
          sym_name_and_arity(sym_name_arity(TypeName, TypeArity)),
          suffix(":"), nl],
      ( if
@@ -608,28 +608,54 @@ make_foreign_tag(ForeignLanguage, ForeignTagMap, ConsId, _, !ConsTagValues,
          !:UnmappedCtors = [ConsSymName | !.UnmappedCtors]
      ).

+%-----------------------------------------------------------------------------%
+
  :- pred add_foreign_enum_unmapped_ctors_error(prog_context::in,
      list(format_component)::in,
      list(sym_name)::in(non_empty_list),
      list(error_spec)::in, list(error_spec)::out) is det.

-add_foreign_enum_unmapped_ctors_error(Context, ContextPieces, UnmappedCtors0,
+add_foreign_enum_unmapped_ctors_error(Context, ContextPieces, Ctors0,
          !Specs) :-
-    ErrorPieces = [words("error: not all constructors have a foreign value.")],
-    list.sort(UnmappedCtors0, UnmappedCtors),
-    CtorComponents = list.map((func(S) = [sym_name(S)]), UnmappedCtors),
-    CtorList = component_list_to_line_pieces(CtorComponents, [nl]),
-    DoOrDoes = choose_number(UnmappedCtors,
-        "constructor does not have a foreign value",
-        "constructors do not have foreign values"),
-    VerboseErrorPieces = [words("The following"), words(DoOrDoes),
-        nl_indent_delta(2)] ++ CtorList,
-    Msg = simple_msg(Context,
-        [always(ContextPieces ++ ErrorPieces),
-        verbose_only(verbose_always, VerboseErrorPieces)]),
+    list.sort(Ctors0, Ctors),
+    list.split_upto(10, Ctors, CtorsStart, CtorsEnd),
+    DoOrDoes = choose_number(Ctors, "constructor does", "constructors do"),
+    PrefixPieces = ContextPieces ++ [
+        words("error: the following"), words(DoOrDoes),
+        words("not have a foreign value:"),
+        nl_indent_delta(2)
+    ],
+    StartListPieces = ctors_to_line_pieces(CtorsStart),
+    (
+        CtorsEnd = [],
+        MsgComponents = [always(PrefixPieces ++ StartListPieces)]
+    ;
+        CtorsEnd = [_ | _],
+        AllListPieces = ctors_to_line_pieces(Ctors),
+        VerbosePieces= PrefixPieces ++ AllListPieces,
+        list.length(CtorsEnd, NumEndCtors),
+        NonVerbosePieces = PrefixPieces ++ StartListPieces ++
+            [nl, fixed("..."), words("plus"), int_fixed(NumEndCtors),
+            words("more.")],
+        MsgComponents = [verbose_and_nonverbose(VerbosePieces,
+            NonVerbosePieces)]
+    ),
+    Msg = simple_msg(Context, MsgComponents),
      Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
      list.cons(Spec, !Specs).

+:- func ctors_to_line_pieces(list(sym_name)) = list(format_component).
+
+ctors_to_line_pieces(Ctors) = Pieces :-
+    Components = list.map(ctor_to_format_component, Ctors),
+    Pieces = component_list_to_line_pieces(Components, [nl]).
+
+:- func ctor_to_format_component(sym_name) = list(format_component).
+
+ctor_to_format_component(S) = [sym_name(unqualified(unqualify_name(S)))].
+
+%-----------------------------------------------------------------------------%
+
  :- pred add_foreign_enum_bijection_error(prog_context::in,
      format_components::in, list(error_spec)::in, list(error_spec)::out) is det.

@@ -641,6 +667,8 @@ add_foreign_enum_bijection_error(Context, ContextPieces, !Specs) :-
      Spec = error_spec(severity_error, phase_parse_tree_to_hlds, [Msg]),
      list.cons(Spec, !Specs).

+%-----------------------------------------------------------------------------%
+
  :- pred add_foreign_enum_pragma_in_interface_error(prog_context::in,
      sym_name::in, arity::in, list(error_spec)::in, list(error_spec)::out)
      is det.
diff --git a/tests/invalid/Mercury.options b/tests/invalid/Mercury.options
index 46261cb..dfb0b5c 100644
--- a/tests/invalid/Mercury.options
+++ b/tests/invalid/Mercury.options
@@ -23,6 +23,7 @@ MCFLAGS-ee_invalid              = --verbose-error-messages
  MCFLAGS-exported_mode           = --infer-all --no-intermodule-optimization
  MCFLAGS-exported_unify          = --no-intermodule-optimization
  MCFLAGS-exported_unify3         = --no-intermodule-optimization
+MCFLAGS-fe_unmapped_verbose     = --verbose-error-messages
  MCFLAGS-foreign_decl_line_number = --no-errorcheck-only --line-numbers \
                                      --line-numbers-for-c-headers \
                                      --compile-only
diff --git a/tests/invalid/Mmakefile b/tests/invalid/Mmakefile
index 39cd4aa..1dba876 100644
--- a/tests/invalid/Mmakefile
+++ b/tests/invalid/Mmakefile
@@ -334,6 +334,8 @@ TRAILED_MODULES = \

  # The following require that the back-end support the C interface
  C_INTERFACE_MODULES = \
+	fe_unmapped_nonverbose \
+	fe_unmapped_verbose \
  	foreign_decl_line_number \
  	trace_goal_env

diff --git a/tests/invalid/ee_invalid.err_exp b/tests/invalid/ee_invalid.err_exp
index 7f5a687..673ffe3 100644
--- a/tests/invalid/ee_invalid.err_exp
+++ b/tests/invalid/ee_invalid.err_exp
@@ -1,40 +1,41 @@
-ee_invalid.m:039: In `:- pragma foreign_export_enum' declaration for `int'/0:
+ee_invalid.m:039: In `:- pragma foreign_export_enum' declaration for type
+ee_invalid.m:039:   `int'/0:
  ee_invalid.m:039:   error: `int'/0 is a builtin type.
  ee_invalid.m:043: In pragma foreign_export_enum:
  ee_invalid.m:043:   error: undefined type `undefined_type'/0.
-ee_invalid.m:047: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:047: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:047:   `ee_invalid.foo'/1:
  ee_invalid.m:047:   error: `ee_invalid.foo'/1 is not an enumeration type. It
  ee_invalid.m:047:   has one or more non-zero arity constructors.
-ee_invalid.m:051: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:051: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:051:   `ee_invalid.bar'/0:
  ee_invalid.m:051:   error: `ee_invalid.bar'/0 is not an enumeration type.
-ee_invalid.m:058: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:058: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:058:   `ee_invalid.baz'/0:
  ee_invalid.m:058:   error: `ee_invalid.baz'/0 is not an enumeration type.
-ee_invalid.m:063: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:063: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:063:   `ee_invalid.alphabet'/0:
  ee_invalid.m:063:   the following constructor does not match any of the
  ee_invalid.m:063:   constructors of `ee_invalid.alphabet'/0:
  ee_invalid.m:063:   `deg'.
-ee_invalid.m:068: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:068: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:068:   `ee_invalid.alphabet'/0:
  ee_invalid.m:068:   the following constructor does not match any of the
  ee_invalid.m:068:   constructors of `ee_invalid.alphabet'/0:
  ee_invalid.m:068:   `foo.def'.
-ee_invalid.m:073: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:073: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:073:   `ee_invalid.alphabet'/0:
  ee_invalid.m:073:   error: the user-specified mapping between Mercury and
  ee_invalid.m:073:   foreign names does not form a bijection.
-ee_invalid.m:075: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:075: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:075:   `ee_invalid.alphabet'/0:
  ee_invalid.m:075:   error: the mapping between Mercury and foreign names does
  ee_invalid.m:075:   not form a bijection.
-ee_invalid.m:077: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:077: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:077:   `ee_invalid.alphabet'/0:
  ee_invalid.m:077:   error: the user-specified mapping between Mercury and
  ee_invalid.m:077:   foreign names does not form a bijection.
-ee_invalid.m:083: In `:- pragma foreign_export_enum' declaration for
+ee_invalid.m:083: In `:- pragma foreign_export_enum' declaration for type
  ee_invalid.m:083:   `ee_invalid.strange_names'/0:
  ee_invalid.m:083:   error: not all the constructors of the type
  ee_invalid.m:083:   `ee_invalid.strange_names'/0 can be converted into valid C
diff --git a/tests/invalid/fe_unmapped_nonverbose.err_exp b/tests/invalid/fe_unmapped_nonverbose.err_exp
index e69de29..d82d8f9 100644
--- a/tests/invalid/fe_unmapped_nonverbose.err_exp
+++ b/tests/invalid/fe_unmapped_nonverbose.err_exp
@@ -0,0 +1,35 @@
+fe_unmapped_nonverbose.m:073: In `:- pragma foreign_enum' declaration for type
+fe_unmapped_nonverbose.m:073:   `fe_unmapped_nonverbose.voice'/0:
+fe_unmapped_nonverbose.m:073:   error: the following constructor does not have
+fe_unmapped_nonverbose.m:073:   a foreign value:
+fe_unmapped_nonverbose.m:073:       `alto'
+fe_unmapped_nonverbose.m:079: In `:- pragma foreign_enum' declaration for type
+fe_unmapped_nonverbose.m:079:   `fe_unmapped_nonverbose.chordophone'/0:
+fe_unmapped_nonverbose.m:079:   error: the following constructors do not have a
+fe_unmapped_nonverbose.m:079:   foreign value:
+fe_unmapped_nonverbose.m:079:       `banjo',
+fe_unmapped_nonverbose.m:079:       `cello',
+fe_unmapped_nonverbose.m:079:       `double_bass',
+fe_unmapped_nonverbose.m:079:       `dulcimer',
+fe_unmapped_nonverbose.m:079:       `harp',
+fe_unmapped_nonverbose.m:079:       `lute',
+fe_unmapped_nonverbose.m:079:       `piano',
+fe_unmapped_nonverbose.m:079:       `sitar',
+fe_unmapped_nonverbose.m:079:       `ukulele',
+fe_unmapped_nonverbose.m:079:       `viola'
+fe_unmapped_nonverbose.m:085: In `:- pragma foreign_enum' declaration for type
+fe_unmapped_nonverbose.m:085:   `fe_unmapped_nonverbose.shade_of_white'/0:
+fe_unmapped_nonverbose.m:085:   error: the following constructors do not have a
+fe_unmapped_nonverbose.m:085:   foreign value:
+fe_unmapped_nonverbose.m:085:       `anitque_white',
+fe_unmapped_nonverbose.m:085:       `blond',
+fe_unmapped_nonverbose.m:085:       `cornsilk',
+fe_unmapped_nonverbose.m:085:       `cosmic_latte',
+fe_unmapped_nonverbose.m:085:       `cream',
+fe_unmapped_nonverbose.m:085:       `eggshell',
+fe_unmapped_nonverbose.m:085:       `floral_white',
+fe_unmapped_nonverbose.m:085:       `ghost_white',
+fe_unmapped_nonverbose.m:085:       `honeydew',
+fe_unmapped_nonverbose.m:085:       `isabelline'
+fe_unmapped_nonverbose.m:085:       ... plus 17 more.
+For more information, recompile with `-E'.
diff --git a/tests/invalid/fe_unmapped_nonverbose.m b/tests/invalid/fe_unmapped_nonverbose.m
index e69de29..5d4c89a 100644
--- a/tests/invalid/fe_unmapped_nonverbose.m
+++ b/tests/invalid/fe_unmapped_nonverbose.m
@@ -0,0 +1,91 @@
+%----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
+%----------------------------------------------------------------------------%
+% Test non-verbose error messages produced for missing construtors in the
+% constructor - foreign value mapping of foreign_enum pragmas.
+%----------------------------------------------------------------------------%
+
+:- module fe_unmapped_nonverbose.
+:- interface.
+
+    % Test for 1 missing foreign value.
+    %
+:- type voice
+    --->    soprano
+    ;       alto
+    ;       tenor
+    ;       bass.
+
+    % Test for > 1 and < 11 missing foreign values.
+    %
+:- type chordophone
+    --->    guitar
+    ;       violin
+    ;       lyre
+    ;       harp
+    ;       cello
+    ;       banjo
+    ;       double_bass
+    ;       dulcimer
+    ;       lute
+    ;       piano
+    ;       sitar
+    ;       ukulele
+    ;       viola.
+
+    % Test for > 10 constructors missing foreign values.
+:- type shade_of_white
+    --->    anti_flash_white
+    ;       anitque_white
+    ;       beige
+    ;       blond
+    ;       cornsilk
+    ;       cosmic_latte
+    ;       cream
+    ;       eggshell
+    ;       floral_white
+    ;       ghost_white
+    ;       honeydew
+    ;       isabelline
+    ;       ivory
+    ;       lavender_blush
+    ;       lemon_chiffon
+    ;       linen
+    ;       magnolia
+    ;       mint_cream
+    ;       navajo_white
+    ;       old_lace
+    ;       papaya_whip
+    ;       peach
+    ;       pearl
+    ;       seashell
+    ;       snow
+    ;       splashed_white
+    ;       vanilla
+    ;       white
+    ;       white_smoke.
+
+%----------------------------------------------------------------------------%
+%----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- pragma foreign_enum("C", voice/0, [
+    soprano - "1",
+    tenor - "3",
+    bass  - "4"
+]).
+
+:- pragma foreign_enum("C", chordophone/0, [
+    guitar - "0",
+    violin - "1",
+    lyre   - "2"
+]).
+
+:- pragma foreign_enum("C", shade_of_white/0, [
+    anti_flash_white - "0xF2F3F4",
+    antique_white    - "0xFAEBD7",
+    beige            - "0xF5F5DC"
+]).
+
+%----------------------------------------------------------------------------%
diff --git a/tests/invalid/fe_unmapped_verbose.err_exp b/tests/invalid/fe_unmapped_verbose.err_exp
index e69de29..0f11cb1 100644
--- a/tests/invalid/fe_unmapped_verbose.err_exp
+++ b/tests/invalid/fe_unmapped_verbose.err_exp
@@ -0,0 +1,31 @@
+fe_unmapped_verbose.m:050: In `:- pragma foreign_enum' declaration for type
+fe_unmapped_verbose.m:050:   `fe_unmapped_verbose.shade_of_white'/0:
+fe_unmapped_verbose.m:050:   error: the following constructors do not have a
+fe_unmapped_verbose.m:050:   foreign value:
+fe_unmapped_verbose.m:050:       `anitque_white',
+fe_unmapped_verbose.m:050:       `blond',
+fe_unmapped_verbose.m:050:       `cornsilk',
+fe_unmapped_verbose.m:050:       `cosmic_latte',
+fe_unmapped_verbose.m:050:       `cream',
+fe_unmapped_verbose.m:050:       `eggshell',
+fe_unmapped_verbose.m:050:       `floral_white',
+fe_unmapped_verbose.m:050:       `ghost_white',
+fe_unmapped_verbose.m:050:       `honeydew',
+fe_unmapped_verbose.m:050:       `isabelline',
+fe_unmapped_verbose.m:050:       `ivory',
+fe_unmapped_verbose.m:050:       `lavender_blush',
+fe_unmapped_verbose.m:050:       `lemon_chiffon',
+fe_unmapped_verbose.m:050:       `linen',
+fe_unmapped_verbose.m:050:       `magnolia',
+fe_unmapped_verbose.m:050:       `mint_cream',
+fe_unmapped_verbose.m:050:       `navajo_white',
+fe_unmapped_verbose.m:050:       `old_lace',
+fe_unmapped_verbose.m:050:       `papaya_whip',
+fe_unmapped_verbose.m:050:       `peach',
+fe_unmapped_verbose.m:050:       `pearl',
+fe_unmapped_verbose.m:050:       `seashell',
+fe_unmapped_verbose.m:050:       `snow',
+fe_unmapped_verbose.m:050:       `splashed_white',
+fe_unmapped_verbose.m:050:       `vanilla',
+fe_unmapped_verbose.m:050:       `white',
+fe_unmapped_verbose.m:050:       `white_smoke'
diff --git a/tests/invalid/fe_unmapped_verbose.m b/tests/invalid/fe_unmapped_verbose.m
index e69de29..30a48a9 100644
--- a/tests/invalid/fe_unmapped_verbose.m
+++ b/tests/invalid/fe_unmapped_verbose.m
@@ -0,0 +1,56 @@
+%----------------------------------------------------------------------------%
+% vim: ft=mercury ts=4 sw=4 et wm=0 tw=0
+%----------------------------------------------------------------------------%
+% Test verbose error messages produced for missing construtors in the
+% constructor - foreign value mapping of foreign_enum pragmas.
+%----------------------------------------------------------------------------%
+
+:- module fe_unmapped_verbose.
+:- interface.
+
+    % If --verbose-error-messages is enabled then all the constructors missing
+    % foreign values should be listed.
+    %
+:- type shade_of_white
+    --->    anti_flash_white
+    ;       anitque_white
+    ;       beige
+    ;       blond
+    ;       cornsilk
+    ;       cosmic_latte
+    ;       cream
+    ;       eggshell
+    ;       floral_white
+    ;       ghost_white
+    ;       honeydew
+    ;       isabelline
+    ;       ivory
+    ;       lavender_blush
+    ;       lemon_chiffon
+    ;       linen
+    ;       magnolia
+    ;       mint_cream
+    ;       navajo_white
+    ;       old_lace
+    ;       papaya_whip
+    ;       peach
+    ;       pearl
+    ;       seashell
+    ;       snow
+    ;       splashed_white
+    ;       vanilla
+    ;       white
+    ;       white_smoke.
+
+%----------------------------------------------------------------------------%
+%----------------------------------------------------------------------------%
+
+:- implementation.
+
+:- pragma foreign_enum("C", shade_of_white/0, [
+    anti_flash_white - "0xF2F3F4",
+    antique_white    - "0xFAEBD7",
+    beige            - "0xF5F5DC"
+]).
+
+%----------------------------------------------------------------------------%
diff --git a/tests/invalid/foreign_enum_import.err_exp b/tests/invalid/foreign_enum_import.err_exp
index f70035d..7237c21 100644
--- a/tests/invalid/foreign_enum_import.err_exp
+++ b/tests/invalid/foreign_enum_import.err_exp
@@ -1,4 +1,4 @@
-foreign_enum_import.m:014: In `:- pragma foreign_enum' declaration for
+foreign_enum_import.m:014: In `:- pragma foreign_enum' declaration for type
  foreign_enum_import.m:014:   `bool.bool'/0:
  foreign_enum_import.m:014:   error: `bool.bool'/0 is not defined in this
  foreign_enum_import.m:014:   module.
diff --git a/tests/invalid/foreign_enum_invalid.err_exp b/tests/invalid/foreign_enum_invalid.err_exp
index 958ddf9..46134a3 100644
--- a/tests/invalid/foreign_enum_invalid.err_exp
+++ b/tests/invalid/foreign_enum_invalid.err_exp
@@ -1,18 +1,17 @@
  foreign_enum_invalid.m:021: Error: `:- pragma foreign_enum' declaration for
  foreign_enum_invalid.m:021:   `foreign_enum_invalid.in_int'/0 in module
  foreign_enum_invalid.m:021:   interface.
-foreign_enum_invalid.m:027: In `:- pragma foreign_enum' declaration for
+foreign_enum_invalid.m:027: In `:- pragma foreign_enum' declaration for type
  foreign_enum_invalid.m:027:   `foreign_enum_invalid.incomplete'/0:
-foreign_enum_invalid.m:027:   error: not all constructors have a foreign value.
-foreign_enum_invalid.m:027:   The following constructor does not have a foreign
-foreign_enum_invalid.m:027:   value
-foreign_enum_invalid.m:027:       `foreign_enum_invalid.baz'
-foreign_enum_invalid.m:032: In `:- pragma foreign_enum' declaration for
+foreign_enum_invalid.m:027:   error: the following constructor does not have a
+foreign_enum_invalid.m:027:   foreign value:
+foreign_enum_invalid.m:027:       `baz'
+foreign_enum_invalid.m:032: In `:- pragma foreign_enum' declaration for type
  foreign_enum_invalid.m:032:   `foreign_enum_invalid.not_a_bijection'/0:
  foreign_enum_invalid.m:032:   error: the mapping between Mercury enumeration
  foreign_enum_invalid.m:032:   values and foreign values does not form a
  foreign_enum_invalid.m:032:   bijection.
-foreign_enum_invalid.m:040: In `:- pragma foreign_enum' declaration for
+foreign_enum_invalid.m:040: In `:- pragma foreign_enum' declaration for type
  foreign_enum_invalid.m:040:   `foreign_enum_invalid.dup_foreign_enum'/0:
  foreign_enum_invalid.m:040:   error: `foreign_enum_invalid.dup_foreign_enum'/0
  foreign_enum_invalid.m:040:   has multiple foreign_enum pragmas.


More information about the reviews mailing list