validation BUGFIX complex new default and explicit node validation
diff --git a/src/validation.c b/src/validation.c
index 7065cd0..31359a0 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -530,105 +530,187 @@
 }
 
 /**
- * @brief Properly delete a node as part of autodelete validation tasks.
+ * @brief Properly delete a node as part of auto-delete validation tasks.
  *
  * @param[in,out] first First sibling, is updated if needed.
- * @param[in] node Node instance to delete.
+ * @param[in] del Node instance to delete.
  * @param[in] mod Module of the siblings, NULL for nested siblings.
- * @param[in,out] next_p Temporary LY_LIST_FOR_SAFE next pointer, is updated if needed.
+ * @param[in,out] node Current iteration node, update it if it is deleted.
  * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
  */
-static void
-lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *node, const struct lys_module *mod,
-        struct lyd_node **next_p, struct lyd_node **diff)
+static ly_bool
+lyd_validate_autodel_node_del(struct lyd_node **first, struct lyd_node *del, const struct lys_module *mod,
+        struct lyd_node **node, struct lyd_node **diff)
 {
     struct lyd_node *iter;
+    ly_bool node_autodel = 0;
 
-    lyd_del_move_root(first, node, mod);
-    if (node == *next_p) {
-        *next_p = (*next_p)->next;
+    lyd_del_move_root(first, del, mod);
+    if (del == *node) {
+        *node = (*node)->next;
+        node_autodel = 1;
     }
     if (diff) {
         /* add into diff */
-        if ((node->schema->nodetype == LYS_CONTAINER) && !(node->schema->flags & LYS_PRESENCE)) {
+        if ((del->schema->nodetype == LYS_CONTAINER) && !(del->schema->flags & LYS_PRESENCE)) {
             /* we do not want to track NP container changes, but remember any removed children */
-            LY_LIST_FOR(lyd_child(node), iter) {
+            LY_LIST_FOR(lyd_child(del), iter) {
                 lyd_val_diff_add(iter, LYD_DIFF_OP_DELETE, diff);
             }
         } else {
-            lyd_val_diff_add(node, LYD_DIFF_OP_DELETE, diff);
+            lyd_val_diff_add(del, LYD_DIFF_OP_DELETE, diff);
         }
     }
-    lyd_free_tree(node);
+    lyd_free_tree(del);
+
+    return node_autodel;
 }
 
 /**
- * @brief Autodelete old instances to prevent validation errors.
+ * @brief Auto-delete leaf-list default instances to prevent validation errors.
  *
  * @param[in,out] first First sibling to search in, is updated if needed.
- * @param[in] node New data node instance to check.
+ * @param[in,out] node New data node instance to check, is updated if auto-deleted.
  * @param[in] mod Module of the siblings, NULL for nested siblings.
- * @param[in,out] next_p Temporary LY_LIST_FOR_SAFE next pointer, is updated if needed.
  * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
  */
-static void
-lyd_validate_autodel_dup(struct lyd_node **first, struct lyd_node *node, const struct lys_module *mod,
-        struct lyd_node **next_p, struct lyd_node **diff)
+static ly_bool
+lyd_validate_autodel_leaflist_dflt(struct lyd_node **first, struct lyd_node **node, const struct lys_module *mod,
+        struct lyd_node **diff)
 {
-    struct lyd_node *match, *next;
+    const struct lysc_node *schema;
+    struct lyd_node *iter, *next;
+    ly_bool found = 0, node_autodel = 0;
 
-    assert(node->flags & LYD_NEW);
+    assert((*node)->flags & LYD_NEW);
 
-    if (lyd_val_has_default(node->schema)) {
-        assert(node->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_CONTAINER));
-        LYD_LIST_FOR_INST_SAFE(*first, node->schema, next, match) {
-            if ((match->flags & LYD_DEFAULT) && !(match->flags & LYD_NEW)) {
-                /* default instance found, remove it */
-                lyd_validate_autodel_node_del(first, match, mod, next_p, diff);
+    schema = (*node)->schema;
+    assert(schema->nodetype == LYS_LEAFLIST);
 
-                /* remove only a single container/leaf default instance, if there are more, it is an error */
-                if (node->schema->nodetype & (LYS_LEAF | LYS_CONTAINER)) {
-                    break;
-                }
+    /* check whether there is any explicit instance */
+    LYD_LIST_FOR_INST(*first, schema, iter) {
+        if (!(iter->flags & LYD_DEFAULT)) {
+            found = 1;
+            break;
+        }
+    }
+    if (!found) {
+        /* no explicit instance, keep defaults as they are */
+        return 0;
+    }
+
+    LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
+        if (iter->flags & LYD_DEFAULT) {
+            /* default instance found, remove it */
+            if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+                node_autodel = 1;
             }
         }
     }
+
+    return node_autodel;
 }
 
 /**
- * @brief Autodelete leftover default nodes of deleted cases (that have no existing explicit data).
+ * @brief Auto-delete container or leaf default instances to prevent validation errors.
  *
  * @param[in,out] first First sibling to search in, is updated if needed.
- * @param[in] node Default data node instance to check.
+ * @param[in,out] node New data node instance to check, is updated if auto-deleted.
  * @param[in] mod Module of the siblings, NULL for nested siblings.
- * @param[in,out] next_p Temporary LY_LIST_FOR_SAFE next pointer, is updated if needed.
  * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
  */
-static void
-lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node *node, const struct lys_module *mod,
-        struct lyd_node **next_p, struct lyd_node **diff)
+static ly_bool
+lyd_validate_autodel_cont_leaf_dflt(struct lyd_node **first, struct lyd_node **node, const struct lys_module *mod,
+        struct lyd_node **diff)
 {
+    const struct lysc_node *schema;
+    struct lyd_node *iter, *next;
+    ly_bool found = 0, node_autodel = 0;
+
+    assert((*node)->flags & LYD_NEW);
+
+    schema = (*node)->schema;
+    assert(schema->nodetype & (LYS_LEAF | LYS_CONTAINER));
+
+    /* check whether there is any explicit instance */
+    LYD_LIST_FOR_INST(*first, schema, iter) {
+        if (!(iter->flags & LYD_DEFAULT)) {
+            found = 1;
+            break;
+        }
+    }
+
+    if (found) {
+        /* remove all default instances */
+        LYD_LIST_FOR_INST_SAFE(*first, schema, next, iter) {
+            if (iter->flags & LYD_DEFAULT) {
+                /* default instance, remove it */
+                if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+                    node_autodel = 1;
+                }
+            }
+        }
+    } else {
+        /* remove a single old default instance, if any */
+        LYD_LIST_FOR_INST(*first, schema, iter) {
+            if ((iter->flags & LYD_DEFAULT) && !(iter->flags & LYD_NEW)) {
+                /* old default instance, remove it */
+                if (lyd_validate_autodel_node_del(first, iter, mod, node, diff)) {
+                    node_autodel = 1;
+                }
+                break;
+            }
+        }
+    }
+
+    return node_autodel;
+}
+
+/**
+ * @brief Auto-delete leftover default nodes of deleted cases (that have no existing explicit data).
+ *
+ * @param[in,out] first First sibling to search in, is updated if needed.
+ * @param[in,out] node Default data node instance to check.
+ * @param[in] mod Module of the siblings, NULL for nested siblings.
+ * @param[in,out] diff Validation diff.
+ * @return 1 if @p node auto-deleted and updated to its next sibling.
+ * @return 0 if @p node was not auto-deleted.
+ */
+static ly_bool
+lyd_validate_autodel_case_dflt(struct lyd_node **first, struct lyd_node **node, const struct lys_module *mod,
+        struct lyd_node **diff)
+{
+    const struct lysc_node *schema;
     struct lysc_node_choice *choic;
     struct lyd_node *iter = NULL;
     const struct lysc_node *slast = NULL;
+    ly_bool node_autodel = 0;
 
-    assert(node->flags & LYD_DEFAULT);
+    assert((*node)->flags & LYD_DEFAULT);
 
-    if (!node->schema->parent || (node->schema->parent->nodetype != LYS_CASE)) {
+    schema = (*node)->schema;
+
+    if (!schema->parent || (schema->parent->nodetype != LYS_CASE)) {
         /* the default node is not a descendant of a case */
-        return;
+        return 0;
     }
 
-    choic = (struct lysc_node_choice *)node->schema->parent->parent;
+    choic = (struct lysc_node_choice *)schema->parent->parent;
     assert(choic->nodetype == LYS_CHOICE);
 
-    if (choic->dflt && (choic->dflt == (struct lysc_node_case *)node->schema->parent)) {
+    if (choic->dflt && (choic->dflt == (struct lysc_node_case *)schema->parent)) {
         /* data of a default case, keep them */
-        return;
+        return 0;
     }
 
     /* try to find an explicit node of the case */
-    while ((iter = lys_getnext_data(iter, *first, &slast, node->schema->parent, NULL))) {
+    while ((iter = lys_getnext_data(iter, *first, &slast, schema->parent, NULL))) {
         if (!(iter->flags & LYD_DEFAULT)) {
             break;
         }
@@ -637,8 +719,12 @@
     if (!iter) {
         /* there are only default nodes of the case meaning it does not exist and neither should any default nodes
          * of the case, remove this one default node */
-        lyd_validate_autodel_node_del(first, node, mod, next_p, diff);
+        if (lyd_validate_autodel_node_del(first, *node, mod, node, diff)) {
+            node_autodel = 1;
+        }
     }
+
+    return node_autodel;
 }
 
 /**
@@ -673,14 +759,17 @@
 lyd_validate_new(struct lyd_node **first, const struct lysc_node *sparent, const struct lys_module *mod,
         struct lyd_node **diff)
 {
-    struct lyd_node *next, *node;
+    LY_ERR r;
+    struct lyd_node *node;
+    const struct lysc_node *last_dflt_schema = NULL;
 
     assert(first && (sparent || mod));
 
     /* validate choices */
     LY_CHECK_RET(lyd_validate_choice_r(first, sparent, mod, diff));
 
-    LY_LIST_FOR_SAFE(*first, next, node) {
+    node = *first;
+    while (node) {
         if (!node->schema || (mod && (lyd_owner_module(node) != mod))) {
             /* opaque node or all top-level data from this module checked */
             break;
@@ -688,31 +777,44 @@
 
         if (!(node->flags & (LYD_NEW | LYD_DEFAULT))) {
             /* check only new and default nodes */
+            node = node->next;
             continue;
         }
 
-        LOG_LOCSET(node->schema, node, NULL, NULL);
+        if (lyd_val_has_default(node->schema) && (node->schema != last_dflt_schema) && (node->flags & LYD_NEW)) {
+            /* remove old default(s) of the new node if an explicit instance exists */
+            last_dflt_schema = node->schema;
+            if (node->schema->nodetype == LYS_LEAFLIST) {
+                if (lyd_validate_autodel_leaflist_dflt(first, &node, mod, diff)) {
+                    continue;
+                }
+            } else {
+                if (lyd_validate_autodel_cont_leaf_dflt(first, &node, mod, diff)) {
+                    continue;
+                }
+            }
+        }
 
         if (node->flags & LYD_NEW) {
-            LY_ERR ret;
-
-            /* remove old default(s) of the new node if it exists */
-            lyd_validate_autodel_dup(first, node, mod, &next, diff);
-
             /* then check new node instance duplicities */
-            ret = lyd_validate_duplicates(*first, node);
-            LY_CHECK_ERR_RET(ret, LOG_LOCBACK(node->schema ? 1 : 0, 1, 0, 0), ret);
+            LOG_LOCSET(NULL, node, NULL, NULL);
+            r = lyd_validate_duplicates(*first, node);
+            LOG_LOCBACK(0, 1, 0, 0);
+            LY_CHECK_RET(r);
 
             /* this node is valid */
             node->flags &= ~LYD_NEW;
         }
 
-        LOG_LOCBACK(node->schema ? 1 : 0, 1, 0, 0);
-
         if (node->flags & LYD_DEFAULT) {
             /* remove leftover default nodes from a no-longer existing case */
-            lyd_validate_autodel_case_dflt(first, node, mod, &next, diff);
+            if (lyd_validate_autodel_case_dflt(first, &node, mod, diff)) {
+                continue;
+            }
         }
+
+        /* next iter */
+        node = node->next;
     }
 
     return LY_SUCCESS;