schema compile CHANGE leafref validation

- check for circular chain of leafrefs
- store the real type (the first non-leafref) of the leafref's type for easier
  data validation.
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 024e383..4ac55c1 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -1879,15 +1879,15 @@
  * @brief Validate the leafref path.
  * @param[in] ctx Compile context
  * @param[in] startnode Path context node (where the leafref path begins/is placed).
- * @param[in] path Leafref path to validate.
- * @param[in] path_context Module where the path was defined to correct resolve of the prefixes.
+ * @param[in] leafref Leafref to validate.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
-lys_compile_leafref_validate(struct lysc_ctx *ctx, struct lysc_node *startnode, const char *path, struct lys_module *path_context)
+lys_compile_leafref_validate(struct lysc_ctx *ctx, struct lysc_node *startnode, struct lysc_type_leafref *leafref)
 {
     const struct lysc_node *node = NULL, *parent = NULL;
     const struct lys_module *mod;
+    struct lysc_type *type;
     const char *id, *prefix, *name;
     size_t prefix_len, name_len;
     int parent_times = 0, has_predicate;
@@ -1896,10 +1896,12 @@
 
     assert(ctx);
     assert(startnode);
-    assert(path);
+    assert(leafref);
+
+    /* TODO leafref targets may be not implemented, in such a case we actually could make (we did it in libyang1) such a models implemented */
 
     iter = 0;
-    id = path;
+    id = leafref->path;
     while(*id && (ret = lys_path_token(&id, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate)) == LY_SUCCESS) {
         if (!iter) { /* first iteration */
             /* precess ".." in relative paths */
@@ -1909,26 +1911,27 @@
                     /* path is supposed to be evaluated in data tree, so we have to skip
                     * all schema nodes that cannot be instantiated in data tree */
                     MOVE_PATH_PARENT(parent, u < (unsigned int)parent_times - 1, return LY_EVALID,
-                                     "Invalid leafref path \"%s\" - too many \"..\" in the path.", path);
+                                     "Invalid leafref path \"%s\" - too many \"..\" in the path.", leafref->path);
                 }
             }
         }
 
         if (prefix) {
-            mod = lys_module_find_prefix(path_context, prefix, prefix_len);
+            mod = lys_module_find_prefix(leafref->path_context, prefix, prefix_len);
         } else {
-            mod = path_context;
+            mod = leafref->path_context;
         }
         if (!mod) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid leafref path - unable to find module connected with the prefix of the node \"%.*s\".", id - path, path);
+                   "Invalid leafref path - unable to find module connected with the prefix of the node \"%.*s\".",
+                   id - leafref->path, leafref->path);
             return LY_EVALID;
         }
 
         node = lys_child(parent, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
         if (!node) {
             LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                   "Invalid leafref path - unable to find \"%.*s\".", id - path, path);
+                   "Invalid leafref path - unable to find \"%.*s\".", id - leafref->path, leafref->path);
             return LY_EVALID;
         }
         parent = node;
@@ -1938,25 +1941,25 @@
             if (node->nodetype != LYS_LIST) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
                        "Invalid leafref path - node \"%.*s\" is expected to be a list, but it is %s.",
-                       id - path, path, lys_nodetype2str(node->nodetype));
+                       id - leafref->path, leafref->path, lys_nodetype2str(node->nodetype));
                 return LY_EVALID;
             }
 
-            LY_CHECK_RET(lys_compile_leafref_predicate_validate(ctx, &id, node, path_context), LY_EVALID);
+            LY_CHECK_RET(lys_compile_leafref_predicate_validate(ctx, &id, node, leafref->path_context), LY_EVALID);
         }
 
         ++iter;
     }
     if (ret) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-               "Invalid leafref path at character %d (%s).", id - path + 1, path);
+               "Invalid leafref path at character %d (%s).", id - leafref->path + 1, leafref->path);
         return LY_EVALID;
     }
 
     if (!(node->nodetype & (LYS_LEAF | LYS_LEAFLIST))) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
                "Invalid leafref path \"%s\" - target node is %s instead of leaf or leaf-list.",
-               path, lys_nodetype2str(node->nodetype));
+               leafref->path, lys_nodetype2str(node->nodetype));
         return LY_EVALID;
     }
 
@@ -1965,6 +1968,17 @@
         return LY_EVALID;
     }
 
+    /* store the target's type and check for circular chain of leafrefs */
+    leafref->realtype = ((struct lysc_node_leaf*)node)->type;
+    for (type = leafref->realtype; type && type->basetype == LY_TYPE_LEAFREF; type = ((struct lysc_type_leafref*)type)->realtype) {
+        if (type == (struct lysc_type*)leafref) {
+            /* circular chain detected */
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path \"%s\" - circular chain of leafrefs detected.", leafref->path);
+            return LY_EVALID;
+        }
+    }
+
     return LY_SUCCESS;
 }
 
@@ -2214,6 +2228,9 @@
         /* RFC 7950 9.9.3 - require-instance */
         if (type_p->flags & LYS_SET_REQINST) {
             ((struct lysc_type_leafref*)(*type))->require_instance = type_p->require_instance;
+        } else if (base) {
+            /* inherit */
+            ((struct lysc_type_leafref*)(*type))->require_instance = ((struct lysc_type_leafref*)base)->require_instance;
         } else {
             /* default is true */
             ((struct lysc_type_leafref*)(*type))->require_instance = 1;
@@ -2391,7 +2408,7 @@
         } else if (tctx->tpdf->type.compiled) {
             base = tctx->tpdf->type.compiled;
             continue;
-        } else if ((u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags)) {
+        } else if ((basetype != LY_TYPE_LEAFREF) && (u != tpdf_chain.count - 1) && !(tctx->tpdf->type.flags)) {
             /* no change, just use the type information from the base */
             base = ((struct lysp_tpdf*)tctx->tpdf)->type.compiled = ((struct type_context*)tpdf_chain.objs[u + 1])->tpdf->type.compiled;
             ++base->refcount;
@@ -2409,7 +2426,7 @@
     }
 
     /* process the type definition in leaf */
-    if (leaf_p->type.flags || !base) {
+    if (leaf_p->type.flags || !base || basetype == LY_TYPE_LEAFREF) {
         /* get restrictions from the node itself */
         (*type)->basetype = basetype;
         ++(*type)->refcount;
@@ -2639,7 +2656,7 @@
 {
     struct lysc_ctx ctx = {0};
     struct lysc_module *mod_c;
-    struct lysc_type *type;
+    struct lysc_type *type, *typeiter;
     struct lysp_module *sp;
     struct lysp_node *node_p;
     unsigned int u;
@@ -2686,17 +2703,31 @@
     COMPILE_ARRAY_GOTO(&ctx, sp->exts, mod_c->exts, options, u, lys_compile_ext, ret, error);
 
     /* validate leafref's paths and when/must xpaths */
+    /* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
+     * can be also leafref, in case it is already resolved, go through the chain and check that it does not
+     * point to the starting leafref type). The second round stores the first non-leafref type for later data validation. */
     for (u = 0; u < ctx.unres.count; ++u) {
         if (((struct lysc_node*)ctx.unres.objs[u])->nodetype == LYS_LEAF) {
             type = ((struct lysc_node_leaf*)ctx.unres.objs[u])->type;
             if (type->basetype == LY_TYPE_LEAFREF) {
                 /* validate the path */
-                ret = lys_compile_leafref_validate(&ctx, ((struct lysc_node*)ctx.unres.objs[u]),
-                                                   ((struct lysc_type_leafref*)type)->path, ((struct lysc_type_leafref*)type)->path_context);
+                ret = lys_compile_leafref_validate(&ctx, ((struct lysc_node*)ctx.unres.objs[u]), (struct lysc_type_leafref*)type);
                 LY_CHECK_GOTO(ret, error);
             }
         }
     }
+    for (u = 0; u < ctx.unres.count; ++u) {
+        if (((struct lysc_node*)ctx.unres.objs[u])->nodetype == LYS_LEAF) {
+            type = ((struct lysc_node_leaf*)ctx.unres.objs[u])->type;
+            if (type->basetype == LY_TYPE_LEAFREF) {
+                /* store pointer to the real type */
+                for (typeiter = ((struct lysc_type_leafref*)type)->realtype;
+                        typeiter->basetype == LY_TYPE_LEAFREF;
+                        typeiter = ((struct lysc_type_leafref*)typeiter)->realtype);
+                ((struct lysc_type_leafref*)type)->realtype = typeiter;
+            }
+        }
+    }
     ly_set_erase(&ctx.unres, NULL);
 
     if (options & LYSC_OPT_FREE_SP) {