extension instances CHANGE access to the extension instance's substatements

Provide libyang a transparent access to the substatements of a specific
extension instances for various generic processing.

The patch introduces lysc_ext_substmt() to simplify work with the newly
added lysc_ext_instance.substmts sized array providing the extension
instance's substatements.
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index c2b9ba0..7491926 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -90,9 +90,12 @@
 
 /**
  * @brief Free the extension instance's data compiled with ::lys_compile_extension_instance().
- * TODO
+ *
+ * @param[in] libyang context
+ * @param[in] substmts The sized array of extension instance's substatements. The whole array is freed except the storage
+ * places which are expected to be covered by the extension plugin.
  */
-void lysc_extension_instance_free(struct ly_ctx *ctx, struct lysc_ext_substmt *substmts);
+void lysc_extension_instance_substatements_free(struct ly_ctx *ctx, struct lysc_ext_substmt *substmts);
 
 /**
  * @brief Duplicate the compiled extension (definition) structure.
@@ -105,6 +108,25 @@
 struct lysc_ext *lysc_ext_dup(struct lysc_ext *orig);
 
 /**
+ * @brief Get pointer to the storage of the specified substatement in the given extension instance.
+ *
+ * The function simplifies access into the ::lysc_ext_instance.substmts sized array.
+ *
+ * @param[in] ext Compiled extension instance to process.
+ * @param[in] substmt Extension substatement to search for.
+ * @param[out] instance_p Pointer where the storage of the @p substmt will be provided. The specific type returned depends
+ * on the @p substmt and can be found in the documentation of each ::ly_stmt value. Also note that some of the substatements
+ * (::lysc_node based or flags) can share the storage with other substatements. In case the pointer is NULL, still the return
+ * code can be used to at least know if the substatement is allowed for the extension.
+ * @param[out] cardinality_p Pointer to provide allowed cardinality of the substatements in the extension. Note that in some
+ * cases, the type of the storage depends also on the cardinality of the substatement.
+ * @return LY_SUCCESS if the @p substmt found.
+ * @return LY_ENOT in case the @p ext is not able to store (does not allow) the specified @p substmt.
+ */
+LY_ERR lysc_ext_substmt(const struct lysc_ext_instance *ext, enum ly_stmt substmt,
+        void **instance_p, enum ly_stmt_cardinality *cardinality_p);
+
+/**
  * @brief Update path in the compile context, which is used for logging where the compilation failed.
  *
  * @param[in] ctx Compile context with the path.
diff --git a/src/plugins_exts_metadata.c b/src/plugins_exts_metadata.c
index c25e4e1..99df4f0 100644
--- a/src/plugins_exts_metadata.c
+++ b/src/plugins_exts_metadata.c
@@ -26,22 +26,18 @@
 LYEXT_VERSION_CHECK
  */
 
-#define ANNOTATION_SUBSTMT_IFF     0
-#define ANNOTATION_SUBSTMT_UNITS   1
-#define ANNOTATION_SUBSTMT_STATUS  2
-#define ANNOTATION_SUBSTMT_TYPE    3
-#define ANNOTATION_SUBSTMT_DSC     4
-#define ANNOTATION_SUBSTMT_REF     5
-
-#define INIT_ANNOTATION_SUBSTMT { \
-    {LY_STMT_IF_FEATURE, LY_STMT_CARD_ANY, NULL}, \
-    {LY_STMT_UNITS, LY_STMT_CARD_OPT, NULL}, \
-    {LY_STMT_STATUS, LY_STMT_CARD_OPT, NULL}, \
-    {LY_STMT_TYPE, LY_STMT_CARD_MAND, NULL}, \
-    {LY_STMT_DESCRIPTION, LY_STMT_CARD_OPT, NULL}, \
-    {LY_STMT_REFERENCE, LY_STMT_CARD_OPT, NULL}, \
-    {0, 0, 0} /* terminating item */ \
-} \
+/**
+ * @brief Representation of the compiled metadata substatements - simplify storage for the items available via
+ * ::lysc_ext_substmt.
+ */
+struct lyext_metadata {
+    struct lysc_type *type;            /**< type of the metadata (mandatory) */
+    const char *units;                 /**< units of the leaf's type */
+    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    const char *dsc;                   /**< description */
+    const char *ref;                   /**< reference */
+    uint16_t flags;                    /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
+};
 
 /**
  * @brief Compile annotation extension instances.
@@ -54,7 +50,6 @@
     struct lyext_metadata *annotation;
     struct lysc_module *mod_c;
     LY_ARRAY_COUNT_TYPE u;
-    struct lysc_ext_substmt annotation_substmt[] = INIT_ANNOTATION_SUBSTMT;
 
     /* annotations can appear only at the top level of a YANG module or submodule */
     if (c_ext->parent_type != LYEXT_PAR_MODULE) {
@@ -81,15 +76,46 @@
     }
 
     /* compile annotation substatements */
+    LY_ARRAY_CREATE_RET(cctx->ctx, c_ext->substmts, 6, LY_EMEM);
     c_ext->data = annotation = calloc(1, sizeof *annotation);
-    LY_CHECK_ERR_RET(!annotation, LOGMEM(cctx->ctx), LY_EMEM);
-    annotation_substmt[ANNOTATION_SUBSTMT_IFF].storage = &annotation->iffeatures;
-    annotation_substmt[ANNOTATION_SUBSTMT_UNITS].storage = &annotation->units;
-    annotation_substmt[ANNOTATION_SUBSTMT_STATUS].storage = &annotation->flags;
-    annotation_substmt[ANNOTATION_SUBSTMT_TYPE].storage = &annotation->type;
-    /* description and reference are allowed, but not compiled */
+    if (!annotation) {
+        LOGMEM(cctx->ctx);
+        LY_ARRAY_FREE(c_ext->substmts);
+        c_ext->substmts = NULL;
+        return LY_EMEM;
+    }
 
-    LY_CHECK_RET(lys_compile_extension_instance(cctx, p_ext, annotation_substmt));
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[ANNOTATION_SUBSTMT_IFF].stmt = LY_STMT_IF_FEATURE;
+    c_ext->substmts[ANNOTATION_SUBSTMT_IFF].cardinality = LY_STMT_CARD_ANY;
+    c_ext->substmts[ANNOTATION_SUBSTMT_IFF].storage = &annotation->iffeatures;
+
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[ANNOTATION_SUBSTMT_UNITS].stmt = LY_STMT_UNITS;
+    c_ext->substmts[ANNOTATION_SUBSTMT_UNITS].cardinality = LY_STMT_CARD_OPT;
+    c_ext->substmts[ANNOTATION_SUBSTMT_UNITS].storage = &annotation->units;
+
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[ANNOTATION_SUBSTMT_STATUS].stmt = LY_STMT_STATUS;
+    c_ext->substmts[ANNOTATION_SUBSTMT_STATUS].cardinality = LY_STMT_CARD_OPT;
+    c_ext->substmts[ANNOTATION_SUBSTMT_STATUS].storage = &annotation->flags;
+
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[ANNOTATION_SUBSTMT_TYPE].stmt = LY_STMT_TYPE;
+    c_ext->substmts[ANNOTATION_SUBSTMT_TYPE].cardinality = LY_STMT_CARD_MAND;
+    c_ext->substmts[ANNOTATION_SUBSTMT_TYPE].storage = &annotation->type;
+
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[ANNOTATION_SUBSTMT_DSC].stmt = LY_STMT_DESCRIPTION;
+    c_ext->substmts[ANNOTATION_SUBSTMT_DSC].cardinality = LY_STMT_CARD_OPT;
+    c_ext->substmts[ANNOTATION_SUBSTMT_DSC].storage = &annotation->dsc;
+
+    LY_ARRAY_INCREMENT(c_ext->substmts);
+    c_ext->substmts[ANNOTATION_SUBSTMT_REF].stmt = LY_STMT_REFERENCE;
+    c_ext->substmts[ANNOTATION_SUBSTMT_REF].cardinality = LY_STMT_CARD_OPT;
+    c_ext->substmts[ANNOTATION_SUBSTMT_REF].storage = &annotation->ref;
+
+    LY_CHECK_RET(lys_compile_extension_instance(cctx, p_ext, c_ext->substmts));
 
     return LY_SUCCESS;
 }
@@ -102,20 +128,11 @@
 void
 annotation_free(struct ly_ctx *ctx, struct lysc_ext_instance *ext)
 {
-    struct lysc_ext_substmt annotation_substmt[] = INIT_ANNOTATION_SUBSTMT;
-
-    if (!ext->data) {
+    if (!ext->substmts) {
         return;
     }
 
-    struct lyext_metadata *annotation = (struct lyext_metadata *)ext->data;
-
-    annotation_substmt[ANNOTATION_SUBSTMT_IFF].storage = &annotation->iffeatures;
-    annotation_substmt[ANNOTATION_SUBSTMT_UNITS].storage = &annotation->units;
-    annotation_substmt[ANNOTATION_SUBSTMT_STATUS].storage = &annotation->flags;
-    annotation_substmt[ANNOTATION_SUBSTMT_TYPE].storage = &annotation->type;
-
-    lysc_extension_instance_free(ctx, annotation_substmt);
+    lysc_extension_instance_substatements_free(ctx, ext->substmts);
     free(ext->data);
 }
 
diff --git a/src/plugins_exts_metadata.h b/src/plugins_exts_metadata.h
index 97bfae5..541891b 100644
--- a/src/plugins_exts_metadata.h
+++ b/src/plugins_exts_metadata.h
@@ -21,15 +21,12 @@
 extern "C" {
 #endif
 
-/**
- * @brief Representation of the compiled metadata substatements as provided by libyang 2 metadata extension plugin.
- */
-struct lyext_metadata {
-    struct lysc_type *type;            /**< type of the metadata (mandatory) */
-    const char *units;                 /**< units of the leaf's type */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
-    uint16_t flags;                    /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values are allowed */
-};
+#define ANNOTATION_SUBSTMT_IFF     0 /**< index for the LY_STMT_IF_FEATURE substatement in annotation's ::lysc_ext_instance.substmts */
+#define ANNOTATION_SUBSTMT_UNITS   1 /**< index for the LY_STMT_UNITS substatement in annotation's ::lysc_ext_instance.substmts */
+#define ANNOTATION_SUBSTMT_STATUS  2 /**< index for the LY_STMT_STATUS substatement in annotation's ::lysc_ext_instance.substmts */
+#define ANNOTATION_SUBSTMT_TYPE    3 /**< index for the LY_STMT_TYPE substatement in annotation's ::lysc_ext_instance.substmts */
+#define ANNOTATION_SUBSTMT_DSC     4 /**< index for the LY_STMT_DSC substatement in annotation's ::lysc_ext_instance.substmts */
+#define ANNOTATION_SUBSTMT_REF     5 /**< index for the LY_STMT_REF substatement in annotation's ::lysc_ext_instance.substmts */
 
 #ifdef __cplusplus
 }
diff --git a/src/schema_compile.c b/src/schema_compile.c
index d0c2976..07133d7 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -205,6 +205,37 @@
     return orig;
 }
 
+LY_ERR
+lysc_ext_substmt(const struct lysc_ext_instance *ext, enum ly_stmt substmt, void **instance_p, enum ly_stmt_cardinality *cardinality_p)
+{
+    LY_ARRAY_COUNT_TYPE u;
+
+    LY_ARRAY_FOR(ext->substmts, u) {
+        if (LY_STMT_IS_NODE(substmt)) {
+            if (!LY_STMT_IS_NODE(ext->substmts[u].stmt)) {
+                continue;
+            }
+        } else if (LY_STMT_IS_OP(substmt)) {
+            if (!LY_STMT_IS_OP(ext->substmts[u].stmt)) {
+                continue;
+            }
+        } else if (ext->substmts[u].stmt != substmt) {
+            continue;
+        }
+
+        /* match */
+        if (cardinality_p) {
+            *cardinality_p = ext->substmts[u].cardinality;
+        }
+        if (instance_p) {
+            *instance_p = ext->substmts[u].storage;
+        }
+        return LY_SUCCESS;
+    }
+
+    return LY_ENOT;
+}
+
 static void
 lysc_unres_dflt_free(const struct ly_ctx *ctx, struct lysc_unres_dflt *r)
 {
@@ -602,12 +633,12 @@
         if (stmt->flags & (LYS_YIN_ATTR | LYS_YIN_ARGUMENT)) {
             continue;
         }
-        for (u = 0; substmts[u].stmt; ++u) {
+        LY_ARRAY_FOR(substmts, u) {
             if (substmts[u].stmt == stmt->kw) {
                 break;
             }
         }
-        if (!substmts[u].stmt) {
+        if (u == LY_ARRAY_COUNT(substmts)) {
             LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s%s%s\" extension instance.",
                     stmt->stmt, ext->name, ext->argument ? " " : "", ext->argument ? ext->argument : "");
             goto cleanup;
@@ -618,7 +649,7 @@
 
     /* keep order of the processing the same as the order in the defined substmts,
      * the order is important for some of the statements depending on others (e.g. type needs status and units) */
-    for (u = 0; substmts[u].stmt; ++u) {
+    LY_ARRAY_FOR(substmts, u) {
         ly_bool stmt_present = 0;
 
         for (stmt = ext->child; stmt; stmt = stmt->next) {
@@ -633,6 +664,8 @@
                     assert(substmts[u].cardinality < LY_STMT_CARD_SOME);
                     LY_CHECK_ERR_GOTO(r = lysp_stmt_parse(ctx, stmt, &substmts[u].storage, /* TODO */ NULL), ret = r, cleanup);
                     break;
+                case LY_STMT_DESCRIPTION:
+                case LY_STMT_REFERENCE:
                 case LY_STMT_UNITS: {
                     const char **units;
 
diff --git a/src/tree_data.c b/src/tree_data.c
index 16436fe..246b6c7 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -2412,8 +2412,8 @@
     LY_CHECK_ERR_GOTO(!mt, LOGMEM(mod->ctx); ret = LY_EMEM, cleanup);
     mt->parent = parent;
     mt->annotation = ant;
-    ret = lyd_value_store(mod->ctx, &mt->value, ((struct lyext_metadata *)ant->data)->type, value, value_len, dynamic,
-            format, prefix_data, hints, parent ? parent->schema : NULL, incomplete);
+    ret = lyd_value_store(mod->ctx, &mt->value,  *(const struct lysc_type **)ant->substmts[ANNOTATION_SUBSTMT_TYPE].storage,
+            value, value_len, dynamic, format, prefix_data, hints, parent ? parent->schema : NULL, incomplete);
     LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
     ret = lydict_insert(mod->ctx, name, name_len, &mt->name);
     LY_CHECK_ERR_GOTO(ret, free(mt), cleanup);
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 7e25fb7..546c587 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -1395,6 +1395,8 @@
     LYEXT_SUBSTMT insubstmt;         /**< value identifying placement of the extension instance */
     LYEXT_PARENT parent_type;        /**< type of the parent structure */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
+
+    struct lysc_ext_substmt *substmts; /**< list of allowed substatements with the storage to access the present substatements */
     void *data;                      /**< private plugins's data, not used by libyang */
 };
 
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 071de57..8bcfb5a 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -987,9 +987,11 @@
 }
 
 API void
-lysc_extension_instance_free(struct ly_ctx *ctx, struct lysc_ext_substmt *substmts)
+lysc_extension_instance_substatements_free(struct ly_ctx *ctx, struct lysc_ext_substmt *substmts)
 {
-    for (LY_ARRAY_COUNT_TYPE u = 0; substmts[u].stmt; ++u) {
+    LY_ARRAY_COUNT_TYPE u;
+
+    LY_ARRAY_FOR(substmts, u) {
         if (!substmts[u].storage) {
             continue;
         }
@@ -1012,6 +1014,8 @@
                 FREE_ARRAY(ctx, types, lysc_type2_free);
             }
             break;
+        case LY_STMT_DESCRIPTION:
+        case LY_STMT_REFERENCE:
         case LY_STMT_UNITS:
             if (substmts[u].cardinality < LY_STMT_CARD_SOME) {
                 /* single item */
@@ -1054,6 +1058,8 @@
             LOGINT(ctx);
         }
     }
+
+    LY_ARRAY_FREE(substmts);
 }
 
 void
diff --git a/src/validation.c b/src/validation.c
index dcf37a9..e122049 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -27,6 +27,7 @@
 #include "hash_table.h"
 #include "log.h"
 #include "parser_data.h"
+#include "plugins_exts.h"
 #include "plugins_exts_metadata.h"
 #include "plugins_types.h"
 #include "set.h"
@@ -269,7 +270,7 @@
             --i;
 
             struct lyd_meta *meta = meta_types->objs[i];
-            struct lysc_type *type = ((struct lyext_metadata *)meta->annotation->data)->type;
+            struct lysc_type *type = *(struct lysc_type **)meta->annotation->substmts[ANNOTATION_SUBSTMT_TYPE].storage;
 
             /* validate and store the value of the metadata */
             ret = lyd_value_validate_incomplete(LYD_CTX(meta->parent), type, &meta->value, meta->parent, *tree);
@@ -1322,7 +1323,7 @@
 
     LYD_TREE_DFS_BEGIN(root, node) {
         LY_LIST_FOR(node->meta, meta) {
-            if (((struct lyext_metadata *)meta->annotation->data)->type->plugin->validate) {
+            if ((*(const struct lysc_type **)meta->annotation->substmts[ANNOTATION_SUBSTMT_TYPE].storage)->plugin->validate) {
                 /* metadata type resolution */
                 LY_CHECK_RET(ly_set_add(meta_types, (void *)meta, 1, NULL));
             }