schema compile BUGFIX evaluate augment with if-features
Disabled nodes must be removed. Test and some
refactoring included.
Fixes #1845
diff --git a/src/schema_compile_amend.c b/src/schema_compile_amend.c
index 483d759..35e7dc5 100644
--- a/src/schema_compile_amend.c
+++ b/src/schema_compile_amend.c
@@ -1732,6 +1732,96 @@
return ret;
}
+static LY_ERR
+lys_compile_augment_children(struct lysc_ctx *ctx, struct lysp_node_augment *aug_p, struct lysp_node *child,
+ struct lysc_node *target, ly_bool disabled_ch)
+{
+ LY_ERR rc = LY_SUCCESS;
+ struct lysp_node *pnode;
+ struct lysc_node *node;
+ struct lysc_when *when_shared = NULL;
+ ly_bool enabled, allow_mand = 0;
+ struct ly_set child_set = {0};
+ uint32_t i, opt_prev = ctx->compile_opts;
+
+ /* check for mandatory nodes
+ * - new cases augmenting some choice can have mandatory nodes
+ * - mandatory nodes are allowed only in case the augmentation is made conditional with a when statement
+ */
+ if (aug_p->when || (target->nodetype == LYS_CHOICE) || (ctx->cur_mod == target->module)) {
+ allow_mand = 1;
+ }
+
+ LY_LIST_FOR(child, pnode) {
+ /* check if the subnode can be connected to the found target (e.g. case cannot be inserted into container) */
+ if (((pnode->nodetype == LYS_CASE) && (target->nodetype != LYS_CHOICE)) ||
+ ((pnode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && !(target->nodetype & (LYS_CONTAINER | LYS_LIST))) ||
+ ((pnode->nodetype == LYS_USES) && (target->nodetype == LYS_CHOICE))) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid augment of %s node which is not allowed to contain %s node \"%s\".",
+ lys_nodetype2str(target->nodetype), lys_nodetype2str(pnode->nodetype), pnode->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ /* compile the children */
+ if (target->nodetype == LYS_CHOICE) {
+ LY_CHECK_GOTO(rc = lys_compile_node_choice_child(ctx, pnode, target, &child_set), cleanup);
+ } else if (target->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
+ if (target->nodetype == LYS_INPUT) {
+ ctx->compile_opts |= LYS_COMPILE_RPC_INPUT;
+ } else {
+ ctx->compile_opts |= LYS_COMPILE_RPC_OUTPUT;
+ }
+ LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
+ } else {
+ LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
+ }
+
+ /* eval if-features again for the rest of this node processing */
+ LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), cleanup);
+ if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ disabled_ch = 1;
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
+ }
+
+ /* since the augment node is not present in the compiled tree, we need to pass some of its
+ * statements to all its children */
+ for (i = 0; i < child_set.count; ++i) {
+ node = child_set.snodes[i];
+ if (!allow_mand && (node->flags & LYS_CONFIG_W) && (node->flags & LYS_MAND_TRUE)) {
+ node->flags &= ~LYS_MAND_TRUE;
+ lys_compile_mandatory_parents(target, 0);
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS,
+ "Invalid augment adding mandatory node \"%s\" without making it conditional via when statement.",
+ node->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+
+ if (aug_p->when) {
+ /* pass augment's when to all the children */
+ rc = lys_compile_when(ctx, aug_p->when, aug_p->flags, target, lysc_data_node(target), node, &when_shared);
+ LY_CHECK_GOTO(rc, cleanup);
+ }
+
+ if (disabled_ch) {
+ /* child is disabled either by its own if-features or by the augment if-features */
+ ly_set_add(&ctx->unres->disabled, node, 1, NULL);
+ }
+ }
+
+ /* next iter */
+ ly_set_erase(&child_set, NULL);
+ ctx->compile_opts = opt_prev;
+ }
+
+cleanup:
+ ly_set_erase(&child_set, NULL);
+ ctx->compile_opts = opt_prev;
+ return rc;
+}
+
/**
* @brief Compile the parsed augment connecting it into its target.
*
@@ -1748,168 +1838,54 @@
static LY_ERR
lys_compile_augment(struct lysc_ctx *ctx, struct lysp_node_augment *aug_p, struct lysc_node *target)
{
- LY_ERR ret = LY_SUCCESS;
- struct lysp_node *pnode;
- struct lysc_node *node;
- struct lysc_when *when_shared = NULL;
- struct lysc_node_action **actions;
- struct lysc_node_notif **notifs;
- ly_bool allow_mandatory = 0, enabled;
- struct ly_set child_set = {0};
- uint32_t i, opt_prev = ctx->compile_opts;
+ LY_ERR rc = LY_SUCCESS;
+ ly_bool enabled, disabled_ch = 0;
+ uint32_t opt_prev = ctx->compile_opts;
+ /* nodetype checks */
if (!(target->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_CHOICE | LYS_CASE | LYS_INPUT | LYS_OUTPUT | LYS_NOTIF))) {
LOGVAL(ctx->ctx, LYVE_REFERENCE,
"Augment's %s-schema-nodeid \"%s\" refers to a %s node which is not an allowed augment's target.",
aug_p->nodeid[0] == '/' ? "absolute" : "descendant", aug_p->nodeid, lys_nodetype2str(target->nodetype));
- ret = LY_EVALID;
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if (aug_p->actions && !lysc_node_actions_p(target)) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid augment of %s node which is not allowed to contain RPC/action node \"%s\".",
+ lys_nodetype2str(target->nodetype), aug_p->actions->name);
+ rc = LY_EVALID;
+ goto cleanup;
+ }
+ if (aug_p->notifs && !lysc_node_notifs_p(target)) {
+ LOGVAL(ctx->ctx, LYVE_REFERENCE,
+ "Invalid augment of %s node which is not allowed to contain notification node \"%s\".",
+ lys_nodetype2str(target->nodetype), aug_p->notifs->name);
+ rc = LY_EVALID;
goto cleanup;
}
- /* check for mandatory nodes
- * - new cases augmenting some choice can have mandatory nodes
- * - mandatory nodes are allowed only in case the augmentation is made conditional with a when statement
- */
- if (aug_p->when || (target->nodetype == LYS_CHOICE) || (ctx->cur_mod == target->module)) {
- allow_mandatory = 1;
+ /* augment if-features */
+ LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, aug_p->iffeatures, &enabled), cleanup);
+ if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+ disabled_ch = 1;
+ ctx->compile_opts |= LYS_COMPILE_DISABLED;
}
- LY_LIST_FOR(aug_p->child, pnode) {
- /* check if the subnode can be connected to the found target (e.g. case cannot be inserted into container) */
- if (((pnode->nodetype == LYS_CASE) && (target->nodetype != LYS_CHOICE)) ||
- ((pnode->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && !(target->nodetype & (LYS_CONTAINER | LYS_LIST))) ||
- ((pnode->nodetype == LYS_USES) && (target->nodetype == LYS_CHOICE))) {
- LOGVAL(ctx->ctx, LYVE_REFERENCE,
- "Invalid augment of %s node which is not allowed to contain %s node \"%s\".",
- lys_nodetype2str(target->nodetype), lys_nodetype2str(pnode->nodetype), pnode->name);
- ret = LY_EVALID;
- goto cleanup;
- }
+ /* augment children */
+ LY_CHECK_GOTO(rc = lys_compile_augment_children(ctx, aug_p, aug_p->child, target, disabled_ch), cleanup);
- /* compile the children */
- if (target->nodetype == LYS_CHOICE) {
- LY_CHECK_GOTO(ret = lys_compile_node_choice_child(ctx, pnode, target, &child_set), cleanup);
- } else if (target->nodetype & (LYS_INPUT | LYS_OUTPUT)) {
- if (target->nodetype == LYS_INPUT) {
- ctx->compile_opts |= LYS_COMPILE_RPC_INPUT;
- } else {
- ctx->compile_opts |= LYS_COMPILE_RPC_OUTPUT;
- }
- LY_CHECK_GOTO(ret = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
- } else {
- LY_CHECK_GOTO(ret = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
- }
+ /* augment actions */
+ rc = lys_compile_augment_children(ctx, aug_p, (struct lysp_node *)aug_p->actions, target, disabled_ch);
+ LY_CHECK_GOTO(rc, cleanup);
- /* eval if-features again for the rest of this node processing */
- LY_CHECK_GOTO(ret = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), cleanup);
- if (!enabled) {
- ctx->compile_opts |= LYS_COMPILE_DISABLED;
- }
-
- /* since the augment node is not present in the compiled tree, we need to pass some of its
- * statements to all its children */
- for (i = 0; i < child_set.count; ++i) {
- node = child_set.snodes[i];
- if (!allow_mandatory && (node->flags & LYS_CONFIG_W) && (node->flags & LYS_MAND_TRUE)) {
- node->flags &= ~LYS_MAND_TRUE;
- lys_compile_mandatory_parents(target, 0);
- LOGVAL(ctx->ctx, LYVE_SEMANTICS,
- "Invalid augment adding mandatory node \"%s\" without making it conditional via when statement.",
- node->name);
- ret = LY_EVALID;
- goto cleanup;
- }
-
- if (aug_p->when) {
- /* pass augment's when to all the children */
- ret = lys_compile_when(ctx, aug_p->when, aug_p->flags, target, lysc_data_node(target), node, &when_shared);
- LY_CHECK_GOTO(ret, cleanup);
- }
- }
- ly_set_erase(&child_set, NULL);
-
- /* restore options */
- ctx->compile_opts = opt_prev;
- }
-
- actions = lysc_node_actions_p(target);
- notifs = lysc_node_notifs_p(target);
-
- if (aug_p->actions) {
- if (!actions) {
- LOGVAL(ctx->ctx, LYVE_REFERENCE,
- "Invalid augment of %s node which is not allowed to contain RPC/action node \"%s\".",
- lys_nodetype2str(target->nodetype), aug_p->actions->name);
- ret = LY_EVALID;
- goto cleanup;
- }
-
- /* compile actions into the target */
- LY_LIST_FOR((struct lysp_node *)aug_p->actions, pnode) {
- LY_CHECK_GOTO(ret = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
-
- /* eval if-features again for the rest of this node processing */
- LY_CHECK_GOTO(ret = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), cleanup);
- if (!enabled) {
- ctx->compile_opts |= LYS_COMPILE_DISABLED;
- }
-
- /* since the augment node is not present in the compiled tree, we need to pass some of its
- * statements to all its children */
- for (i = 0; i < child_set.count; ++i) {
- node = child_set.snodes[i];
- if (aug_p->when) {
- /* pass augment's when to all the actions */
- ret = lys_compile_when(ctx, aug_p->when, aug_p->flags, target, lysc_data_node(target), node, &when_shared);
- LY_CHECK_GOTO(ret, cleanup);
- }
- }
- ly_set_erase(&child_set, NULL);
-
- /* restore options */
- ctx->compile_opts = opt_prev;
- }
- }
- if (aug_p->notifs) {
- if (!notifs) {
- LOGVAL(ctx->ctx, LYVE_REFERENCE,
- "Invalid augment of %s node which is not allowed to contain notification node \"%s\".",
- lys_nodetype2str(target->nodetype), aug_p->notifs->name);
- ret = LY_EVALID;
- goto cleanup;
- }
-
- /* compile notifications into the target */
- LY_LIST_FOR((struct lysp_node *)aug_p->notifs, pnode) {
- LY_CHECK_GOTO(ret = lys_compile_node(ctx, pnode, target, 0, &child_set), cleanup);
-
- /* eval if-features again for the rest of this node processing */
- LY_CHECK_GOTO(ret = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), cleanup);
- if (!enabled) {
- ctx->compile_opts |= LYS_COMPILE_DISABLED;
- }
-
- /* since the augment node is not present in the compiled tree, we need to pass some of its
- * statements to all its children */
- for (i = 0; i < child_set.count; ++i) {
- node = child_set.snodes[i];
- if (aug_p->when) {
- /* pass augment's when to all the actions */
- ret = lys_compile_when(ctx, aug_p->when, aug_p->flags, target, lysc_data_node(target), node, &when_shared);
- LY_CHECK_GOTO(ret, cleanup);
- }
- }
- ly_set_erase(&child_set, NULL);
-
- /* restore options */
- ctx->compile_opts = opt_prev;
- }
- }
+ /* augment notifications */
+ rc = lys_compile_augment_children(ctx, aug_p, (struct lysp_node *)aug_p->notifs, target, disabled_ch);
+ LY_CHECK_GOTO(rc, cleanup);
cleanup:
- ly_set_erase(&child_set, NULL);
ctx->compile_opts = opt_prev;
- return ret;
+ return rc;
}
LY_ERR
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index 6e0d29b..4416fc3 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -2835,6 +2835,13 @@
CHECK_LOG(NULL, NULL);
assert_null(mod->compiled->data);
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module m {namespace urn:m;prefix m;yang-version 1.1;"
+ "feature f;"
+ "container root;"
+ "augment /root {if-feature f; leaf l{type string;}}}", LYS_IN_YANG, &mod));
+ assert_non_null(cont = (const struct lysc_node_container *)mod->compiled->data);
+ assert_null(cont->child);
+
assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa; container c {leaf a {type string;}}"
"augment /x/ {leaf a {type int8;}}}", LYS_IN_YANG, &mod));
CHECK_LOG_CTX("Invalid absolute-schema-nodeid value \"/x/\" - unexpected end of expression.", "/aa:{augment='/x/'}");