parser UPDATE guess opaque node schema nodes

... for their descendant parsing purposes.

Refs #2086
diff --git a/src/common.h b/src/common.h
index 4009f3c..b9842bc 100644
--- a/src/common.h
+++ b/src/common.h
@@ -130,6 +130,21 @@
 void ly_log_location_revert(uint32_t scnode_steps, uint32_t dnode_steps, uint32_t path_steps, uint32_t in_steps);
 
 /**
+ * @brief Get the stored data node for logging at the index.
+ *
+ * @param[in] idx Index of the data node.
+ * @return Logged data node, NULL if out of range.
+ */
+const struct lyd_node *ly_log_location_dnode(uint32_t idx);
+
+/**
+ * @brief Get the count of stored data nodes for logging.
+ *
+ * @return Count of the data nodes.
+ */
+uint32_t ly_log_location_dnode_count(void);
+
+/**
  * @brief Update location data for logger, not provided arguments (NULLs) are kept (does not override).
  *
  * @param[in] SCNODE Compiled schema node.
diff --git a/src/log.c b/src/log.c
index a788a2e..a0d997f 100644
--- a/src/log.c
+++ b/src/log.c
@@ -500,6 +500,22 @@
     }
 }
 
+const struct lyd_node *
+ly_log_location_dnode(uint32_t idx)
+{
+    if (idx < log_location.dnodes.count) {
+        return log_location.dnodes.dnodes[idx];
+    }
+
+    return NULL;
+}
+
+uint32_t
+ly_log_location_dnode_count(void)
+{
+    return log_location.dnodes.count;
+}
+
 /**
  * @brief Store generated error in a context.
  *
diff --git a/src/parser_common.c b/src/parser_common.c
index a859925..040318e 100644
--- a/src/parser_common.c
+++ b/src/parser_common.c
@@ -157,6 +157,63 @@
     return LY_SUCCESS;
 }
 
+const struct lysc_node *
+lyd_parser_node_schema(const struct lyd_node *node)
+{
+    uint32_t i;
+    const struct lyd_node *iter;
+    const struct lysc_node *schema = NULL;
+    const struct lys_module *mod;
+
+    if (!node) {
+        return NULL;
+    } else if (node->schema) {
+        /* simplest case */
+        return node->schema;
+    }
+
+    /* find the first schema node in the parsed nodes */
+    i = ly_log_location_dnode_count();
+    if (i) {
+        do {
+            --i;
+            if (ly_log_location_dnode(i)->schema) {
+                /* this node is processed */
+                schema = ly_log_location_dnode(i)->schema;
+                ++i;
+                break;
+            }
+        } while (i);
+    }
+
+    /* get schema node of an opaque node */
+    do {
+        /* get next data node */
+        if (i == ly_log_location_dnode_count()) {
+            iter = node;
+        } else {
+            iter = ly_log_location_dnode(i);
+        }
+        assert(!iter->schema);
+
+        /* get module */
+        mod = lyd_owner_module(iter);
+        if (!mod) {
+            /* unknown module, no schema node */
+            schema = NULL;
+            break;
+        }
+
+        /* get schema node */
+        schema = lys_find_child(schema, mod, LYD_NAME(iter), 0, 0, 0);
+
+        /* move to the descendant */
+        ++i;
+    } while (schema && (iter != node));
+
+    return schema;
+}
+
 LY_ERR
 lyd_parser_check_schema(struct lyd_ctx *lydctx, const struct lysc_node *snode)
 {
diff --git a/src/parser_internal.h b/src/parser_internal.h
index 419d68e..5b632a2 100644
--- a/src/parser_internal.h
+++ b/src/parser_internal.h
@@ -358,6 +358,14 @@
 LY_ERR lyd_parser_find_operation(const struct lyd_node *parent, uint32_t int_opts, struct lyd_node **op);
 
 /**
+ * @brief Get schema node of a node being parsed, use nodes stored for logging.
+ *
+ * @param[in] node Node whose schema node to get.
+ * @return Schema node even for an opaque node, NULL if none found.
+ */
+const struct lysc_node *lyd_parser_node_schema(const struct lyd_node *node);
+
+/**
  * @brief Check that a data node representing the @p snode is suitable based on options.
  *
  * @param[in] lydctx Common data parsers context.
diff --git a/src/parser_json.c b/src/parser_json.c
index fca58f4..589e9c6 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -295,7 +295,7 @@
         if (!parent && lydctx->ext) {
             *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
         } else {
-            *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+            *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts);
         }
         if (!*snode) {
             /* check for extension data */
@@ -1018,17 +1018,23 @@
         struct lyd_node *parent, enum LYJSON_PARSER_STATUS *status_p, enum LYJSON_PARSER_STATUS *status_inner_p,
         struct lyd_node **first_p, struct lyd_node **node_p)
 {
-    LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+    LY_ERR ret = LY_SUCCESS;
+
+    LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup);
+
+    assert(*node_p);
+    LOG_LOCSET(NULL, *node_p, NULL, NULL);
 
     if ((*status_p == LYJSON_ARRAY) && (*status_inner_p == LYJSON_NULL)) {
         /* special array null value */
         ((struct lyd_node_opaq *)*node_p)->hints |= LYD_VALHINT_EMPTY;
 
         /* must be the only item */
-        LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+        LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
         if (*status_inner_p != LYJSON_ARRAY_CLOSED) {
             LOGVAL(lydctx->jsonctx->ctx, LYVE_SYNTAX, "Array \"null\" member with another member.");
-            return LY_EVALID;
+            ret = LY_EVALID;
+            goto cleanup;
         }
 
         goto finish;
@@ -1042,7 +1048,7 @@
 
             /* but first process children of the object in the array */
             do {
-                LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
+                LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup);
                 *status_inner_p = lyjson_ctx_status(lydctx->jsonctx);
             } while (*status_inner_p == LYJSON_OBJECT_NEXT);
         } else {
@@ -1050,30 +1056,42 @@
             ((struct lyd_node_opaq *)*node_p)->hints |= LYD_NODEHINT_LEAFLIST;
         }
 
-        LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
+        LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
         if (*status_inner_p == LYJSON_ARRAY_CLOSED) {
             goto finish;
         }
         assert(*status_inner_p == LYJSON_ARRAY_NEXT);
 
         /* continue with the next instance */
-        LY_CHECK_RET(lyjson_ctx_next(lydctx->jsonctx, status_inner_p));
-        assert(node_p);
+        LY_CHECK_GOTO(ret = lyjson_ctx_next(lydctx->jsonctx, status_inner_p), cleanup);
+        assert(*node_p);
         lydjson_maintain_children(parent, first_p, node_p, lydctx->parse_opts & LYD_PARSE_ORDERED ? 1 : 0, NULL);
-        LY_CHECK_RET(lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p));
+
+        LOG_LOCBACK(0, 1, 0, 0);
+
+        LY_CHECK_GOTO(ret = lydjson_create_opaq(lydctx, name, name_len, prefix, prefix_len, parent, status_inner_p, node_p), cleanup);
+
+        assert(*node_p);
+        LOG_LOCSET(NULL, *node_p, NULL, NULL);
     }
 
     if (*status_p == LYJSON_OBJECT) {
         /* process children */
         do {
-            LY_CHECK_RET(lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL));
+            LY_CHECK_GOTO(ret = lydjson_subtree_r(lydctx, *node_p, lyd_node_child_p(*node_p), NULL), cleanup);
             *status_p = lyjson_ctx_status(lydctx->jsonctx);
         } while (*status_p == LYJSON_OBJECT_NEXT);
     }
 
 finish:
     /* finish linking metadata */
-    return lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
+    ret = lydjson_metadata_finish(lydctx, lyd_node_child_p(*node_p));
+
+cleanup:
+    if (*node_p) {
+        LOG_LOCBACK(0, 1, 0, 0);
+    }
+    return ret;
 }
 
 /**
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 2e3d0c5..788be94 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -1171,6 +1171,9 @@
             value, strlen(value), &dynamic, format, val_prefix_data, LYD_HINT_DATA, &node);
     LY_CHECK_GOTO(ret, cleanup);
 
+    assert(node);
+    LOG_LOCSET(NULL, node, NULL, NULL);
+
     /* process children */
     ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
     LY_CHECK_GOTO(ret, cleanup);
@@ -1178,8 +1181,12 @@
     /* register parsed opaq node */
     lyb_finish_opaq(lybctx, parent, flags, &attr, &node, first_p, parsed);
     assert(!attr && !node);
+    LOG_LOCBACK(0, 1, 0, 0);
 
 cleanup:
+    if (node) {
+        LOG_LOCBACK(0, 1, 0, 0);
+    }
     free(prefix);
     free(module_key);
     free(name);
@@ -1265,9 +1272,13 @@
         goto error;
     }
 
+    assert(node);
+    LOG_LOCSET(NULL, node, NULL, NULL);
+
     /* register parsed anydata node */
     lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
 
+    LOG_LOCBACK(0, 1, 0, 0);
     return LY_SUCCESS;
 
 error:
@@ -1304,6 +1315,9 @@
     ret = lyd_create_inner(snode, &node);
     LY_CHECK_GOTO(ret, error);
 
+    assert(node);
+    LOG_LOCSET(NULL, node, NULL, NULL);
+
     /* process children */
     ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
     LY_CHECK_GOTO(ret, error);
@@ -1320,9 +1334,13 @@
     /* register parsed node */
     lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
 
+    LOG_LOCBACK(0, 1, 0, 0);
     return LY_SUCCESS;
 
 error:
+    if (node) {
+        LOG_LOCBACK(0, 1, 0, 0);
+    }
     lyd_free_meta_siblings(meta);
     lyd_free_tree(node);
     return ret;
@@ -1355,8 +1373,12 @@
     ret = lyb_create_term(lybctx, snode, &node);
     LY_CHECK_GOTO(ret, error);
 
+    assert(node);
+    LOG_LOCSET(NULL, node, NULL, NULL);
+
     lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
 
+    LOG_LOCBACK(0, 1, 0, 0);
     return LY_SUCCESS;
 
 error:
@@ -1416,6 +1438,7 @@
     struct lyd_node *node = NULL;
     struct lyd_meta *meta = NULL;
     uint32_t flags;
+    ly_bool log_node = 0;
 
     /* register a new sibling */
     ret = lyb_read_start_siblings(lybctx->lybctx);
@@ -1430,6 +1453,10 @@
         ret = lyd_create_inner(snode, &node);
         LY_CHECK_GOTO(ret, error);
 
+        assert(node);
+        LOG_LOCSET(NULL, node, NULL, NULL);
+        log_node = 1;
+
         /* process children */
         ret = lyb_parse_siblings(lybctx, node, NULL, NULL);
         LY_CHECK_GOTO(ret, error);
@@ -1445,6 +1472,9 @@
 
         /* register parsed list node */
         lyb_finish_node(lybctx, parent, flags, &meta, &node, first_p, parsed);
+
+        LOG_LOCBACK(0, 1, 0, 0);
+        log_node = 0;
     }
 
     /* end the sibling */
@@ -1454,6 +1484,9 @@
     return LY_SUCCESS;
 
 error:
+    if (log_node) {
+        LOG_LOCBACK(0, 1, 0, 0);
+    }
     lyd_free_meta_siblings(meta);
     lyd_free_tree(node);
     return ret;
@@ -1492,7 +1525,7 @@
     case LYB_NODE_CHILD:
     case LYB_NODE_OPAQ:
         /* read hash, find the schema node starting from parent schema, if any */
-        LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, parent ? parent->schema : NULL, NULL, &snode), cleanup);
+        LY_CHECK_GOTO(ret = lyb_parse_schema_hash(lybctx, lyd_parser_node_schema(parent), NULL, &snode), cleanup);
         break;
     case LYB_NODE_EXT:
         /* ext, read module name */
diff --git a/src/parser_xml.c b/src/parser_xml.c
index b24e9fa..a117825 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -570,46 +570,45 @@
     }
 
     /* get the schema node */
-    if (mod) {
-        if (!parent && lydctx->ext) {
-            *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
-        } else {
-            *snode = lys_find_child(parent ? parent->schema : NULL, mod, name, name_len, 0, getnext_opts);
+    if (!parent && lydctx->ext) {
+        *snode = lysc_ext_find_node(lydctx->ext, mod, name, name_len, 0, getnext_opts);
+    } else {
+        /* try to find parent schema node even if it is an opaque node (not connected to the parent) */
+        *snode = lys_find_child(lyd_parser_node_schema(parent), mod, name, name_len, 0, getnext_opts);
+    }
+    if (!*snode) {
+        /* check for extension data */
+        r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
+                name_len, snode, ext);
+        if (r != LY_ENOT) {
+            /* success or error */
+            return r;
         }
-        if (!*snode) {
-            /* check for extension data */
-            r = ly_nested_ext_schema(parent, NULL, prefix, prefix_len, LY_VALUE_XML, &lydctx->xmlctx->ns, name,
-                    name_len, snode, ext);
-            if (r != LY_ENOT) {
-                /* success or error */
-                return r;
-            }
 
-            /* unknown data node */
-            if (lydctx->parse_opts & LYD_PARSE_STRICT) {
-                if (parent) {
-                    LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
-                            (int)name_len, name, LYD_NAME(parent));
-                } else if (lydctx->ext) {
-                    if (lydctx->ext->argument) {
-                        LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
-                                (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
-                    } else {
-                        LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
-                                (int)name_len, name, lydctx->ext->def->name);
-                    }
+        /* unknown data node */
+        if (lydctx->parse_opts & LYD_PARSE_STRICT) {
+            if (parent) {
+                LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found as a child of \"%s\" node.",
+                        (int)name_len, name, LYD_NAME(parent));
+            } else if (lydctx->ext) {
+                if (lydctx->ext->argument) {
+                    LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" %s extension instance.",
+                            (int)name_len, name, lydctx->ext->argument, lydctx->ext->def->name);
                 } else {
-                    LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
-                            (int)name_len, name, mod->name);
+                    LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the %s extension instance.",
+                            (int)name_len, name, lydctx->ext->def->name);
                 }
-                return LY_EVALID;
+            } else {
+                LOGVAL(ctx, LYVE_REFERENCE, "Node \"%.*s\" not found in the \"%s\" module.",
+                        (int)name_len, name, mod->name);
             }
-            return LY_SUCCESS;
-        } else {
-            /* check that schema node is valid and can be used */
-            LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
-            LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
+            return LY_EVALID;
         }
+        return LY_SUCCESS;
+    } else {
+        /* check that schema node is valid and can be used */
+        LY_CHECK_RET(lyd_parser_check_schema((struct lyd_ctx *)lydctx, *snode));
+        LY_CHECK_RET(lydxml_data_check_opaq(lydctx, snode));
     }
 
     return LY_SUCCESS;
@@ -672,6 +671,9 @@
             NULL, format, NULL, hints, node);
     LY_CHECK_GOTO(rc, cleanup);
 
+    assert(*node);
+    LOG_LOCSET(NULL, *node, NULL, NULL);
+
     /* parser next */
     rc = lyxml_ctx_next(xmlctx);
     LY_CHECK_GOTO(rc, cleanup);
@@ -706,6 +708,9 @@
     val_prefix_data = NULL;
 
 cleanup:
+    if (*node) {
+        LOG_LOCBACK(0, 1, 0, 0);
+    }
     ly_free_prefix_data(format, val_prefix_data);
     if (dynamic) {
         free((char *)value);
diff --git a/src/tree_data.c b/src/tree_data.c
index a5a0201..463daac 100644
--- a/src/tree_data.c
+++ b/src/tree_data.c
@@ -697,7 +697,7 @@
     lyd_insert_hash(node);
 
     /* finish hashes for our parent, if needed and possible */
-    if (node->schema && (node->schema->flags & LYS_KEY) && parent && lyd_insert_has_keys(parent)) {
+    if (node->schema && (node->schema->flags & LYS_KEY) && parent && parent->schema && lyd_insert_has_keys(parent)) {
         lyd_hash(parent);
 
         /* now we can insert even the list into its parent HT */
diff --git a/src/tree_data_common.c b/src/tree_data_common.c
index 3f168ae..115886b 100644
--- a/src/tree_data_common.c
+++ b/src/tree_data_common.c
@@ -757,17 +757,10 @@
     }
 
     LY_LIST_FOR(lyd_child(node), child) {
-        if (child->schema) {
-            /* skip data nodes, opaque nodes are after them */
-            continue;
-        }
-
-        opaq_k = (struct lyd_node_opaq *)child;
-
         /* find the key schema node */
         for (i = 0; i < key_set.count; ++i) {
             key = key_set.snodes[i];
-            if (!strcmp(key->name, opaq_k->name.name)) {
+            if (!strcmp(key->name, LYD_NAME(child))) {
                 break;
             }
         }
@@ -779,7 +772,13 @@
         /* key found */
         ly_set_rm_index(&key_set, i, NULL);
 
+        if (child->schema) {
+            /* valid key */
+            continue;
+        }
+
         /* check value */
+        opaq_k = (struct lyd_node_opaq *)child;
         ret = ly_value_validate(LYD_CTX(node), key, opaq_k->value, strlen(opaq_k->value), opaq_k->format,
                 opaq_k->val_prefix_data, opaq_k->hints);
         LY_CHECK_GOTO(ret, cleanup);
@@ -1059,27 +1058,30 @@
         return node->schema;
     }
 
+    /* find the first schema node in the parents */
+    for (iter = lyd_parent(node); iter && !iter->schema; iter = lyd_parent(iter)) {}
+    if (iter) {
+        prev_iter = iter;
+        schema = prev_iter->schema;
+    }
+
     /* get schema node of an opaque node */
     do {
         /* get next data node */
         for (iter = node; lyd_parent(iter) != prev_iter; iter = lyd_parent(iter)) {}
 
-        /* get equivalent schema node */
-        if (iter->schema) {
-            schema = iter->schema;
-        } else {
-            /* get module */
-            mod = lyd_owner_module(iter);
-            if (!mod && !schema) {
-                /* top-level opaque node has unknown module */
-                break;
-            }
-
-            /* get schema node */
-            schema = lys_find_child(schema, mod ? mod : schema->module, LYD_NAME(iter), 0, 0, 0);
+        /* get module */
+        mod = lyd_owner_module(iter);
+        if (!mod) {
+            /* unknown module, no schema node */
+            schema = NULL;
+            break;
         }
 
-        /* remember to move to the descendant */
+        /* get schema node */
+        schema = lys_find_child(schema, mod, LYD_NAME(iter), 0, 0, 0);
+
+        /* move to the descendant */
         prev_iter = iter;
     } while (schema && (iter != node));
 
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index e5513da..e9619e2 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -584,7 +584,7 @@
 
     /* empty name */
     PARSER_CHECK_ERROR("{\"@a:foo\":{\"\":0}}", 0, LYD_VALIDATE_PRESENT, tree, LY_EVALID,
-            "JSON object member name cannot be a zero-length string.", "Line number 1.");
+            "JSON object member name cannot be a zero-length string.", "Data location \"/@a:foo\", line number 1.");
 
     /* opaque data tree format print */
     data =
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index 340724c..889e502 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -350,7 +350,7 @@
             "  <c xmld:id=\"D\">1</c>\n"
             "</a>\n",
             LYD_XML, LYD_PARSE_OPAQ, LYD_VALIDATE_PRESENT, &tree));
-    CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "Line number 3.");
+    CHECK_LOG_CTX("Unknown XML prefix \"xmld\".", "Data location \"/a\", line number 3.");
 }
 
 static void