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