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.");
+}