validation BUGFIX mandatory nodes with when

Validation can be generated only if all the
node's when are true, it is disabled otherwise.
diff --git a/src/validation.c b/src/validation.c
index f8ec013..a5b5c68 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -90,61 +90,124 @@
 }
 
 /**
- * @brief Evaluate a single "when" condition.
+ * @brief Evaluate all relevant "when" conditions of a node.
  *
- * @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
- * @param[in] node Node whose existence depends on this when.
- * @param[in] when When to evaluate.
- * @param[in,out] diff Validation diff.
- * @return LY_ERR value (LY_EINCOMPLETE if a referenced node does not have its when evaluated)
+ * @param[in] tree Data tree.
+ * @param[in] node Node whose relevant when conditions will be evaluated.
+ * @param[in] schema Schema node of @p node. It may not be possible to use directly if @p node is opaque.
+ * @param[out] disabled First when that evaluated false, if any.
+ * @return LY_SUCCESS on success.
+ * @return LY_EINCOMPLETE if a referenced node does not have its when evaluated.
+ * @return LY_ERR value on error.
  */
 static LY_ERR
-lyd_validate_when(struct lyd_node **tree, struct lyd_node *node, struct lysc_when *when, struct lyd_node **diff)
+lyd_validate_node_when(const struct lyd_node *tree, const struct lyd_node *node, const struct lysc_node *schema,
+        const struct lysc_when **disabled)
 {
     LY_ERR ret;
     const struct lyd_node *ctx_node;
+    const struct lysc_when *when;
     struct lyxp_set xp_set;
+    LY_ARRAY_COUNT_TYPE u;
 
-    memset(&xp_set, 0, sizeof xp_set);
+    assert(!node->schema || (node->schema == schema));
 
-    if (when->context == node->schema) {
-        ctx_node = node;
-    } else {
-        assert((!when->context && !node->parent) || (when->context == node->parent->schema));
-        ctx_node = (struct lyd_node *)node->parent;
-    }
+    *disabled = NULL;
 
-    /* evaluate when */
-    ret = lyxp_eval(when->cond, node->schema->module, LY_PREF_SCHEMA_RESOLVED, when->prefixes, ctx_node, *tree,
-            &xp_set, LYXP_SCHEMA);
-    lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
+    do {
+        LY_ARRAY_FOR(schema->when, u) {
+            when = schema->when[u];
 
-    /* return error or LY_EINCOMPLETE for dependant unresolved when */
-    LY_CHECK_RET(ret);
-
-    /* take action based on the result */
-    if (!xp_set.val.bln) {
-        if (node->flags & LYD_WHEN_TRUE) {
-            /* autodelete */
-            if (LYD_DEL_IS_ROOT(*tree, node)) {
-                *tree = (*tree)->next;
+            /* get context node */
+            if (when->context == schema) {
+                ctx_node = node;
+            } else {
+                assert((!when->context && !node->parent) || (when->context == node->parent->schema));
+                ctx_node = (struct lyd_node *)node->parent;
             }
-            if (diff) {
-                /* add into diff */
-                LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff));
+
+            /* evaluate when */
+            memset(&xp_set, 0, sizeof xp_set);
+            ret = lyxp_eval(when->cond, schema->module, LY_PREF_SCHEMA_RESOLVED, when->prefixes, ctx_node, tree,
+                    &xp_set, LYXP_SCHEMA);
+            lyxp_set_cast(&xp_set, LYXP_SET_BOOLEAN);
+
+            /* return error or LY_EINCOMPLETE for dependant unresolved when */
+            LY_CHECK_RET(ret);
+
+            if (!xp_set.val.bln) {
+                /* false when */
+                *disabled = when;
+                return LY_SUCCESS;
             }
-            lyd_free_tree(node);
-        } else {
-            /* invalid data */
-            LOGVAL(LYD_CTX(node), LY_VLOG_LYD, node, LY_VCODE_NOWHEN, when->cond->expr);
-            return LY_EVALID;
         }
-    } else {
-        /* remember that when evaluated to true */
-        node->flags |= LYD_WHEN_TRUE;
+
+        schema = schema->parent;
+    } while (schema && (schema->nodetype & (LYS_CASE | LYS_CHOICE)));
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Evaluate when conditions of collected unres nodes.
+ *
+ * @param[in,out] tree Data tree, is updated if some nodes are autodeleted.
+ * @param[in] node_when Set with nodes with "when" conditions.
+ * @param[in,out] diff Validation diff.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR value on error.
+ */
+static LY_ERR
+lyd_validate_unres_when(struct lyd_node **tree, struct ly_set *node_when, struct lyd_node **diff)
+{
+    LY_ERR ret;
+    uint32_t i;
+    const struct lysc_when *disabled;
+    struct lyd_node *node;
+
+    if (!node_when->count) {
+        return LY_SUCCESS;
     }
 
-    return ret;
+    i = node_when->count;
+    do {
+        --i;
+        node = node_when->dnodes[i];
+
+        /* evaluate all when expressions that affect this node's existence */
+        ret = lyd_validate_node_when(*tree, node, node->schema, &disabled);
+        if (!ret) {
+            if (disabled) {
+                /* when false */
+                if (node->flags & LYD_WHEN_TRUE) {
+                    /* autodelete */
+                    if (LYD_DEL_IS_ROOT(*tree, node)) {
+                        *tree = (*tree)->next;
+                    }
+                    if (diff) {
+                        /* add into diff */
+                        LY_CHECK_RET(lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff));
+                    }
+                    lyd_free_tree(node);
+                } else {
+                    /* invalid data */
+                    LOGVAL(LYD_CTX(node), LY_VLOG_LYD, node, LY_VCODE_NOWHEN, disabled->cond->expr);
+                    return LY_EVALID;
+                }
+            } else {
+                /* when true */
+                node->flags |= LYD_WHEN_TRUE;
+            }
+
+            /* remove this node from the set, its when was resolved */
+            ly_set_rm_index(node_when, i, NULL);
+        } else if (ret != LY_EINCOMPLETE) {
+            /* error */
+            return ret;
+        }
+    } while (i);
+
+    return LY_SUCCESS;
 }
 
 LY_ERR
@@ -159,41 +222,7 @@
         uint32_t prev_count;
         do {
             prev_count = node_when->count;
-            i = 0;
-            while (i < node_when->count) {
-                /* evaluate all when expressions that affect this node's existence */
-                struct lyd_node *node = (struct lyd_node *)node_when->objs[i];
-                const struct lysc_node *schema = node->schema;
-                ly_bool unres_when = 0;
-
-                do {
-                    LY_ARRAY_COUNT_TYPE u;
-                    LY_ARRAY_FOR(schema->when, u) {
-                        ret = lyd_validate_when(tree, node, schema->when[u], diff);
-                        if (ret) {
-                            break;
-                        }
-                    }
-                    if (ret == LY_EINCOMPLETE) {
-                        /* could not evaluate this when */
-                        unres_when = 1;
-                        break;
-                    } else if (ret) {
-                        /* error */
-                        return ret;
-                    }
-                    schema = schema->parent;
-                } while (schema && (schema->nodetype & (LYS_CASE | LYS_CHOICE)));
-
-                if (unres_when) {
-                    /* keep in set and go to the next node */
-                    ++i;
-                } else {
-                    /* remove this node from the set */
-                    ly_set_rm_index(node_when, i, NULL);
-                }
-            }
-
+            LY_CHECK_RET(lyd_validate_unres_when(tree, node_when, diff));
             /* there must have been some when conditions resolved */
         } while (prev_count > node_when->count);
 
@@ -563,15 +592,77 @@
 }
 
 /**
+ * @brief Evaluate any "when" conditions of a non-existent data node with existing parent.
+ *
+ * @param[in] first First data sibling of the non-existing node.
+ * @param[in] parent Data parent of the non-existing node.
+ * @param[in] snode Schema node of the non-existing node.
+ * @param[out] disabled First when that evaluated false, if any.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lyd_validate_dummy_when(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
+        const struct lysc_when **disabled)
+{
+    LY_ERR ret = LY_SUCCESS;
+    struct lyd_node *tree, *dummy = NULL;
+
+    /* find root */
+    if (parent) {
+        tree = (struct lyd_node *)parent;
+        while (tree->parent) {
+            tree = lyd_parent(tree);
+        }
+        tree = lyd_first_sibling(tree);
+    } else {
+        assert(!first || !first->prev->next);
+        tree = (struct lyd_node *)first;
+    }
+
+    /* create dummy opaque node */
+    ret = lyd_new_opaq((struct lyd_node *)parent, snode->module->ctx, snode->name, NULL, snode->module->name, &dummy);
+    LY_CHECK_GOTO(ret, cleanup);
+
+    /* connect it if needed */
+    if (!parent) {
+        if (first) {
+            lyd_insert_sibling((struct lyd_node *)first, dummy, &tree);
+        } else {
+            assert(!tree);
+            tree = dummy;
+        }
+    }
+
+    /* evaluate all when */
+    ret = lyd_validate_node_when(tree, dummy, snode, disabled);
+    if (ret == LY_EINCOMPLETE) {
+        /* all other when must be resolved by now */
+        LOGINT(snode->module->ctx);
+        ret = LY_EINT;
+        goto cleanup;
+    } else if (ret) {
+        /* error */
+        goto cleanup;
+    }
+
+cleanup:
+    lyd_free_tree(dummy);
+    return ret;
+}
+
+/**
  * @brief Validate mandatory node existence.
  *
  * @param[in] first First sibling to search in.
+ * @param[in] parent Data parent.
  * @param[in] snode Schema node to validate.
  * @return LY_ERR value.
  */
 static LY_ERR
-lyd_validate_mandatory(const struct lyd_node *first, const struct lysc_node *snode)
+lyd_validate_mandatory(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode)
 {
+    const struct lysc_when *disabled;
+
     if (snode->nodetype == LYS_CHOICE) {
         /* some data of a choice case exist */
         if (lys_getnext_data(NULL, first, NULL, snode, NULL)) {
@@ -586,25 +677,38 @@
         }
     }
 
-    /* node instance not found */
-    LOGVAL(snode->module->ctx, LY_VLOG_LYSC, snode, LY_VCODE_NOMAND, snode->name);
-    return LY_EVALID;
+    disabled = NULL;
+    if (lysc_has_when(snode)) {
+        /* if there are any when conditions, they must be true for a validation error */
+        LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
+    }
+
+    if (!disabled) {
+        /* node instance not found */
+        LOGVAL(snode->module->ctx, LY_VLOG_LYSC, snode, LY_VCODE_NOMAND, snode->name);
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
 }
 
 /**
  * @brief Validate min/max-elements constraints, if any.
  *
  * @param[in] first First sibling to search in.
+ * @param[in] parent Data parent.
  * @param[in] snode Schema node to validate.
  * @param[in] min Minimum number of elements, 0 for no restriction.
  * @param[in] max Max number of elements, 0 for no restriction.
  * @return LY_ERR value.
  */
 static LY_ERR
-lyd_validate_minmax(const struct lyd_node *first, const struct lysc_node *snode, uint32_t min, uint32_t max)
+lyd_validate_minmax(const struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *snode,
+        uint32_t min, uint32_t max)
 {
     uint32_t count = 0;
     struct lyd_node *iter;
+    const struct lysc_when *disabled;
 
     assert(min || max);
 
@@ -627,8 +731,17 @@
 
     if (min) {
         assert(count < min);
-        LOGVAL(snode->module->ctx, LY_VLOG_LYSC, snode, LY_VCODE_NOMIN, snode->name);
-        return LY_EVALID;
+
+        disabled = NULL;
+        if (lysc_has_when(snode)) {
+            /* if there are any when conditions, they must be true for a validation error */
+            LY_CHECK_RET(lyd_validate_dummy_when(first, parent, snode, &disabled));
+        }
+
+        if (!disabled) {
+            LOGVAL(snode->module->ctx, LY_VLOG_LYSC, snode, LY_VCODE_NOMIN, snode->name);
+            return LY_EVALID;
+        }
     } else if (max && (count > max)) {
         LOGVAL(snode->module->ctx, LY_VLOG_LYSC, snode, LY_VCODE_NOMAX, snode->name);
         return LY_EVALID;
@@ -899,6 +1012,7 @@
  * @brief Validate data siblings based on generic schema node restrictions, recursively for schema-only nodes.
  *
  * @param[in] first First sibling to search in.
+ * @param[in] parent Data parent.
  * @param[in] sparent Schema parent of the nodes to check.
  * @param[in] mod Module of the nodes to check.
  * @param[in] val_opts Validation options, see @ref datavalidationoptions.
@@ -906,8 +1020,8 @@
  * @return LY_ERR value.
  */
 static LY_ERR
-lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lysc_node *sparent,
-        const struct lysc_module *mod, uint32_t val_opts, LYD_VALIDATE_OP op)
+lyd_validate_siblings_schema_r(const struct lyd_node *first, const struct lyd_node *parent,
+        const struct lysc_node *sparent, const struct lysc_module *mod, uint32_t val_opts, LYD_VALIDATE_OP op)
 {
     const struct lysc_node *snode = NULL;
     struct lysc_node_list *slist;
@@ -926,17 +1040,17 @@
         if (snode->nodetype == LYS_LIST) {
             slist = (struct lysc_node_list *)snode;
             if (slist->min || slist->max) {
-                LY_CHECK_RET(lyd_validate_minmax(first, snode, slist->min, slist->max));
+                LY_CHECK_RET(lyd_validate_minmax(first, parent, snode, slist->min, slist->max));
             }
         } else if (snode->nodetype == LYS_LEAFLIST) {
             sllist = (struct lysc_node_leaflist *)snode;
             if (sllist->min || sllist->max) {
-                LY_CHECK_RET(lyd_validate_minmax(first, snode, sllist->min, sllist->max));
+                LY_CHECK_RET(lyd_validate_minmax(first, parent, snode, sllist->min, sllist->max));
             }
 
         } else if (snode->flags & LYS_MAND_TRUE) {
             /* check generic mandatory existence */
-            LY_CHECK_RET(lyd_validate_mandatory(first, snode));
+            LY_CHECK_RET(lyd_validate_mandatory(first, parent, snode));
         }
 
         /* check unique */
@@ -949,7 +1063,7 @@
 
         if (snode->nodetype & (LYS_CHOICE | LYS_CASE)) {
             /* go recursively for schema-only nodes */
-            LY_CHECK_RET(lyd_validate_siblings_schema_r(first, snode, mod, val_opts, op));
+            LY_CHECK_RET(lyd_validate_siblings_schema_r(first, parent, snode, mod, val_opts, op));
         }
     }
 
@@ -1058,8 +1172,8 @@
 }
 
 LY_ERR
-lyd_validate_final_r(struct lyd_node *first, const struct lysc_node *sparent, const struct lys_module *mod, uint32_t val_opts,
-        LYD_VALIDATE_OP op)
+lyd_validate_final_r(struct lyd_node *first, const struct lyd_node *parent, const struct lysc_node *sparent,
+        const struct lys_module *mod, uint32_t val_opts, LYD_VALIDATE_OP op)
 {
     struct lyd_node *next = NULL, *node;
 
@@ -1099,11 +1213,11 @@
     }
 
     /* validate schema-based restrictions */
-    LY_CHECK_RET(lyd_validate_siblings_schema_r(first, sparent, mod ? mod->compiled : NULL, val_opts, op));
+    LY_CHECK_RET(lyd_validate_siblings_schema_r(first, parent, sparent, mod ? mod->compiled : NULL, val_opts, op));
 
     LY_LIST_FOR(first, node) {
         /* validate all children recursively */
-        LY_CHECK_RET(lyd_validate_final_r(lyd_child(node), node->schema, NULL, val_opts, op));
+        LY_CHECK_RET(lyd_validate_final_r(lyd_child(node), node, node->schema, NULL, val_opts, op));
 
         /* set default for containers */
         if ((node->schema->nodetype == LYS_CONTAINER) && !(node->schema->flags & LYS_PRESENCE)) {
@@ -1155,8 +1269,8 @@
             LY_CHECK_RET(lyd_validate_new(lyd_node_children_p((struct lyd_node *)node), node->schema, NULL, diff));
 
             /* add nested defaults */
-            LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_children_p((struct lyd_node *)node), NULL, NULL, type_check,
-                    when_check, val_opts & LYD_VALIDATE_NO_STATE ? LYD_IMPLICIT_NO_STATE : 0, diff));
+            LY_CHECK_RET(lyd_new_implicit_r(node, lyd_node_children_p((struct lyd_node *)node), NULL, NULL, NULL,
+                    NULL, val_opts & LYD_VALIDATE_NO_STATE ? LYD_IMPLICIT_NO_STATE : 0, diff));
         }
 
         if (!(node->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) && node->schema->when) {
@@ -1233,7 +1347,7 @@
         LY_CHECK_GOTO(ret, cleanup);
 
         /* perform final validation that assumes the data tree is final */
-        ret = lyd_validate_final_r(*first2, NULL, mod, val_opts, 0);
+        ret = lyd_validate_final_r(*first2, NULL, NULL, mod, val_opts, 0);
         LY_CHECK_GOTO(ret, cleanup);
     }
 
@@ -1367,7 +1481,7 @@
     LY_CHECK_GOTO(ret = lyd_validate_must(op_node, op), cleanup);
 
     /* final validation of all the descendants */
-    LY_CHECK_GOTO(ret = lyd_validate_final_r(lyd_child(op_node), op_node->schema, NULL, 0, op), cleanup);
+    LY_CHECK_GOTO(ret = lyd_validate_final_r(lyd_child(op_node), op_node, op_node->schema, NULL, 0, op), cleanup);
 
 cleanup:
     /* restore operation tree */