[m-rev.] for review: improve error reporting for foreign_type pragmas

Julien Fischer jfischer at opturion.com
Mon Mar 13 16:55:26 AEDT 2017


For review by anyone.

Improve error reporting for foreign_type pragmas.

compiler/parse_pragma.m:
      Use parse_foreign_language/4 to parse the foreign language argument of a
      foreign_type pragmas; there is no reason for foreign type pragmas to
      use separate code for parsing their first argument.

      Do not allow the foreign type descriptor for C, C# and Java foreign type
      pragmas to be an empty string.

      Do not allow the foreign type descriptor for Erlang foreign type pragmas to
      be a non-empty string.

      In the case of a repeated foreign type assertion, report *which* assertion
      is repeated in the error message.

compiler/parse_type_defn.m:
      Distinguish between when we are parsing a type definition head for an
      actual type definition and when we are parsing it as the second argument
      of a foreign_type pragma.  Currently, we do not make the distinction and
      this causes nonsensical error messages in the case of foreign_type
      pragmas (e.g. in the case where the foreign type descriptor is a type
      variable).

tests/invalid/bad_foreign_type.{m,err_exp}:
      Extend this test to cover the new error messages and also cover other
      cases that we were not previously testing.

tests/invalid/where_direct_arg2.err_exp:
      Conform to the above change.

Julien.

diff --git a/compiler/parse_pragma.m b/compiler/parse_pragma.m
index f7732bd..a3e3fb4 100644
--- a/compiler/parse_pragma.m
+++ b/compiler/parse_pragma.m
@@ -387,19 +387,12 @@ parse_pragma_foreign_type(ModuleName, VarSet, ErrorTerm, PragmaTerms,
              MaybeAssertionTerm = yes(AssertionTerm0)
          )
      then
-        ( if term_to_foreign_language(LangTerm, Language) then
-            parse_foreign_language_type(ForeignTypeTerm, VarSet, Language,
-                MaybeForeignType)
-        else
-            LangPieces = [words("Error: invalid foreign language in"),
-                pragma_decl("foreign_type"), words("declaration."), nl],
-            LangSpec = error_spec(severity_error, phase_term_to_parse_tree,
-                [simple_msg(get_term_context(LangTerm),
-                    [always(LangPieces)])]),
-            MaybeForeignType = error1([LangSpec])
-        ),
-        parse_type_defn_head(ModuleName, VarSet, MercuryTypeTerm,
-            MaybeTypeDefnHead),
+        parse_foreign_language("foreign_type", VarSet, LangTerm,
+            MaybeForeignLang),
+        parse_foreign_language_type(ForeignTypeTerm, VarSet,
+            MaybeForeignLang, MaybeForeignType),
+        parse_type_defn_head(tdhpc_foreign_type_pragma, ModuleName, VarSet,
+            MercuryTypeTerm, MaybeTypeDefnHead),
          (
              MaybeAssertionTerm = no,
              AssertionsSet = set.init,
@@ -411,6 +404,7 @@ parse_pragma_foreign_type(ModuleName, VarSet, ErrorTerm, PragmaTerms,
          ),
          Assertions = foreign_type_assertions(AssertionsSet),
          ( if
+            MaybeForeignLang = ok1(_),
              MaybeMaybeUC = ok1(MaybeUC),
              MaybeForeignType = ok1(ForeignType),
              MaybeTypeDefnHead = ok2(MercuryTypeSymName, MercuryParams),
@@ -427,6 +421,7 @@ parse_pragma_foreign_type(ModuleName, VarSet, ErrorTerm, PragmaTerms,
              Specs = get_any_errors1(MaybeMaybeUC) ++
                  get_any_errors1(MaybeForeignType) ++
                  get_any_errors2(MaybeTypeDefnHead) ++
+                get_any_errors1(MaybeForeignLang) ++
                  AssertionSpecs,
              MaybeIOM = error1(Specs)
          )
@@ -454,10 +449,13 @@ parse_foreign_type_assertions(VarSet, Term, !Assertions, !Specs) :-
              then
                  true
              else
-                TermStr = mercury_term_to_string(VarSet, print_name_only,
-                    Term),
-                Pieces = [words("Error: foreign type assertion"),
-                    quote(TermStr), words("is repeated."), nl],
+                HeadTermStr = mercury_term_to_string(VarSet, print_name_only,
+                    HeadTerm),
+                Pieces = [
+                    words("In fourth argument of"), pragma_decl("foreign_type"),
+                    words("declaration: error:"), words("foreign type assertion"),
+                    quote(HeadTermStr), words("is repeated.")
+                ],
                  Spec = error_spec(severity_error, phase_term_to_parse_tree,
                      [simple_msg(get_term_context(HeadTerm),
                          [always(Pieces)])]),
@@ -465,8 +463,11 @@ parse_foreign_type_assertions(VarSet, Term, !Assertions, !Specs) :-
              )
          else
              TermStr = mercury_term_to_string(VarSet, print_name_only, Term),
-            Pieces = [words("Error: expected an assertion for a foreign type"),
-                words("got"), quote(TermStr), suffix("."), nl],
+            Pieces = [
+                words("In fourth argument of"), pragma_decl("foreign_type"),
+                words("declaration: error: expected a foreign type assertion,"),
+                words("got"), quote(TermStr), suffix(".")
+            ],
              Spec = error_spec(severity_error, phase_term_to_parse_tree,
                  [simple_msg(get_term_context(HeadTerm), [always(Pieces)])]),
              !:Specs = [Spec | !.Specs]
@@ -474,9 +475,12 @@ parse_foreign_type_assertions(VarSet, Term, !Assertions, !Specs) :-
          parse_foreign_type_assertions(VarSet, TailTerm, !Assertions, !Specs)
      else
          TermStr = mercury_term_to_string(VarSet, print_name_only, Term),
-        Pieces = [words("Error: expected a list of"),
-            words("assertions for a foreign type, got"),
-            quote(TermStr), suffix("."), nl],
+        Pieces = [
+            words("In fourth argument of"), pragma_decl("foreign_type"),
+            words("declaration: error: expected a list of"),
+            words("foreign type assertions, got"),
+            quote(TermStr), suffix(".")
+        ],
          Spec = error_spec(severity_error,
              phase_term_to_parse_tree,
              [simple_msg(get_term_context(Term), [always(Pieces)])]),
@@ -683,7 +687,7 @@ process_export_enum_attribute(ee_attr_upper(MakeUpperCase), !Attributes) :-
  parse_export_enum_attr(VarSet, Term, MaybeAttribute) :-
      ( if
          Term = functor(atom("prefix"), Args, _),
-        Args = [ ForeignNameTerm ],
+        Args = [ForeignNameTerm],
          ForeignNameTerm = functor(string(Prefix), [], _)
      then
          MaybeAttribute = ok1(ee_attr_prefix(yes(Prefix)))
@@ -1922,60 +1926,75 @@ term_to_foreign_language(term.functor(term.string(String), _, _), Lang) :-
  term_to_foreign_language(term.functor(term.atom(String), _, _), Lang) :-
      globals.convert_foreign_language(String, Lang).

-:- pred parse_foreign_language_type(term::in, varset::in, foreign_language::in,
-    maybe1(foreign_language_type)::out) is det.
+:- pred parse_foreign_language_type(term::in, varset::in,
+    maybe1(foreign_language)::in, maybe1(foreign_language_type)::out) is det.

-parse_foreign_language_type(InputTerm, VarSet, Language,
+parse_foreign_language_type(InputTerm, VarSet, MaybeLanguage,
          MaybeForeignLangType) :-
-    (
-        Language = lang_c,
-        ( if InputTerm = term.functor(term.string(CTypeName), [], _) then
-            MaybeForeignLangType = ok1(c(c_type(CTypeName)))
-        else
-            InputTermStr = describe_error_term(VarSet, InputTerm),
-            Pieces = [words("Error: invalid backend specification"),
-                quote(InputTermStr), suffix("."), nl],
-            Spec = error_spec(severity_error, phase_term_to_parse_tree,
-                [simple_msg(get_term_context(InputTerm), [always(Pieces)])]),
-            MaybeForeignLangType = error1([Spec])
-        )
-    ;
-        Language = lang_java,
-        ( if InputTerm = term.functor(term.string(JavaTypeName), [], _) then
-            MaybeForeignLangType = ok1(java(java_type(JavaTypeName)))
-        else
-            InputTermStr = describe_error_term(VarSet, InputTerm),
-            Pieces = [words("Error: invalid backend specification"),
-                quote(InputTermStr), suffix("."), nl],
-            Spec = error_spec(severity_error, phase_term_to_parse_tree,
-                [simple_msg(get_term_context(InputTerm), [always(Pieces)])]),
-            MaybeForeignLangType = error1([Spec])
-        )
-    ;
-        Language = lang_csharp,
-        ( if InputTerm = term.functor(term.string(CSharpTypeName), [], _) then
-            MaybeForeignLangType = ok1(csharp(csharp_type(CSharpTypeName)))
-        else
-            InputTermStr = describe_error_term(VarSet, InputTerm),
-            Pieces = [words("Error: invalid backend specification"),
-                quote(InputTermStr), suffix("."), nl],
-            Spec = error_spec(severity_error, phase_term_to_parse_tree,
-                [simple_msg(get_term_context(InputTerm), [always(Pieces)])]),
-            MaybeForeignLangType = error1([Spec])
-        )
-    ;
-        Language = lang_erlang,
-        ( if InputTerm = term.functor(term.string(_ErlangTypeName), [], _) then
-            % XXX should we check if the type is blank?
-            MaybeForeignLangType = ok1(erlang(erlang_type))
-        else
-            InputTermStr = describe_error_term(VarSet, InputTerm),
-            Pieces = [words("Error: invalid backend specification"),
-                quote(InputTermStr), suffix("."), nl],
-            Spec = error_spec(severity_error, phase_term_to_parse_tree,
-                [simple_msg(get_term_context(InputTerm), [always(Pieces)])]),
-            MaybeForeignLangType = error1([Spec])
+    ( if InputTerm = term.functor(term.string(ForeignTypeName), [], _) then
+        (
+            MaybeLanguage = ok1(Language),
+            (
+                (
+                    Language = lang_c,
+                    ForeignLangType = c(c_type(ForeignTypeName))
+                ;
+                    Language = lang_java,
+                    ForeignLangType = java(java_type(ForeignTypeName))
+                ;
+                    Language = lang_csharp,
+                    ForeignLangType = csharp(csharp_type(ForeignTypeName))
+                ),
+                ( if ForeignTypeName = "" then
+                    Pieces = [
+                        words("In third argument of"),
+                        pragma_decl("foreign_type"),
+                        words("declaration: error:"),
+                        words("foreign type descriptor for language"),
+                        quote(foreign_language_string(Language)),
+                        words("must be a non-empty string.")
+                    ],
+                    Spec = error_spec(severity_error, phase_term_to_parse_tree,
+                        [simple_msg(get_term_context(InputTerm),
+                        [always(Pieces)])]),
+                    MaybeForeignLangType = error1([Spec])
+                else
+                    MaybeForeignLangType = ok1(ForeignLangType)
+                )
+            ;
+                Language = lang_erlang,
+                ( if ForeignTypeName = "" then
+                    MaybeForeignLangType = ok1(erlang(erlang_type))
+                else
+                    Pieces = [
+                        words("In third argument of"),
+                        pragma_decl("foreign_type"),
+                        words("declaration: error:"),
+                        words("foreign type descriptor for language"),
+                        quote(foreign_language_string(Language)),
+                        words("must be an empty string.")
+                    ],
+                    Spec = error_spec(severity_error, phase_term_to_parse_tree,
+                        [simple_msg(get_term_context(InputTerm),
+                        [always(Pieces)])]),
+                    MaybeForeignLangType = error1([Spec])
+                )
+            )
+        ;
+            MaybeLanguage = error1(_),
+            MaybeForeignLangType = error1([])   % Dummy value.
          )
+    else
+        InputTermStr = describe_error_term(VarSet, InputTerm),
+        Pieces = [
+            words("In third argument of"),
+            pragma_decl("foreign_type"),
+            words("declaration: error:"),
+            words("invalid foreign type descriptor"),
+            quote(InputTermStr), suffix("."), nl],
+        Spec = error_spec(severity_error, phase_term_to_parse_tree,
+            [simple_msg(get_term_context(InputTerm), [always(Pieces)])]),
+        MaybeForeignLangType = error1([Spec])
      ).

      % This predicate parses foreign_decl pragmas.
diff --git a/compiler/parse_type_defn.m b/compiler/parse_type_defn.m
index c180c02..c14ff33 100644
--- a/compiler/parse_type_defn.m
+++ b/compiler/parse_type_defn.m
@@ -38,13 +38,22 @@
      list(term)::in, prog_context::in, int::in, is_solver_type::in,
      maybe1(item_or_marker)::out) is det.

-    % parse_type_defn_head(ModuleName, VarSet, Head, HeadResult):
+    % Are we parsing the type defn head in a `:- type' declaration or the in
+    % the second argument of a foreign_type pragma?
+    %
+:- type type_defn_head_parse_context
+    --->    tdhpc_type_defn
+    ;       tdhpc_foreign_type_pragma.
+
+    % parse_type_defn_head(ParseContext, ModuleName, VarSet, Head,
+    %   HeadResult):
      %
      % Check the head of a type definition for errors.
      %
      % Exported to parse_pragma.m for use when parsing foreign type pragmas.
      %
-:- pred parse_type_defn_head(module_name::in, varset::in, term::in,
+:- pred parse_type_defn_head(type_defn_head_parse_context::in,
+    module_name::in, varset::in, term::in,
      maybe2(sym_name, list(type_param))::out) is det.

      % A cut-down version of parse_type_decl_where_part_if_present
@@ -161,7 +170,8 @@ parse_du_type_defn(ModuleName, VarSet, HeadTerm, BodyTerm, Context, SeqNum,
          SolverSpecs = []
      ),

-    parse_type_defn_head(ModuleName, VarSet, HeadTerm, MaybeTypeCtorAndArgs),
+    parse_type_defn_head(tdhpc_type_defn, ModuleName, VarSet, HeadTerm,
+        MaybeTypeCtorAndArgs),
      du_type_rhs_ctors_and_where_terms(BodyTerm, CtorsTerm, MaybeWhereTerm),
      parse_maybe_exist_quant_constructors(ModuleName, VarSet, CtorsTerm,
          MaybeOneOrMoreCtors),
@@ -622,7 +632,8 @@ parse_eqv_type_defn(ModuleName, VarSet, HeadTerm, BodyTerm, Context, SeqNum,
              [simple_msg(get_term_context(HeadTerm), [always(SolverPieces)])]),
          SolverSpecs = [SolverSpec]
      ),
-    parse_type_defn_head(ModuleName, VarSet, HeadTerm, MaybeNameAndParams),
+    parse_type_defn_head(tdhpc_type_defn, ModuleName, VarSet, HeadTerm,
+        MaybeNameAndParams),
      % XXX Should pass more correct ContextPieces.
      ContextPieces = cord.init,
      parse_type(no_allow_ho_inst_info(wnhii_eqv_type_defn_body),
@@ -703,7 +714,8 @@ parse_where_block_type_defn(ModuleName, VarSet, HeadTerm, BodyTerm,
  parse_where_type_is_abstract_enum(ModuleName, VarSet, HeadTerm, BodyTerm,
          Context, SeqNum, MaybeIOM) :-
      varset.coerce(VarSet, TypeVarSet),
-    parse_type_defn_head(ModuleName, VarSet, HeadTerm, MaybeNameParams),
+    parse_type_defn_head(tdhpc_type_defn, ModuleName, VarSet, HeadTerm,
+        MaybeNameParams),
      ( if
          BodyTerm = term.functor(term.atom("type_is_abstract_enum"), Args, _)
      then
@@ -748,7 +760,8 @@ parse_where_type_is_abstract_enum(ModuleName, VarSet, HeadTerm, BodyTerm,
  parse_solver_type_base(ModuleName, VarSet, HeadTerm,
          MaybeSolverTypeDetails, MaybeUserEqComp, Context, SeqNum, MaybeIOM) :-
      varset.coerce(VarSet, TVarSet),
-    parse_type_defn_head(ModuleName, VarSet, HeadTerm, MaybeNameParams),
+    parse_type_defn_head(tdhpc_type_defn, ModuleName, VarSet, HeadTerm,
+        MaybeNameParams),
      (
          MaybeSolverTypeDetails = yes(_),
          SolverSpecs = []
@@ -795,7 +808,8 @@ parse_solver_type_base(ModuleName, VarSet, HeadTerm,

  parse_abstract_type_defn(ModuleName, VarSet, HeadTerm, Context, SeqNum,
          IsSolverType, MaybeIOM) :-
-    parse_type_defn_head(ModuleName, VarSet, HeadTerm, MaybeTypeCtorAndArgs),
+    parse_type_defn_head(tdhpc_type_defn, ModuleName, VarSet, HeadTerm,
+        MaybeTypeCtorAndArgs),
      (
          MaybeTypeCtorAndArgs = error2(Specs),
          MaybeIOM = error1(Specs)
@@ -943,8 +957,12 @@ parse_where_unify_compare(ModuleName, VarSet, Term0, MaybeUnifyCompare) :-
              MaybeWhereEnd = ok1(unit)
          ;
              !.MaybeTerm = yes(EndTerm),
-            Pieces = [words("Error: unrecognized or unexpected attribute."),
-                nl],
+            EndTermStr = describe_error_term(VarSet, EndTerm),
+            Pieces = [
+                words("In"), pragma_decl("foreign_type"),
+                words("declaration: error:"),
+                words("unrecognized or unexpected"), quote("where"),
+                words("attribute"), quote(EndTermStr), suffix("."), nl],
              EndSpec = error_spec(severity_error, phase_term_to_parse_tree,
                  [simple_msg(get_term_context(EndTerm), [always(Pieces)])]),
              MaybeWhereEnd = error1([EndSpec])
@@ -1358,16 +1376,37 @@ maybe_unify_compare(MaybeEqPred, MaybeCmpPred) =
  % Predicates useful for parsing several kinds of type definitions.
  %

-parse_type_defn_head(ModuleName, VarSet, HeadTerm, MaybeTypeCtorAndArgs) :-
+parse_type_defn_head(ParseContext, ModuleName, VarSet, HeadTerm,
+        MaybeTypeCtorAndArgs) :-
      (
-        HeadTerm = term.variable(_, Context),
-        Pieces = [words("Error: variable on LHS of type definition."), nl],
+        HeadTerm = term.variable(Var, Context),
+        (
+            ParseContext = tdhpc_type_defn,
+            Pieces = [words("Error: variable on LHS of type definition."), nl]
+        ;
+            ParseContext = tdhpc_foreign_type_pragma,
+            VarName = varset.lookup_name(VarSet, Var),
+            Pieces = [
+                words("In second argument of"), pragma_decl("foreign_type"),
+                words("declaration: error: expected a type name declared"),
+                words("using a"), decl("type"), words("declaration, got"),
+                words("type variable"), quote(VarName), suffix(".")
+            ]
+        ),
          Spec = error_spec(severity_error, phase_term_to_parse_tree,
              [simple_msg(Context, [always(Pieces)])]),
          MaybeTypeCtorAndArgs = error2([Spec])
      ;
          HeadTerm = term.functor(_, _, HeadContext),
-        ContextPieces = cord.singleton(words("In type definition:")),
+        (
+            ParseContext = tdhpc_type_defn,
+            ContextPieces = cord.singleton(words("In type definition:"))
+        ;
+            ParseContext = tdhpc_foreign_type_pragma,
+            ContextPieces = cord.from_list([
+                words("In"), pragma_decl("foreign_type"), words("declaration:")
+            ])
+        ),
          parse_implicitly_qualified_sym_name_and_args(ModuleName, HeadTerm,
              VarSet, ContextPieces, HeadResult),
          (
diff --git a/tests/invalid/bad_foreign_type.err_exp b/tests/invalid/bad_foreign_type.err_exp
index 66d06b4..d736ded 100644
--- a/tests/invalid/bad_foreign_type.err_exp
+++ b/tests/invalid/bad_foreign_type.err_exp
@@ -1,25 +1,57 @@
-bad_foreign_type.m:011: Error: abstract declaration for type
-bad_foreign_type.m:011:   `bad_foreign_type.foo'/0 has no corresponding
-bad_foreign_type.m:011:   definition.
  bad_foreign_type.m:017: Error: wrong number of arguments in
  bad_foreign_type.m:017:   `:- pragma foreign_type' declaration.
  bad_foreign_type.m:021: Error: wrong number of arguments in
  bad_foreign_type.m:021:   `:- pragma foreign_type' declaration.
-bad_foreign_type.m:025: Error: invalid foreign language in
+bad_foreign_type.m:025: Error: invalid foreign language `"InvalidLanguage"' in
  bad_foreign_type.m:025:   `:- pragma foreign_type' declaration.
-bad_foreign_type.m:029: In type definition: error: atom expected at 1111.
-bad_foreign_type.m:033: Error: invalid backend specification `2222'.
-bad_foreign_type.m:037: Error: expected a list of assertions for a foreign
-bad_foreign_type.m:037:   type, got `5555'.
-bad_foreign_type.m:041: Error: expected an assertion for a foreign type got
-bad_foreign_type.m:041:   `[not_an_assertion]'.
-bad_foreign_type.m:045: Error: expected an assertion for a foreign type got
-bad_foreign_type.m:045:   `["I\'m a string"]'.
-bad_foreign_type.m:045: Error: expected an assertion for a foreign type got
-bad_foreign_type.m:045:   `[3333, "I\'m a string"]'.
+bad_foreign_type.m:029: In `:- pragma foreign_type' declaration: error: atom
+bad_foreign_type.m:029:   expected at 1111.
+bad_foreign_type.m:033: In third argument of `:- pragma foreign_type'
+bad_foreign_type.m:033:   declaration: error: invalid foreign type descriptor
+bad_foreign_type.m:033:   `2222'.
+bad_foreign_type.m:037: In fourth argument of `:- pragma foreign_type'
+bad_foreign_type.m:037:   declaration: error: expected a list of foreign type
+bad_foreign_type.m:037:   assertions, got `5555'.
+bad_foreign_type.m:041: In fourth argument of `:- pragma foreign_type'
+bad_foreign_type.m:041:   declaration: error: expected a foreign type
+bad_foreign_type.m:041:   assertion, got `[not_an_assertion]'.
+bad_foreign_type.m:045: In fourth argument of `:- pragma foreign_type'
+bad_foreign_type.m:045:   declaration: error: expected a foreign type
+bad_foreign_type.m:045:   assertion, got `["I\'m a string"]'.
+bad_foreign_type.m:045: In fourth argument of `:- pragma foreign_type'
+bad_foreign_type.m:045:   declaration: error: expected a foreign type
+bad_foreign_type.m:045:   assertion, got `[3333, "I\'m a string"]'.
  bad_foreign_type.m:050: Error: expected `is'.
-bad_foreign_type.m:055: Error: unrecognized or unexpected attribute.
-bad_foreign_type.m:060: Error: invalid foreign language in
+bad_foreign_type.m:055: In `:- pragma foreign_type' declaration: error:
+bad_foreign_type.m:055:   unrecognized or unexpected `where' attribute
+bad_foreign_type.m:055:   `(foo is bar)'.
+bad_foreign_type.m:060: Error: invalid foreign language `"InvalidLanguage"' in
  bad_foreign_type.m:060:   `:- pragma foreign_type' declaration.
-bad_foreign_type.m:061: In type definition: error: atom expected at 9999.
-bad_foreign_type.m:066: Error: unrecognized or unexpected attribute.
+bad_foreign_type.m:061: In `:- pragma foreign_type' declaration: error: atom
+bad_foreign_type.m:061:   expected at 9999.
+bad_foreign_type.m:062: In third argument of `:- pragma foreign_type'
+bad_foreign_type.m:062:   declaration: error: invalid foreign type descriptor
+bad_foreign_type.m:062:   `[not_an_assertion]'.
+bad_foreign_type.m:066: In `:- pragma foreign_type' declaration: error:
+bad_foreign_type.m:066:   unrecognized or unexpected `where' attribute
+bad_foreign_type.m:066:   `(bar is baaz)'.
+bad_foreign_type.m:071: Error: type `bad_foreign_type.bar'/0 defined as
+bad_foreign_type.m:071:   foreign_type without being declared.
+bad_foreign_type.m:075: In second argument of `:- pragma foreign_type'
+bad_foreign_type.m:075:   declaration: error: expected a type name declared
+bad_foreign_type.m:075:   using a `:- type' declaration, got type variable `T'.
+bad_foreign_type.m:081: In third argument of `:- pragma foreign_type'
+bad_foreign_type.m:081:   declaration: error: foreign type descriptor for
+bad_foreign_type.m:081:   language `C' must be a non-empty string.
+bad_foreign_type.m:082: In third argument of `:- pragma foreign_type'
+bad_foreign_type.m:082:   declaration: error: foreign type descriptor for
+bad_foreign_type.m:082:   language `C#' must be a non-empty string.
+bad_foreign_type.m:083: In third argument of `:- pragma foreign_type'
+bad_foreign_type.m:083:   declaration: error: foreign type descriptor for
+bad_foreign_type.m:083:   language `Java' must be a non-empty string.
+bad_foreign_type.m:084: In third argument of `:- pragma foreign_type'
+bad_foreign_type.m:084:   declaration: error: foreign type descriptor for
+bad_foreign_type.m:084:   language `Erlang' must be an empty string.
+bad_foreign_type.m:089: In fourth argument of `:- pragma foreign_type'
+bad_foreign_type.m:089:   declaration: error: foreign type assertion
+bad_foreign_type.m:089:   `can_pass_as_mercury_type' is repeated.
diff --git a/tests/invalid/bad_foreign_type.m b/tests/invalid/bad_foreign_type.m
index 288fd81..c82e027 100644
--- a/tests/invalid/bad_foreign_type.m
+++ b/tests/invalid/bad_foreign_type.m
@@ -65,3 +65,25 @@
           bar
      is
           baaz.
+
+    % No corresponding `:- type' declaration present.
+    %
+:- pragma foreign_type("C", bar, "int").
+
+    % Second argument is a type variable.
+    %
+:- pragma foreign_type("C", T, "int").
+
+    % Empty foreign type descriptor (C, C# , Java).
+    % Non-empty foreign type descriptor (Erlang).
+    %
+:- type quux.
+:- pragma foreign_type("C", quux, "").
+:- pragma foreign_type("C#", quux, "").
+:- pragma foreign_type("Java", quux, "").
+:- pragma foreign_type("Erlang", quux, "abcde").
+
+    % Repeated foreign type assertion.
+    %
+:- pragma foreign_type("C", foo, "int",
+    [can_pass_as_mercury_type, can_pass_as_mercury_type]).
diff --git a/tests/invalid/where_direct_arg2.err_exp b/tests/invalid/where_direct_arg2.err_exp
index 876584e..d3eb8c1 100644
--- a/tests/invalid/where_direct_arg2.err_exp
+++ b/tests/invalid/where_direct_arg2.err_exp
@@ -6,4 +6,6 @@ where_direct_arg2.m:012:   as a direct pointer to its sole argument.
  where_direct_arg2.m:016: Error: `where_direct_arg2.enum'/1 cannot be
  where_direct_arg2.m:016:   represented as a direct pointer to its sole
  where_direct_arg2.m:016:   argument.
-where_direct_arg2.m:038: Error: unrecognized or unexpected attribute.
+where_direct_arg2.m:038: In `:- pragma foreign_type' declaration: error:
+where_direct_arg2.m:038:   unrecognized or unexpected `where' attribute
+where_direct_arg2.m:038:   `(direct_arg is [])'.


More information about the reviews mailing list