schema compile FEATURE disabled bits/enums unres

Do not remove the values right away to allow
other checks (XPath) to finish first.
Fixes cesnet/netopeer2#1048
diff --git a/src/schema_compile.c b/src/schema_compile.c
index 5978a45..5b52a19 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -990,6 +990,75 @@
 }
 
 /**
+ * @brief Remove all disabled bits/enums from a sized array.
+ *
+ * @param[in] ctx Context with the dictionary.
+ * @param[in] items Sized array of bits/enums.
+ */
+static void
+lys_compile_unres_disabled_bitenum_remove(struct ly_ctx *ctx, struct lysc_type_bitenum_item *items)
+{
+    LY_ARRAY_COUNT_TYPE u = 0, last_u;
+
+    while (u < LY_ARRAY_COUNT(items)) {
+        if (items[u].flags & LYS_DISABLED) {
+            /* free the disabled item */
+            lysc_enum_item_free(ctx, &items[u]);
+
+            /* replace it with the following items */
+            last_u = LY_ARRAY_COUNT(items) - 1;
+            if (u < last_u) {
+                memmove(items + u, items + u + 1, (last_u - u) * sizeof *items);
+            }
+
+            /* one item less */
+            LY_ARRAY_DECREMENT(items);
+            continue;
+        }
+
+        ++u;
+    }
+}
+
+/**
+ * @brief Find and remove all disabled bits/enums in a leaf/leaf-list type.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] leaf Leaf/leaf-list to check.
+ * @return LY_ERR value
+ */
+static LY_ERR
+lys_compile_unres_disabled_bitenum(struct lysc_ctx *ctx, struct lysc_node_leaf *leaf)
+{
+    struct lysc_type **t;
+    LY_ARRAY_COUNT_TYPE u, count;
+    struct lysc_type_enum *ent;
+
+    if (leaf->type->basetype == LY_TYPE_UNION) {
+        t = ((struct lysc_type_union *)leaf->type)->types;
+        count = LY_ARRAY_COUNT(t);
+    } else {
+        t = &leaf->type;
+        count = 1;
+    }
+    for (u = 0; u < count; ++u) {
+        if ((t[u]->basetype == LY_TYPE_BITS) || (t[u]->basetype == LY_TYPE_ENUM)) {
+            /* remove all disabled items */
+            ent = (struct lysc_type_enum *)(t[u]);
+            lys_compile_unres_disabled_bitenum_remove(ctx->ctx, ent->enums);
+
+            if (!LY_ARRAY_COUNT(ent->enums)) {
+                LOGVAL(ctx->ctx, LYVE_SEMANTICS, "%s type of node \"%s\" without any (or all disabled) valid values.",
+                        (ent->basetype == LY_TYPE_BITS) ? "Bits" : "Enumeration", leaf->name);
+                return LY_EVALID;
+            }
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
  * @brief Check leafref for its target existence on a complete compiled schema tree.
  *
  * @param[in] ctx Compile context.
@@ -1375,6 +1444,20 @@
         ly_set_rm_index(&ds_unres->musts, i, NULL);
     }
 
+    /* remove disabled enums/bits */
+    for (i = 0; i < ds_unres->disabled_bitenums.count; ++i) {
+        node = ds_unres->disabled_bitenums.objs[i];
+        cctx.cur_mod = node->module;
+        cctx.pmod = node->module->parsed;
+
+        LOG_LOCSET(node, NULL, NULL, NULL);
+        ret = lys_compile_unres_disabled_bitenum(&cctx, (struct lysc_node_leaf *)node);
+        LOG_LOCBACK(1, 0, 0, 0);
+        LY_CHECK_RET(ret);
+
+        ly_set_rm_index(&ds_unres->disabled_bitenums, i, NULL);
+    }
+
     /* finish incomplete default values compilation */
     while (ds_unres->dflts.count) {
         i = ds_unres->dflts.count - 1;
@@ -1462,8 +1545,9 @@
         lysc_unres_dflt_free(ctx, unres->ds_unres.dflts.objs[i]);
     }
     ly_set_erase(&unres->ds_unres.dflts, NULL);
-    ly_set_erase(&unres->ds_unres.disabled_leafrefs, free);
     ly_set_erase(&unres->ds_unres.disabled, NULL);
+    ly_set_erase(&unres->ds_unres.disabled_leafrefs, free);
+    ly_set_erase(&unres->ds_unres.disabled_bitenums, NULL);
 }
 
 /**
diff --git a/src/schema_compile.h b/src/schema_compile.h
index 746b326..37f69dd 100644
--- a/src/schema_compile.h
+++ b/src/schema_compile.h
@@ -65,8 +65,11 @@
     struct ly_set musts;                /**< set of musts to check */
     struct ly_set leafrefs;             /**< to validate target of leafrefs */
     struct ly_set dflts;                /**< set of incomplete default values */
-    struct ly_set disabled;             /**< set of compiled nodes whose if-feature(s) was not satisfied (stored ::lysc_node *) */
+    struct ly_set disabled;             /**< set of compiled nodes whose if-feature(s) was not satisfied
+                                             (stored ::lysc_node *) */
     struct ly_set disabled_leafrefs;    /**< subset of the lys_depset_unres.disabled to validate target of disabled leafrefs */
+    struct ly_set disabled_bitenums;    /**< set of enumation/bits leaves/leaf-lists with bits/enums to disable
+                                             (stored ::lysc_node_leaf *) */
 };
 
 /**
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index 74444ab..2128ac7 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -244,6 +244,26 @@
 }
 
 /**
+ * @brief Add a bits/enumeration type to unres.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] leaf Leaf of type bits/enumeration whose disabled items to free.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysc_unres_bitenum_add(struct lysc_ctx *ctx, struct lysc_node_leaf *leaf)
+{
+    if (ctx->compile_opts & (LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING)) {
+        /* skip groupings and redundant for disabled nodes */
+        return LY_SUCCESS;
+    }
+
+    LY_CHECK_RET(ly_set_add(&ctx->unres->disabled_bitenums, leaf, 1, NULL));
+
+    return LY_SUCCESS;
+}
+
+/**
  * @brief Duplicate the compiled pattern structure.
  *
  * Instead of duplicating memory, the reference counter in the @p orig is increased.
@@ -1457,12 +1477,6 @@
             }
         }
 
-        /* evaluate if-ffeatures */
-        LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, enums_p[u].iffeatures, &enabled));
-        if (!enabled) {
-            continue;
-        }
-
         /* add new enum/bit */
         LY_ARRAY_NEW_RET(ctx->ctx, *bitenums, e, LY_EMEM);
         DUP_STRING_GOTO(ctx->ctx, enums_p[u].name, e->name, ret, done);
@@ -1476,6 +1490,13 @@
         }
         COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, ret, done);
 
+        /* evaluate if-ffeatures */
+        LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, enums_p[u].iffeatures, &enabled));
+        if (!enabled) {
+            /* set only flag, later resolved and removed */
+            e->flags |= LYS_DISABLED;
+        }
+
         if (basetype == LY_TYPE_BITS) {
             /* keep bits ordered by position */
             for (v = u; v && (*bitenums)[v - 1].position > e->position; --v) {}
@@ -2783,6 +2804,9 @@
         struct lysc_node_leaf *leaf)
 {
     struct lysp_qname *dflt;
+    struct lysc_type **t;
+    LY_ARRAY_COUNT_TYPE u, count;
+    ly_bool in_unres = 0;
 
     LY_CHECK_RET(lys_compile_type(ctx, context_node, leaf->flags, leaf->name, type_p, &leaf->type,
             leaf->units ? NULL : &leaf->units, &dflt));
@@ -2795,10 +2819,24 @@
     /* store leafref(s) to be resolved */
     LY_CHECK_RET(lysc_unres_leafref_add(ctx, leaf, type_p->pmod));
 
-    if (leaf->type->basetype == LY_TYPE_EMPTY) {
-        if ((leaf->nodetype == LYS_LEAFLIST) && (ctx->pmod->version < LYS_VERSION_1_1)) {
-            LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
-            return LY_EVALID;
+    /* type-specific checks */
+    if (leaf->type->basetype == LY_TYPE_UNION) {
+        t = ((struct lysc_type_union *)leaf->type)->types;
+        count = LY_ARRAY_COUNT(t);
+    } else {
+        t = &leaf->type;
+        count = 1;
+    }
+    for (u = 0; u < count; ++u) {
+        if (t[u]->basetype == LY_TYPE_EMPTY) {
+            if ((leaf->nodetype == LYS_LEAFLIST) && (ctx->pmod->version < LYS_VERSION_1_1)) {
+                LOGVAL(ctx->ctx, LYVE_SEMANTICS, "Leaf-list of type \"empty\" is allowed only in YANG 1.1 modules.");
+                return LY_EVALID;
+            }
+        } else if (!in_unres && ((t[u]->basetype == LY_TYPE_BITS) || (t[u]->basetype == LY_TYPE_ENUM))) {
+            /* store in unres for all disabled bits/enums to be removed */
+            LY_CHECK_RET(lysc_unres_bitenum_add(ctx, leaf));
+            in_unres = 1;
         }
     }
 
diff --git a/src/tree_schema.h b/src/tree_schema.h
index d51174e..e02baeb 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -840,11 +840,11 @@
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       2 LYS_CONFIG_R     |x|x|x|x|x|x|x| | | | | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x|x|x|x| |x|
+ *       3 LYS_STATUS_CURR  |x|x|x|x|x|x|x|x|x|x|x|x|x|x|
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x|x|x|x| |x|
+ *       4 LYS_STATUS_DEPRC |x|x|x|x|x|x|x|x|x|x|x|x|x|x|
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x|x|x|x| |x|
+ *       5 LYS_STATUS_OBSLT |x|x|x|x|x|x|x|x|x|x|x|x|x|x|
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       6 LYS_MAND_TRUE    |x|x|x|x|x|x| | | | | | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -856,6 +856,7 @@
  *         LYS_UNIQUE       | | |x| | | | | | | | | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       9 LYS_KEY          | | |x| | | | | | | | | | | |
+ *         LYS_DISABLED     | | | | | | | | | | | | |x| |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *      10 LYS_SET_DFLT     | | |x|x| | |x| | | | | | | |
  *         LYS_IS_ENUM      | | | | | | | | | | | | |x| |
@@ -903,6 +904,7 @@
 #define LYS_UNIQUE       0x80        /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
 #define LYS_KEY          0x0100      /**< flag for leafs being a key of a list, applicable only to ::lysc_node_leaf */
 #define LYS_KEYLESS      0x0200      /**< flag for list without any key, applicable only to ::lysc_node_list */
+#define LYS_DISABLED     0x0100      /**< internal flag for a disabled statement, used only for bits/enums */
 #define LYS_FENABLED     0x20        /**< feature enabled flag, applicable only to ::lysp_feature. */
 #define LYS_ORDBY_SYSTEM 0x80        /**< ordered-by system configuration lists, applicable only to
                                           ::lysc_node_leaflist/::lysp_node_leaflist and ::lysc_node_list/::lysp_node_list */
@@ -1571,8 +1573,8 @@
         int32_t value;           /**< integer value associated with the enumeration */
         uint32_t position;       /**< non-negative integer value associated with the bit */
     };
-    uint16_t flags;              /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ and LYS_SET_VALUE
-                                          values are allowed */
+    uint16_t flags;              /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ and LYS_IS_ENUM values
+                                      are allowed */
 };
 
 struct lysc_type_enum {
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 3b9dfdb..3f3d753 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -648,7 +648,7 @@
     free(*pattern);
 }
 
-static void
+void
 lysc_enum_item_free(struct ly_ctx *ctx, struct lysc_type_bitenum_item *item)
 {
     lydict_remove(ctx, item->name);
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index f7f7be0..9f78dd9 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -693,7 +693,16 @@
 void lysp_node_free(struct ly_ctx *ctx, struct lysp_node *node);
 
 /**
+ * @brief Free a bit/enum item.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] item Bit/enum item to free.
+ */
+void lysc_enum_item_free(struct ly_ctx *ctx, struct lysc_type_bitenum_item *item);
+
+/**
  * @brief Free the compiled type structure.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] type Compiled type structure to be freed. The structure has refcount, so it is freed only in case the value is decreased to 0.
  */
@@ -701,6 +710,7 @@
 
 /**
  * @brief Free the compiled if-feature structure.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] iff Compiled if-feature structure to be cleaned.
  * Since the structure is typically part of the sized array, the structure itself is not freed.
@@ -709,6 +719,7 @@
 
 /**
  * @brief Free the compiled identity structure.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] ident Compiled identity structure to be cleaned.
  * Since the structure is typically part of the sized array, the structure itself is not freed.
@@ -717,6 +728,7 @@
 
 /**
  * @brief Free the compiled must structure.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] must Compiled must structure to be cleaned.
  * Since the structure is typically part of the sized array, the structure itself is not freed.
@@ -741,6 +753,7 @@
 
 /**
  * @brief Free the items inside the compiled Notification structure.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] notif Compiled Notification structure to be cleaned.
  * Since the structure is typically part of the sized array, the structure itself is not freed.
@@ -749,6 +762,7 @@
 
 /**
  * @brief Free the compiled extension definition and NULL the provided pointer.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] ext Compiled extension definition to be freed.
  */
@@ -756,6 +770,7 @@
 
 /**
  * @brief Free the compiled extension instance structure.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] ext Compiled extension instance structure to be cleaned.
  * Since the structure is typically part of the sized array, the structure itself is not freed.
@@ -764,6 +779,7 @@
 
 /**
  * @brief Free the compiled node structure.
+ *
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in] node Compiled node structure to be freed.
  * @param[in] unlink Whether to first unlink the node before freeing.
@@ -783,12 +799,14 @@
 
 /**
  * @brief Free the compiled schema structure.
+ *
  * @param[in,out] module Compiled schema module structure to free.
  */
 void lysc_module_free(struct lysc_module *module);
 
 /**
  * @brief Free the schema structure. It just frees, it does not remove the schema from its context.
+ *
  * @param[in,out] module Schema module structure to free.
  */
 void lys_module_free(struct lys_module *module);
diff --git a/tests/utests/schema/test_schema.c b/tests/utests/schema/test_schema.c
index f72ae43..949ffdf 100644
--- a/tests/utests/schema/test_schema.c
+++ b/tests/utests/schema/test_schema.c
@@ -50,6 +50,7 @@
 void test_accessible_tree(void **state);
 void test_includes(void **state);
 void test_key_order(void **state);
+void test_disabled_enum(void **state);
 
 /* test_schema_stmts.c */
 void test_identity(void **state);
@@ -74,6 +75,7 @@
         UTEST(test_accessible_tree),
         UTEST(test_includes),
         UTEST(test_key_order),
+        UTEST(test_disabled_enum),
 
         /** test_schema_stmts.c */
         UTEST(test_identity),
diff --git a/tests/utests/schema/test_schema_common.c b/tests/utests/schema/test_schema_common.c
index 8c2a6a1..3c78d0d 100644
--- a/tests/utests/schema/test_schema_common.c
+++ b/tests/utests/schema/test_schema_common.c
@@ -1051,3 +1051,41 @@
     node = node->next;
     assert_string_equal("k4", node->name);
 }
+
+void
+test_disabled_enum(void **state)
+{
+    const char *str;
+
+    /* no enabled enum */
+    str = "module a {"
+            "yang-version 1.1;"
+            "namespace urn:test:a;"
+            "prefix a;"
+            "feature f;"
+            "leaf l {type enumeration {"
+            "  enum e1 {if-feature f;}"
+            "  enum e2 {if-feature f;}"
+            "}}"
+            "}";
+    assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+    CHECK_LOG_CTX("Enumeration type of node \"l\" without any (or all disabled) valid values.", "Schema location /a:l.");
+
+    /* disabled default value */
+    str = "module a {"
+            "yang-version 1.1;"
+            "namespace urn:test:a;"
+            "prefix a;"
+            "feature f;"
+            "leaf l {"
+            "  type enumeration {"
+            "    enum e1 {if-feature f;}"
+            "    enum e2;"
+            "  }"
+            "  default e1;"
+            "}"
+            "}";
+    assert_int_equal(lys_parse_mem(UTEST_LYCTX, str, LYS_IN_YANG, NULL), LY_EVALID);
+    CHECK_LOG_CTX("Invalid default - value does not fit the type (Invalid enumeration value \"e1\".).",
+            "Schema location /a:l.");
+}