eolian: add library support for declaring and using errors
authorDaniel Kolesa <d.kolesa@samsung.com>
Fri, 21 Jun 2019 13:05:50 +0000 (15:05 +0200)
committerJongmin Lee <jm105.lee@samsung.com>
Wed, 26 Jun 2019 01:43:46 +0000 (10:43 +0900)
You can now declare errors like this:

error Foo = "message"; [[documentation]]

Then you can use them as types like this:

foo {
    return: error(Error1, Error2, ...);
}

They have a separate type category and storage. They are checked
for redefinitions the same as anything else though. This does
not add any generator support nor it adds any advanced checking.

Ref T6890

src/lib/eolian/Eolian.h
src/lib/eolian/database_type_api.c
src/lib/eolian/database_validate.c
src/lib/eolian/eo_lexer.h
src/lib/eolian/eo_parser.c
src/lib/eolian/eolian_database.c
src/lib/eolian/eolian_database.h
src/tests/eolian/data/error.eo [new file with mode: 0644]
src/tests/eolian/eolian_parsing.c

index 80ccd1e..4c9535d 100644 (file)
@@ -170,6 +170,12 @@ typedef struct _Eolian_Expression Eolian_Expression;
  */
 typedef struct _Eolian_Variable Eolian_Variable;
 
+/* Error information
+ *
+ * @ingroup Eolian
+ */
+typedef struct _Eolian_Error Eolian_Error;
+
 /* Struct field information
  *
  * @ingroup Eolian
@@ -222,7 +228,8 @@ typedef enum
    EOLIAN_OBJECT_PART,
    EOLIAN_OBJECT_IMPLEMENT,
    EOLIAN_OBJECT_CONSTRUCTOR,
-   EOLIAN_OBJECT_DOCUMENTATION
+   EOLIAN_OBJECT_DOCUMENTATION,
+   EOLIAN_OBJECT_ERROR
 } Eolian_Object_Type;
 
 typedef enum
@@ -276,6 +283,7 @@ typedef enum
    EOLIAN_TYPE_VOID,
    EOLIAN_TYPE_REGULAR,
    EOLIAN_TYPE_CLASS,
+   EOLIAN_TYPE_ERROR,
    EOLIAN_TYPE_UNDEFINED
 } Eolian_Type_Type;
 
@@ -1079,6 +1087,16 @@ EAPI const Eolian_Variable *eolian_unit_global_by_name_get(const Eolian_Unit *un
 EAPI const Eolian_Variable *eolian_unit_constant_by_name_get(const Eolian_Unit *unit, const char *name);
 
 /*
+ * @brief Get an error declaration in a unit by name.
+ *
+ * @param[in] unit The unit.
+ * @param[in] name the name of the error
+ *
+ * @ingroup Eolian
+ */
+EAPI const Eolian_Error *eolian_unit_error_by_name_get(const Eolian_Unit *unit, const char *name);
+
+/*
  * @brief Get an iterator to all constant variables in the Eolian database.
  *
  * @return the iterator or NULL
@@ -1101,6 +1119,17 @@ EAPI Eina_Iterator *eolian_unit_constants_get(const Eolian_Unit *unit);
 EAPI Eina_Iterator *eolian_unit_globals_get(const Eolian_Unit *unit);
 
 /*
+ * @brief Get an iterator to all error declarations in the Eolian database.
+ *
+ * @return the iterator or NULL
+ *
+ * Thanks to internal caching, this is an O(1) operation.
+ *
+ * @ingroup Eolian
+ */
+EAPI Eina_Iterator *eolian_unit_errors_get(const Eolian_Unit *unit);
+
+/*
  * @brief Get an alias type declaration within a unit by name.
  *
  * @param[in] unit The unit.
@@ -1266,6 +1295,19 @@ eolian_state_constant_by_name_get(const Eolian_State *state, const char *name)
 }
 
 /*
+ * @brief A helper function to get an error declaration in a state by name.
+ *
+ * @see eolian_unit_error_by_name_get
+ *
+ * @ingroup Eolian
+ */
+static inline const Eolian_Error *
+eolian_state_error_by_name_get(const Eolian_State *state, const char *name)
+{
+   return eolian_unit_error_by_name_get(EOLIAN_UNIT(state), name);
+}
+
+/*
  * @brief Get an iterator to all global variables contained in a file.
  *
  * @param[in] state The state.
@@ -1291,6 +1333,19 @@ EAPI Eina_Iterator *eolian_state_globals_by_file_get(const Eolian_State *state,
 EAPI Eina_Iterator *eolian_state_constants_by_file_get(const Eolian_State *state, const char *file_name);
 
 /*
+ * @brief Get an iterator to all error declarations contained in a file.
+ *
+ * @param[in] state The state.
+ * @param[in] file_name The file name.
+ * @return the iterator or NULL
+ *
+ * Thanks to internal caching, this is an O(1) operation.
+ *
+ * @ingroup Eolian
+ */
+EAPI Eina_Iterator *eolian_state_errors_by_file_get(const Eolian_State *state, const char *file_name);
+
+/*
  * @brief A helper function to get all globals in a state.
  *
  * @see eolian_unit_globals_get
@@ -1317,6 +1372,19 @@ eolian_state_constants_get(const Eolian_State *state)
 }
 
 /*
+ * @brief A helper function to get all error declarations in a state.
+ *
+ * @see eolian_unit_errors_get
+ *
+ * @ingroup Eolian
+ */
+static inline Eina_Iterator *
+eolian_state_errors_get(const Eolian_State *state)
+{
+   return eolian_unit_errors_get(EOLIAN_UNIT(state));
+}
+
+/*
  * @brief A helper function to get an alias in a state by name.
  *
  * @see eolian_unit_alias_by_name_get
@@ -2704,7 +2772,8 @@ EAPI const Eolian_Type *eolian_type_base_type_get(const Eolian_Type *tp);
  *
  * The inner types of a complex type form a chain. Therefore, you first retrieve
  * the first one via eolian_type_base_type_get and then get the next one via
- * this API function called on the first inner type if necessary.
+ * this API function called on the first inner type if necessary. Another use
+ * for this is with errors, specifying error(Foo, Bar, ...) makes a chain.
  *
  * @param[in] tp the type.
  * @return the next type or NULL.
@@ -2753,6 +2822,16 @@ EAPI const Eolian_Type *eolian_type_aliased_base_get(const Eolian_Type *tp);
 EAPI const Eolian_Class *eolian_type_class_get(const Eolian_Type *tp);
 
 /*
+ * @brief Get the error declaration associated with an EOLIAN_TYPE_ERROR type.
+ *
+ * @param[in] tp the type.
+ * @return the error or NULL.
+ *
+ * @ingroup Eolian
+ */
+EAPI const Eolian_Error *eolian_type_error_get(const Eolian_Type *tp);
+
+/*
  * @brief Get whether the given type is owned.
  *
  * This is true when a parameter, return or whatever is marked as @owned.
@@ -3131,6 +3210,101 @@ eolian_variable_is_beta(const Eolian_Variable *var)
 }
 
 /*
+ * @brief Get the message of an error declaration.
+ *
+ * @param[in] err the error.
+ * @return the message or NULL.
+ *
+ * @ingroup Eolian
+ */
+EAPI const char *eolian_error_message_get(const Eolian_Error *err);
+
+/*
+ * @brief Get the documentation of an error declaration.
+ *
+ * @param[in] err the error declaration.
+ * @return the documentation or NULL.
+ *
+ * @ingroup Eolian
+ */
+EAPI const Eolian_Documentation *eolian_error_documentation_get(const Eolian_Error *err);
+
+/*
+ * @brief A helper function to get the full name of an error declaration.
+ *
+ * @see eolian_object_name_get
+ *
+ * @ingroup Eolian
+ */
+static inline const char *
+eolian_error_name_get(const Eolian_Error *err)
+{
+   return eolian_object_name_get(EOLIAN_OBJECT(err));
+}
+
+/*
+ * @brief A helper function to get the C name of an error declaration.
+ *
+ * @see eolian_object_c_name_get
+ *
+ * @ingroup Eolian
+ */
+static inline const char *
+eolian_error_c_name_get(const Eolian_Error *err)
+{
+   return eolian_object_c_name_get(EOLIAN_OBJECT(err));
+}
+
+/*
+ * @brief A helper function to get the short name of an error declaration.
+ *
+ * @see eolian_object_short_name_get
+ *
+ * @ingroup Eolian
+ */
+static inline const char *
+eolian_error_short_name_get(const Eolian_Error *err)
+{
+   return eolian_object_short_name_get(EOLIAN_OBJECT(err));
+}
+
+/*
+ * @brief A helper function to get the namespaces of an error declaration.
+ *
+ * @see eolian_object_namespaces_get
+ *
+ * @ingroup Eolian
+ */
+static inline Eina_Iterator *
+eolian_error_namespaces_get(const Eolian_Error *err)
+{
+   return eolian_object_namespaces_get(EOLIAN_OBJECT(err));
+}
+
+/*
+ * @brief Get whether an error declaration is beta.
+ *
+ * @see eolian_object_is_beta
+ *
+ * @ingroup Eolian
+ */
+static inline Eina_Bool
+eolian_error_is_beta(const Eolian_Error *err)
+{
+   return eolian_object_is_beta(EOLIAN_OBJECT(err));
+}
+
+/*
+ * @brief Check if an error declaration is extern.
+ *
+ * @param[in] err the errpr decůaratopm.
+ * @return EINA_TRUE if it's extern, EINA_FALSE otherwise.
+ *
+ * @ingroup Eolian
+ */
+EAPI Eina_Bool eolian_error_is_extern(const Eolian_Error *err);
+
+/*
  * @brief Get the summary of the documentation.
  *
  * This should never return NULL unless the input is invalid.
index b10a9a7..d4869e2 100644 (file)
@@ -197,6 +197,15 @@ eolian_type_class_get(const Eolian_Type *tp)
    return tp->klass;
 }
 
+EAPI const Eolian_Error *
+eolian_type_error_get(const Eolian_Type *tp)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(tp, NULL);
+   if (eolian_type_type_get(tp) != EOLIAN_TYPE_ERROR)
+     return NULL;
+   return tp->error;
+}
+
 EAPI Eina_Bool
 eolian_type_is_owned(const Eolian_Type *tp)
 {
index 71c05d7..8deea6d 100644 (file)
@@ -351,9 +351,29 @@ _validate_type(Validate_State *vals, Eolian_Type *tp)
              }
            if (!tp->freefunc)
              tp->freefunc = eina_stringshare_add(eo_obj_free);
-           tp->base.c_name = eina_stringshare_ref(tp->tdecl->base.c_name);
+           tp->base.c_name = eina_stringshare_ref(tp->klass->base.c_name);
            return _validate_ownable(tp);
         }
+      case EOLIAN_TYPE_ERROR:
+        {
+           tp->error = (Eolian_Error *)eolian_unit_error_by_name_get(src, tp->base.name);
+           if (!tp->error)
+             {
+                _eo_parser_log(&tp->base, "undefined error %s "
+                         "(likely wrong namespacing)", tp->base.name);
+                return EINA_FALSE;
+             }
+           else if (vals->stable && tp->error->base.is_beta)
+             {
+                _eo_parser_log(&tp->base, "beta error '%s' used in stable context",
+                               tp->error->base.name);
+                return EINA_FALSE;
+             }
+           tp->base.c_name = eina_stringshare_ref(tp->error->base.c_name);
+           if (tp->next_type && !_validate_type(vals, tp->next_type))
+             return EINA_FALSE;
+           return _validate(&tp->base);
+        }
       default:
         return EINA_FALSE;
      }
index 5bc7064..0e79217 100644 (file)
@@ -28,7 +28,7 @@ enum Tokens
 #define KEYWORDS KW(class), KW(const), KW(enum), KW(return), KW(struct), \
     \
     KW(abstract), KW(c_prefix), KW(composite), KW(constructor), KW(constructors), \
-    KW(data), KW(destructor), KW(event_prefix), KW(events), KW(extends), \
+    KW(data), KW(destructor), KW(error), KW(event_prefix), KW(events), KW(extends), \
     KW(free), KW(get), KW(implements), KW(import), KW(interface), \
     KW(keys), KW(legacy), KW(methods), KW(mixin), KW(params), \
     KW(parse), KW(parts), KW(ptr), KW(set), KW(type), KW(values), KW(var), KW(requires), \
@@ -296,6 +296,18 @@ eo_lexer_expr_release_ref(Eo_Lexer *ls, Eolian_Expression *expr)
    return eo_lexer_expr_release(ls, expr);
 }
 
+static inline Eolian_Error *
+eo_lexer_error_new(Eo_Lexer *ls)
+{
+   return (Eolian_Error *)eo_lexer_node_new(ls, sizeof(Eolian_Error));
+}
+
+static inline Eolian_Error *
+eo_lexer_error_release(Eo_Lexer *ls, Eolian_Error *err)
+{
+   return (Eolian_Error *)eo_lexer_node_release(ls, (Eolian_Object *)err);
+}
+
 /* "stack" management, only to protect against errors (jumps) in parsing */
 void eo_lexer_dtor_push(Eo_Lexer *ls, Eina_Free_Cb free_cb, void *data);
 void eo_lexer_dtor_pop(Eo_Lexer *ls);
index ee056b7..f33db75 100644 (file)
@@ -646,6 +646,29 @@ parse_enum(Eo_Lexer *ls, const char *name, Eina_Bool is_extern,
    return def;
 }
 
+/* error(Error1, Error2, Error3, ...) */
+static Eolian_Type *
+parse_type_error(Eo_Lexer *ls)
+{
+   Eolian_Type *def = eo_lexer_type_new(ls);
+   Eina_Strbuf *buf = eina_strbuf_new();
+   eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_strbuf_free), buf);
+   for (Eolian_Type *cdef = def;; cdef = cdef->next_type)
+     {
+        FILL_BASE(cdef->base, ls, ls->line_number, ls->column, TYPE);
+        parse_name(ls, buf);
+        cdef->type = EOLIAN_TYPE_ERROR;
+        cdef->base.name = eina_stringshare_add(eina_strbuf_string_get(buf));
+        eina_strbuf_reset(buf);
+        if (ls->t.token != ',')
+          break;
+        eo_lexer_get(ls);
+        cdef->next_type = eo_lexer_type_release(ls, eo_lexer_type_new(ls));
+     }
+   eo_lexer_dtor_pop(ls);
+   return def;
+}
+
 static Eolian_Type *
 parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ptr)
 {
@@ -698,6 +721,17 @@ parse_type_void(Eo_Lexer *ls, Eina_Bool allow_ptr)
            check_match(ls, ')', '(', pline, pcolumn);
            return def;
         }
+      case KW_error:
+        {
+           int pline, pcolumn;
+           eo_lexer_get(ls);
+           pline = ls->line_number;
+           pcolumn = ls->column;
+           check_next(ls, '(');
+           def = parse_type_error(ls);
+           check_match(ls, ')', '(', pline, pcolumn);
+           return def;
+        }
       default:
         break;
      }
@@ -910,6 +944,71 @@ tags_done:
    return def;
 }
 
+static Eolian_Error *
+parse_error(Eo_Lexer *ls)
+{
+   Eolian_Error *def = eo_lexer_error_new(ls);
+   Eina_Strbuf *buf;
+   eo_lexer_get(ls);
+   Eina_Stringshare *cname = NULL;
+   Eina_Bool has_extern = EINA_FALSE, has_beta = EINA_FALSE, has_c_name = EINA_FALSE;
+   for (;;) switch (ls->t.kw)
+     {
+      case KW_at_extern:
+        CASE_LOCK(ls, extern, "extern qualifier");
+        def->is_extern = EINA_TRUE;
+        eo_lexer_get(ls);
+        break;
+      case KW_at_beta:
+        CASE_LOCK(ls, beta, "beta qualifier");
+        def->base.is_beta = EINA_TRUE;
+        eo_lexer_get(ls);
+        break;
+      case KW_at_c_name:
+        CASE_LOCK(ls, c_name, "@c_name specifier");
+        cname = parse_c_name(ls);
+        eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_stringshare_del), (void *)cname);
+        break;
+      default:
+        goto tags_done;
+     }
+tags_done:
+   buf = eina_strbuf_new();
+   eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_strbuf_free), buf);
+   eo_lexer_context_push(ls);
+   FILL_BASE(def->base, ls, ls->line_number, ls->column, ERROR);
+   parse_name(ls, buf);
+   def->base.name = eina_stringshare_add(eina_strbuf_string_get(buf));
+   if (cname)
+     {
+        def->base.c_name = cname;
+        eo_lexer_dtor_pop(ls);
+     }
+   else
+     def->base.c_name = make_c_name(def->base.name);
+   Eolian_Object *decl = _eolian_decl_get(ls, def->base.name);
+   if (decl)
+     {
+        eo_lexer_context_restore(ls);
+        redef_error(ls, decl, &def->base);
+     }
+   eo_lexer_context_pop(ls);
+   check(ls, '=');
+   /* we need to parse a string so switch to exprmode */
+   ls->expr_mode = EINA_TRUE;
+   /* consume = to get string */
+   eo_lexer_get(ls);
+   /* verify and switch back to plain syntax mode */
+   check(ls, TOK_STRING);
+   ls->expr_mode = EINA_FALSE;
+   def->msg = eina_stringshare_ref(ls->t.value.s);
+   eo_lexer_get(ls);
+   check_next(ls, ';');
+   FILL_DOC(ls, def, doc);
+   eo_lexer_dtor_pop(ls);
+   return def;
+}
+
 typedef struct _Eo_Ret_Def
 {
    Eolian_Type *type;
@@ -2273,6 +2372,9 @@ parse_unit(Eo_Lexer *ls, Eina_Bool eot)
              parse_variable(ls, ls->t.kw == KW_var)));
            break;
         }
+      case KW_error:
+        database_error_add(ls->unit, eo_lexer_error_release(ls, parse_error(ls)));
+        break;
       case KW_struct:
       case KW_enum:
         {
index 40b0ca1..caa4340 100644 (file)
@@ -568,6 +568,7 @@ database_unit_init(Eolian_State *state, Eolian_Unit *unit, const char *file)
    unit->classes    = eina_hash_stringshared_new(EINA_FREE_CB(database_class_del));
    unit->globals    = eina_hash_stringshared_new(EINA_FREE_CB(database_var_del));
    unit->constants  = eina_hash_stringshared_new(EINA_FREE_CB(database_var_del));
+   unit->errors     = eina_hash_stringshared_new(EINA_FREE_CB(database_error_del));
    unit->aliases    = eina_hash_stringshared_new(EINA_FREE_CB(database_typedecl_del));
    unit->structs    = eina_hash_stringshared_new(EINA_FREE_CB(database_typedecl_del));
    unit->enums      = eina_hash_stringshared_new(EINA_FREE_CB(database_typedecl_del));
@@ -585,6 +586,7 @@ _unit_contents_del(Eolian_Unit *unit)
    eina_hash_free(unit->classes);
    eina_hash_free(unit->globals);
    eina_hash_free(unit->constants);
+   eina_hash_free(unit->errors);
    eina_hash_free(unit->aliases);
    eina_hash_free(unit->structs);
    eina_hash_free(unit->enums);
@@ -637,6 +639,7 @@ _state_area_init(Eolian_State *state, Eolian_State_Area *a)
    a->enums_f     = eina_hash_stringshared_new(NULL);
    a->globals_f   = eina_hash_stringshared_new(NULL);
    a->constants_f = eina_hash_stringshared_new(NULL);
+   a->errors_f    = eina_hash_stringshared_new(NULL);
    a->objects_f   = eina_hash_stringshared_new(NULL);
 }
 
@@ -663,6 +666,7 @@ _state_area_contents_del(Eolian_State_Area *a)
    _hashlist_free(a->enums_f);
    _hashlist_free(a->globals_f);
    _hashlist_free(a->constants_f);
+   _hashlist_free(a->errors_f);
    _hashlist_free(a->objects_f);
 }
 
@@ -1240,6 +1244,17 @@ eolian_state_constants_by_file_get(const Eolian_State *state, const char *file_n
 }
 
 EAPI Eina_Iterator *
+eolian_state_errors_by_file_get(const Eolian_State *state, const char *file_name)
+{
+   if (!state) return NULL;
+   Eina_Stringshare *shr = eina_stringshare_add(file_name);
+   Eina_List *l = eina_hash_find(state->main.errors_f, shr);
+   eina_stringshare_del(shr);
+   if (!l) return NULL;
+   return eina_list_iterator_new(l);
+}
+
+EAPI Eina_Iterator *
 eolian_state_aliases_by_file_get(const Eolian_State *state, const char *file_name)
 {
    if (!state) return NULL;
@@ -1361,6 +1376,16 @@ eolian_unit_constant_by_name_get(const Eolian_Unit *unit, const char *name)
    return v;
 }
 
+EAPI const Eolian_Error *
+eolian_unit_error_by_name_get(const Eolian_Unit *unit, const char *name)
+{
+   if (!unit) return NULL;
+   Eina_Stringshare *shr = eina_stringshare_add(name);
+   Eolian_Error *v = eina_hash_find(unit->errors, shr);
+   eina_stringshare_del(shr);
+   return v;
+}
+
 EAPI Eina_Iterator *
 eolian_unit_constants_get(const Eolian_Unit *unit)
 {
@@ -1373,6 +1398,12 @@ eolian_unit_globals_get(const Eolian_Unit *unit)
    return (unit ? eina_hash_iterator_data_new(unit->globals) : NULL);
 }
 
+EAPI Eina_Iterator *
+eolian_unit_errors_get(const Eolian_Unit *unit)
+{
+   return (unit ? eina_hash_iterator_data_new(unit->errors) : NULL);
+}
+
 EAPI const Eolian_Typedecl *
 eolian_unit_alias_by_name_get(const Eolian_Unit *unit, const char *name)
 {
@@ -1424,6 +1455,45 @@ eolian_unit_enums_get(const Eolian_Unit *unit)
    return (unit ? eina_hash_iterator_data_new(unit->enums) : NULL);
 }
 
+EAPI const char *
+eolian_error_message_get(const Eolian_Error *err)
+{
+   if (!err) return NULL;
+   return err->msg;
+}
+
+EAPI Eina_Bool
+eolian_error_is_extern(const Eolian_Error *err)
+{
+   if (!err) return EINA_FALSE;
+   return err->is_extern;
+}
+
+EAPI const Eolian_Documentation *
+eolian_error_documentation_get(const Eolian_Error *err)
+{
+   EINA_SAFETY_ON_NULL_RETURN_VAL(err, NULL);
+   return err->doc;
+}
+
+void
+database_error_del(Eolian_Error *err)
+{
+   if (!err || eolian_object_unref(&err->base)) return;
+   eina_stringshare_del(err->msg);
+   free(err);
+}
+
+void
+database_error_add(Eolian_Unit *unit, Eolian_Error *err)
+{
+   EOLIAN_OBJECT_ADD(unit, err->base.name, err, errors);
+   eina_hash_set(unit->state->staging.errors_f, err->base.file, eina_list_append
+                ((Eina_List*)eina_hash_find(unit->state->staging.errors_f, err->base.file),
+                err));
+   database_object_add(unit, &err->base);
+}
+
 char *
 database_class_to_filename(const char *cname)
 {
index 0dce769..950e411 100644 (file)
@@ -41,6 +41,7 @@ struct _Eolian_Unit
    Eina_Hash     *classes;
    Eina_Hash     *globals;
    Eina_Hash     *constants;
+   Eina_Hash     *errors;
    Eina_Hash     *aliases;
    Eina_Hash     *structs;
    Eina_Hash     *enums;
@@ -60,6 +61,7 @@ typedef struct _Eolian_State_Area
    Eina_Hash *enums_f;
    Eina_Hash *globals_f;
    Eina_Hash *constants_f;
+   Eina_Hash *errors_f;
    Eina_Hash *objects_f;
 } Eolian_State_Area;
 
@@ -267,6 +269,7 @@ struct _Eolian_Type
    {
       Eolian_Class *klass;
       Eolian_Typedecl *tdecl;
+      Eolian_Error *error;
    };
    Eina_Bool is_const  :1;
    Eina_Bool is_ptr    :1;
@@ -325,6 +328,14 @@ struct _Eolian_Event
    Eina_Bool is_restart :1;
 };
 
+struct _Eolian_Error
+{
+   Eolian_Object base;
+   Eina_Stringshare *msg;
+   Eolian_Documentation *doc;
+   Eina_Bool is_extern :1;
+};
+
 struct _Eolian_Struct_Type_Field
 {
    Eolian_Object     base;
@@ -448,4 +459,8 @@ void database_event_del(Eolian_Event *event);
 /* parts */
 void database_part_del(Eolian_Part *part);
 
+/* errors */
+void database_error_del(Eolian_Error *err);
+void database_error_add(Eolian_Unit *unit, Eolian_Error *err);
+
 #endif
diff --git a/src/tests/eolian/data/error.eo b/src/tests/eolian/data/error.eo
new file mode 100644 (file)
index 0000000..3a5867a
--- /dev/null
@@ -0,0 +1,13 @@
+error Foo = "something bad happened"; [[Error doc]]
+error @beta Bar = "another bad thing happened"; [[Another error doc]]
+
+class @beta Error {
+   methods {
+      foo {
+         return: error(Foo);
+      }
+      bar {
+        return: error(Foo, Bar);
+      }
+   }
+}
index 57120f6..ccabb26 100644 (file)
@@ -843,6 +843,53 @@ EFL_START_TEST(eolian_var)
 }
 EFL_END_TEST
 
+EFL_START_TEST(eolian_error)
+{
+   const Eolian_Unit *unit;
+   const Eolian_Class *class;
+   const Eolian_Function *f1, *f2;
+   const Eolian_Type *rtp1, *rtp2;
+   const Eolian_Error *err1, *err2;
+
+   Eolian_State *eos = eolian_state_new();
+
+   fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data"));
+   fail_if(!(unit = eolian_state_file_parse(eos, "error.eo")));
+
+   fail_if(!(class = eolian_unit_class_by_name_get(unit, "Error")));
+   fail_if(!(f1 = eolian_class_function_by_name_get(class, "foo", EOLIAN_METHOD)));
+   fail_if(!(f2 = eolian_class_function_by_name_get(class, "bar", EOLIAN_METHOD)));
+
+   fail_if(!(rtp1 = eolian_function_return_type_get(f1, EOLIAN_METHOD)));
+   fail_if(!(rtp2 = eolian_function_return_type_get(f2, EOLIAN_METHOD)));
+
+   /* single error */
+   fail_if(eolian_type_type_get(rtp1) != EOLIAN_TYPE_ERROR);
+   fail_if(eolian_type_next_type_get(rtp1) != NULL);
+   fail_if(strcmp(eolian_type_name_get(rtp1), "Foo"));
+   fail_if(!(err1 = eolian_type_error_get(rtp1)));
+   fail_if(strcmp(eolian_error_message_get(err1), "something bad happened"));
+
+   /* error range */
+   fail_if(eolian_type_type_get(rtp2) != EOLIAN_TYPE_ERROR);
+   fail_if(!(rtp1 = eolian_type_next_type_get(rtp2)));
+   fail_if(strcmp(eolian_type_name_get(rtp2), "Foo"));
+   fail_if(strcmp(eolian_type_name_get(rtp1), "Bar"));
+   /* it's the same Foo here */
+   fail_if(eolian_type_error_get(rtp2) != err1);
+   fail_if(!(err2 = eolian_type_error_get(rtp1)));
+   fail_if(strcmp(eolian_error_message_get(err1), "something bad happened"));
+   fail_if(strcmp(eolian_error_message_get(err2), "another bad thing happened"));
+
+   fail_if(!eolian_error_documentation_get(err1));
+   fail_if(!eolian_error_documentation_get(err2));
+   fail_if(eolian_error_is_beta(err1));
+   fail_if(!eolian_error_is_beta(err2));
+
+   eolian_state_free(eos);
+}
+EFL_END_TEST
+
 EFL_START_TEST(eolian_enum)
 {
    const Eolian_Enum_Type_Field *field = NULL;
@@ -1621,6 +1668,7 @@ void eolian_parsing_test(TCase *tc)
    tcase_add_test(tc, eolian_struct);
    tcase_add_test(tc, eolian_extern);
    tcase_add_test(tc, eolian_var);
+   tcase_add_test(tc, eolian_error);
    tcase_add_test(tc, eolian_enum);
    tcase_add_test(tc, eolian_class_funcs);
    tcase_add_test(tc, eolian_free_func);