schema compile BUGFIX when cyclic check
... for cases when the context node differs
from the node that depends on the "when".
Fixes #2031
diff --git a/src/schema_compile.c b/src/schema_compile.c
index 001a3af..71a3fed 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -519,7 +519,7 @@
{
struct lyxp_set tmp_set;
struct lyxp_set_scnode *xp_scnode;
- uint32_t i, j;
+ uint32_t i, j, idx;
LY_ARRAY_COUNT_TYPE u;
LY_ERR ret = LY_SUCCESS;
@@ -565,36 +565,46 @@
}
for (j = 0; j < tmp_set.used; ++j) {
- /* skip roots'n'stuff */
- if (tmp_set.val.scnodes[j].type == LYXP_NODE_ELEM) {
- /* try to find this node in our set */
- uint32_t idx;
-
- if (lyxp_set_scnode_contains(set, tmp_set.val.scnodes[j].scnode, LYXP_NODE_ELEM, -1, &idx) &&
- (set->val.scnodes[idx].in_ctx == LYXP_SET_SCNODE_START_USED)) {
- LOGVAL(set->ctx, LYVE_SEMANTICS, "When condition cyclic dependency on the node \"%s\".",
- tmp_set.val.scnodes[j].scnode->name);
- ret = LY_EVALID;
- LOG_LOCBACK(1, 0, 0, 0);
- goto cleanup;
- }
-
- /* needs to be checked, if in both sets, will be ignored */
- tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
- } else {
- /* no when, nothing to check */
+ if (tmp_set.val.scnodes[j].type != LYXP_NODE_ELEM) {
+ /* skip roots'n'stuff, no when, nothing to check */
tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_NODE;
+ continue;
+ }
+
+ /* try to find this node in our set */
+ if (lyxp_set_scnode_contains(set, tmp_set.val.scnodes[j].scnode, LYXP_NODE_ELEM, -1, &idx) &&
+ (set->val.scnodes[idx].in_ctx == LYXP_SET_SCNODE_START_USED)) {
+ LOGVAL(set->ctx, LYVE_SEMANTICS, "When condition cyclic dependency on the node \"%s\".",
+ tmp_set.val.scnodes[j].scnode->name);
+ ret = LY_EVALID;
+ LOG_LOCBACK(1, 0, 0, 0);
+ goto cleanup;
+ }
+
+ /* needs to be checked, if in both sets, will be ignored */
+ tmp_set.val.scnodes[j].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
+ }
+
+ if (when->context != node) {
+ /* node actually depends on this "when", not the context node */
+ assert(tmp_set.val.scnodes[0].scnode == when->context);
+ if (tmp_set.val.scnodes[0].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* replace the non-traversed context node with the dependent node */
+ tmp_set.val.scnodes[0].scnode = (struct lysc_node *)node;
+ } else {
+ /* context node was traversed, so just add the dependent node */
+ ret = lyxp_set_scnode_insert_node(&tmp_set, node, LYXP_SET_SCNODE_START_USED, LYXP_AXIS_CHILD, NULL);
+ LY_CHECK_ERR_GOTO(ret, LOG_LOCBACK(1, 0, 0, 0), cleanup);
}
}
/* merge this set into the global when set */
lyxp_set_scnode_merge(set, &tmp_set);
}
+ LOG_LOCBACK(1, 0, 0, 0);
/* check when of non-data parents as well */
node = node->parent;
-
- LOG_LOCBACK(1, 0, 0, 0);
} while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
/* this node when was checked (xp_scnode could have been reallocd) */
@@ -640,6 +650,7 @@
{
struct lyxp_set tmp_set = {0};
uint32_t i, opts;
+ struct lysc_node *schema;
LY_ERR ret = LY_SUCCESS;
opts = LYXP_SCNODE_SCHEMA | ((node->flags & LYS_IS_OUTPUT) ? LYXP_SCNODE_OUTPUT : 0);
@@ -655,28 +666,45 @@
ctx->path[0] = '\0';
lysc_path(node, LYSC_PATH_LOG, ctx->path, LYSC_CTX_BUFSIZE);
for (i = 0; i < tmp_set.used; ++i) {
- /* skip roots'n'stuff */
- if ((tmp_set.val.scnodes[i].type == LYXP_NODE_ELEM) &&
- (tmp_set.val.scnodes[i].in_ctx != LYXP_SET_SCNODE_START_USED)) {
- struct lysc_node *schema = tmp_set.val.scnodes[i].scnode;
+ if (tmp_set.val.scnodes[i].type != LYXP_NODE_ELEM) {
+ /* skip roots'n'stuff */
+ continue;
+ } else if (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* context node not actually traversed */
+ continue;
+ }
- /* XPath expression cannot reference "lower" status than the node that has the definition */
- if (lysc_check_status(NULL, when->flags, node->module, node->name, schema->flags, schema->module,
- schema->name)) {
- LOGWRN(ctx->ctx, "When condition \"%s\" may be referencing %s node \"%s\".", when->cond->expr,
- (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
- }
+ schema = tmp_set.val.scnodes[i].scnode;
- /* check dummy node children/value accessing */
- if (lysc_data_parent(schema) == node) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node children.");
- ret = LY_EVALID;
- goto cleanup;
- } else if ((schema == node) && (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
- LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node value.");
- ret = LY_EVALID;
- goto cleanup;
- }
+ /* XPath expression cannot reference "lower" status than the node that has the definition */
+ if (lysc_check_status(NULL, when->flags, node->module, node->name, schema->flags, schema->module,
+ schema->name)) {
+ LOGWRN(ctx->ctx, "When condition \"%s\" may be referencing %s node \"%s\".", when->cond->expr,
+ (schema->flags == LYS_STATUS_OBSLT) ? "obsolete" : "deprecated", schema->name);
+ }
+
+ /* check dummy node children/value accessing */
+ if (lysc_data_parent(schema) == node) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node children.");
+ ret = LY_EVALID;
+ goto cleanup;
+ } else if ((schema == node) && (tmp_set.val.scnodes[i].in_ctx == LYXP_SET_SCNODE_ATOM_VAL)) {
+ LOGVAL(ctx->ctx, LYVE_SEMANTICS, "When condition is accessing its own conditional node value.");
+ ret = LY_EVALID;
+ goto cleanup;
+ }
+ }
+
+ if (when->context != node) {
+ /* node actually depends on this "when", not the context node */
+ assert(tmp_set.val.scnodes[0].scnode == when->context);
+ if (tmp_set.val.scnodes[0].in_ctx == LYXP_SET_SCNODE_START_USED) {
+ /* replace the non-traversed context node with the dependent node */
+ tmp_set.val.scnodes[0].scnode = (struct lysc_node *)node;
+ } else {
+ /* context node was traversed, so just add the dependent node */
+ ret = lyxp_set_scnode_insert_node(&tmp_set, node, LYXP_SET_SCNODE_START_USED, LYXP_AXIS_CHILD, NULL);
+ LY_CHECK_GOTO(ret, cleanup);
}
}
diff --git a/src/xpath.c b/src/xpath.c
index 3184350..b676dd9 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -43,8 +43,6 @@
#include "tree_schema_internal.h"
#include "xml.h"
-static LY_ERR set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
- enum lyxp_axis axis, uint32_t *index_p);
static LY_ERR reparse_or_expr(const struct ly_ctx *ctx, struct lyxp_expr *exp, uint32_t *tok_idx, uint32_t depth);
static LY_ERR eval_expr_select(const struct lyxp_expr *exp, uint32_t *tok_idx, enum lyxp_expr_type etype,
struct lyxp_set *set, uint32_t options);
@@ -893,7 +891,7 @@
(set->val.scnodes[i].in_ctx == LYXP_SET_SCNODE_START)) {
uint32_t idx;
- LY_CHECK_ERR_RET(set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
+ LY_CHECK_ERR_RET(lyxp_set_scnode_insert_node(ret, set->val.scnodes[i].scnode, set->val.scnodes[i].type,
set->val.scnodes[i].axis, &idx), lyxp_set_free(ret), NULL);
/* coverity seems to think scnodes can be NULL */
if (!ret->val.scnodes) {
@@ -1318,19 +1316,8 @@
set_insert_node_hash(set, (struct lyd_node *)node, node_type);
}
-/**
- * @brief Insert schema node into set.
- *
- * @param[in] set Set to insert into.
- * @param[in] node Node to insert.
- * @param[in] node_type Node type of @p node.
- * @param[in] axis Axis that @p node was reached on.
- * @param[out] index_p Optional pointer to store index if the inserted @p node.
- * @return LY_SUCCESS on success.
- * @return LY_EMEM on memory allocation failure.
- */
-static LY_ERR
-set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+LY_ERR
+lyxp_set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
enum lyxp_axis axis, uint32_t *index_p)
{
uint32_t index;
@@ -1350,7 +1337,7 @@
}
if (lyxp_set_scnode_contains(set, node, node_type, -1, &index)) {
- /* BUG if axes differs, this new one is thrown away */
+ /* BUG if axes differ, this new one is thrown away */
set->val.scnodes[index].in_ctx = LYXP_SET_SCNODE_ATOM_CTX;
} else {
if (set->used == set->size) {
@@ -3938,10 +3925,10 @@
set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
if (set->cur_scnode) {
- LY_CHECK_RET(set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, set->cur_scnode, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
} else {
/* root node */
- LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
}
} else {
lyxp_set_free_content(set);
@@ -4007,7 +3994,7 @@
target = p[LY_ARRAY_COUNT(p) - 1].node;
ly_path_free(set->ctx, p);
- LY_CHECK_RET(set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, target, LYXP_NODE_ELEM, LYXP_AXIS_SELF, NULL));
} /* else the target was found before but is disabled so it was removed */
}
@@ -5661,7 +5648,7 @@
if (options & LYXP_SCNODE_ALL) {
set_scnode_clear_ctx(set, LYXP_SET_SCNODE_ATOM_NODE);
- LY_CHECK_RET(set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, NULL, set->root_type, LYXP_AXIS_SELF, NULL));
} else {
set->type = LYXP_SET_NODE_SET;
set->used = 0;
@@ -6714,7 +6701,7 @@
}
/* insert */
- LY_CHECK_RET(set_scnode_insert_node(set, iter, iter_type, axis, &idx));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, iter, iter_type, axis, &idx));
/* we need to prevent these nodes from being considered in this moveto */
if ((idx < orig_used) && (idx > i)) {
@@ -6727,7 +6714,7 @@
(set->val.scnodes[i].type == LYXP_NODE_ELEM) && !ly_nested_ext_schema(NULL, set->val.scnodes[i].scnode,
moveto_mod->name, strlen(moveto_mod->name), LY_VALUE_JSON, NULL, ncname, strlen(ncname), &iter, NULL)) {
/* there is a matching node from an extension, use it */
- LY_CHECK_RET(set_scnode_insert_node(set, iter, LYXP_NODE_ELEM, axis, &idx));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, iter, LYXP_NODE_ELEM, axis, &idx));
if ((idx < orig_used) && (idx > i)) {
set->val.scnodes[idx].in_ctx = LYXP_SET_SCNODE_ATOM_NEW_CTX;
temp_ctx = 1;
@@ -6871,7 +6858,7 @@
goto skip_children;
}
} else {
- LY_CHECK_RET(set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, elem, LYXP_NODE_ELEM, LYXP_AXIS_DESCENDANT, NULL));
}
} else if (rc == LY_EINVAL) {
goto skip_children;
@@ -9876,7 +9863,7 @@
memset(set, 0, sizeof *set);
set->type = LYXP_SET_SCNODE_SET;
set->root_type = lyxp_get_root_type(NULL, ctx_scnode, options);
- LY_CHECK_RET(set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
+ LY_CHECK_RET(lyxp_set_scnode_insert_node(set, ctx_scnode, ctx_scnode ? LYXP_NODE_ELEM : set->root_type, LYXP_AXIS_SELF, NULL));
set->val.scnodes[0].in_ctx = LYXP_SET_SCNODE_START;
set->ctx = (struct ly_ctx *)ctx;
diff --git a/src/xpath.h b/src/xpath.h
index cfa1fe6..96f7419 100644
--- a/src/xpath.h
+++ b/src/xpath.h
@@ -422,6 +422,20 @@
void lyxp_set_scnode_merge(struct lyxp_set *set1, struct lyxp_set *set2);
/**
+ * @brief Insert schema node into set.
+ *
+ * @param[in] set Set to insert into.
+ * @param[in] node Node to insert.
+ * @param[in] node_type Node type of @p node.
+ * @param[in] axis Axis that @p node was reached on.
+ * @param[out] index_p Optional pointer to store the index of the inserted @p node.
+ * @return LY_SUCCESS on success.
+ * @return LY_EMEM on memory allocation failure.
+ */
+LY_ERR lyxp_set_scnode_insert_node(struct lyxp_set *set, const struct lysc_node *node, enum lyxp_node_type node_type,
+ enum lyxp_axis axis, uint32_t *index_p);
+
+/**
* @brief Parse an XPath expression into a structure of tokens.
* Logs directly.
*
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index 09c63a3..e9f1f10 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -3901,6 +3901,55 @@
" }"
"}",
LYS_IN_YANG, NULL));
+
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb,
+ "module d1 {"
+ " namespace urn:d1;"
+ " prefix d1;"
+ " container ifm {"
+ " container interfaces {"
+ " list interface {"
+ " key \"name\";"
+ " leaf name {"
+ " type string;"
+ " }"
+ " container ethernet {"
+ " container main-interface {"
+ " container l2-attribute {"
+ " when \"not(/d1:ifm/d1:interfaces/d1:interface/d1:trunk/d1:members/d1:member[d1:name=current()/../../../d1:name])\";"
+ " presence \"\";"
+ " }"
+ " }"
+ " }"
+ " container trunk {"
+ " container members {"
+ " list member {"
+ " key \"name\";"
+ " leaf name {"
+ " type string;"
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ " }"
+ "}");
+ assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX,
+ "module d2 {"
+ " namespace \"urn:d2\";"
+ " prefix d2;"
+ " import d1 {"
+ " prefix d1;"
+ " }"
+ " augment \"/d1:ifm/d1:interfaces/d1:interface/d1:ethernet/d1:main-interface\" {"
+ " when \"not(d1:l2-attribute)\";"
+ " container extra-attribute {"
+ " presence \"\";"
+ " }"
+ " }"
+ "}",
+ LYS_IN_YANG, NULL));
}
static void