schema compile CHANGE support for leafref type
diff --git a/src/common.h b/src/common.h
index 63077ed..f90ae36 100644
--- a/src/common.h
+++ b/src/common.h
@@ -112,12 +112,12 @@
 #define LY_CHECK_RET(...) GETMACRO2(__VA_ARGS__, LY_CHECK_RET2, LY_CHECK_RET1)(__VA_ARGS__)
 #define LY_CHECK_ERR_RET(COND, ERR, RETVAL) if (COND) {ERR; return RETVAL;}
 
-#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!ARG) {LOGARG(CTX, ARG);goto GOTO;}
+#define LY_CHECK_ARG_GOTO1(CTX, ARG, GOTO) if (!(ARG)) {LOGARG(CTX, ARG);goto GOTO;}
 #define LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO) LY_CHECK_ARG_GOTO1(CTX, ARG1, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG2, GOTO)
 #define LY_CHECK_ARG_GOTO3(CTX, ARG1, ARG2, ARG3, GOTO) LY_CHECK_ARG_GOTO2(CTX, ARG1, ARG2, GOTO);LY_CHECK_ARG_GOTO1(CTX, ARG3, GOTO)
 #define LY_CHECK_ARG_GOTO(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_GOTO3, LY_CHECK_ARG_GOTO2, LY_CHECK_ARG_GOTO1)(CTX, __VA_ARGS__)
 
-#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!ARG) {LOGARG(CTX, ARG);return RETVAL;}
+#define LY_CHECK_ARG_RET1(CTX, ARG, RETVAL) if (!(ARG)) {LOGARG(CTX, ARG);return RETVAL;}
 #define LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL) LY_CHECK_ARG_RET1(CTX, ARG1, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG2, RETVAL)
 #define LY_CHECK_ARG_RET3(CTX, ARG1, ARG2, ARG3, RETVAL) LY_CHECK_ARG_RET2(CTX, ARG1, ARG2, RETVAL);LY_CHECK_ARG_RET1(CTX, ARG3, RETVAL)
 #define LY_CHECK_ARG_RET(CTX, ...) GETMACRO4(__VA_ARGS__, LY_CHECK_ARG_RET3, LY_CHECK_ARG_RET2, LY_CHECK_ARG_RET1)(CTX, __VA_ARGS__)
diff --git a/src/tree_schema.c b/src/tree_schema.c
index cacadf7..9ababa3 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -30,6 +30,125 @@
 #include "tree_schema_internal.h"
 #include "xpath.h"
 
+API const struct lysc_node *
+lys_getnext(const struct lysc_node *last, const struct lysc_node *parent, const struct lysc_module *module, int options)
+{
+    const struct lysc_node *next;
+    struct lysc_node **snode;
+
+    LY_CHECK_ARG_RET(NULL, parent || module, NULL);
+
+    if (!last) {
+        /* first call */
+
+        /* get know where to start */
+        if (parent) {
+            /* schema subtree */
+            snode = lysc_node_children(parent);
+            /* do not return anything if the augment does not have any children */
+            if (!snode || !(*snode)) {
+                return NULL;
+            }
+            next = last = *snode;
+        } else {
+            /* top level data */
+            next = last = module->data;
+        }
+        if (!next) {
+            return next;
+        } else if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+            if (!lys_is_disabled(next, 0)) {
+                return next;
+            }
+            /* continue to find next available node */
+        } else {
+            return next;
+        }
+    }
+
+    next = last->next;
+repeat:
+    if (!next) {
+        return next;
+    }
+    switch (next->nodetype) {
+    case LYS_ACTION:
+    case LYS_NOTIF:
+    case LYS_LEAF:
+    case LYS_ANYXML:
+    case LYS_ANYDATA:
+    case LYS_LIST:
+    case LYS_LEAFLIST:
+        break;
+    case LYS_CONTAINER:
+        if (!(((struct lysc_node_container *)next)->flags & LYS_PRESENCE) && (options & LYS_GETNEXT_INTONPCONT)) {
+            if (((struct lysc_node_container *)next)->child) {
+                /* go into */
+                next = ((struct lysc_node_container *)next)->child;
+            } else {
+                next = next->next;
+            }
+            goto repeat;
+        }
+        break;
+    case LYS_CHOICE:
+        if (options & LYS_GETNEXT_WITHCHOICE) {
+            return next;
+        } else if (((struct lysc_node_choice *)next)->cases) {
+            /* go into */
+            next = ((struct lysc_node_choice *)next)->cases[0].child;
+        } else {
+            next = next->next;
+        }
+        goto repeat;
+    default:
+        /* we should not be here */
+        LOGINT(last->module->compiled->ctx);
+        return NULL;
+    }
+
+    if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
+        /* check if the node is disabled by if-feature */
+        if (lys_is_disabled(next, 0)) {
+            next = next->next;
+            goto repeat;
+        }
+    }
+    return next;
+}
+
+API const struct lysc_node *
+lys_child(const struct lysc_node *parent, const struct lys_module *module,
+          const char *name, size_t name_len, uint16_t nodetype, int options)
+{
+    const struct lysc_node *node = NULL;
+
+    LY_CHECK_ARG_RET(NULL, module, name, NULL);
+    if (!nodetype) {
+        nodetype = 0xffff;
+    }
+
+    while ((node = lys_getnext(node, parent, module->compiled, options))) {
+        if (!(node->nodetype & nodetype)) {
+            continue;
+        }
+        if (node->module != module) {
+            continue;
+        }
+
+        if (name_len) {
+            if (!strncmp(node->name, name, name_len) && !node->name[name_len]) {
+                return node;
+            }
+        } else {
+            if (!strcmp(node->name, name)) {
+                return node;
+            }
+        }
+    }
+    return NULL;
+}
+
 API int
 lysc_feature_value(const struct lysc_feature *feature)
 {
@@ -282,6 +401,39 @@
     return -1;
 }
 
+API const struct lysc_iffeature *
+lys_is_disabled(const struct lysc_node *node, int recursive)
+{
+    unsigned int u;
+    struct lysc_iffeature **iff;
+
+    LY_CHECK_ARG_RET(NULL, node, NULL);
+
+    while(node) {
+        if (node->nodetype & LYS_CHOICE) {
+            return NULL;
+        }
+
+        iff = lysc_node_iff(node);
+        if (iff && *iff) {
+            /* check local if-features */
+            LY_ARRAY_FOR(*iff, u) {
+                if (!lysc_iffeature_value(&(*iff)[u])) {
+                    return &(*iff)[u];
+                }
+            }
+        }
+
+        if (!recursive) {
+            return NULL;
+        }
+
+        /* go through parents */
+        node = node->parent;
+    }
+    return NULL;
+}
+
 static void
 lys_latest_switch(struct lys_module *old, struct lysp_module *new)
 {
diff --git a/src/tree_schema.h b/src/tree_schema.h
index dab3b1b..61e04b6 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -156,10 +156,10 @@
 #define LYS_LEAFLIST 0x0008       /**< leaf-list statement node */
 #define LYS_LIST 0x0010           /**< list statement node */
 #define LYS_ANYXML 0x0020         /**< anyxml statement node */
-#define LYS_CASE 0x0040           /**< case statement node */
-#define LYS_USES 0x0080           /**< uses statement node */
 #define LYS_ANYDATA 0x0120        /**< anydata statement node, in tests it can be used for both #LYS_ANYXML and #LYS_ANYDATA */
 
+#define LYS_CASE 0x0040           /**< case statement node */
+#define LYS_USES 0x0080           /**< uses statement node */
 #define LYS_INOUT 0x200
 #define LYS_ACTION 0x400
 #define LYS_NOTIF 0x800
@@ -940,6 +940,7 @@
  */
 struct lysc_when {
     struct lyxp_expr *cond;          /**< XPath when condition */
+    struct lysc_node *context;       /**< context node of the expression */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 };
 
@@ -1072,7 +1073,7 @@
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     LY_DATA_TYPE basetype;           /**< Base type of the type */
     uint32_t refcount;               /**< reference counter for type sharing */
-    struct lysc_node* target;        /**< Target schema node */
+    const char* path;                /**< target path */
     uint8_t require_instance;        /**< require-instance flag */
 };
 
@@ -1158,6 +1159,14 @@
     struct lysc_notif *notifs;       /**< list of notifications ([sized array](@ref sizedarrays)) */
 };
 
+struct lysc_node_case {
+    const char *name;                /**< name of the case, including the implicit case */
+    struct lysc_node *child;         /**< first child node of the case (linked list). Note that all the children of all the sibling cases are linked
+                                          each other as siblings with the parent pointer pointing to the choice node holding the case. To distinguish
+                                          which children node belongs to which case, it is needed to match the first children of the cases while going
+                                          through the children linked list. */
+};
+
 struct lysc_node_choice {
     uint16_t nodetype;               /**< LYS_CHOICE */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
@@ -1172,7 +1181,10 @@
     const char *name;                /**< node name (mandatory) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 
-    struct lysc_node *child;
+    struct lysc_node_case *cases;    /**< list of the cases with their name and pointer to the first children of each case ([sized array](@ref sizedarrays))
+                                          Note that all the children of all the cases are linked each other as siblings with the parent pointer pointing
+                                          to this choice node. To distinguish which children node belongs to which case, it is needed to match the first
+                                          children of the cases while going through the children linked list. */
 };
 
 struct lysc_node_leaf {
@@ -1247,40 +1259,6 @@
 
 };
 
-struct lysc_node_case {
-    uint16_t nodetype;               /**< LYS_CASE */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
-    struct lys_module *module;       /**< module structure */
-    struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
-    struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
-    struct lysc_node *next;          /**< next sibling node (NULL if there is no one) */
-    struct lysc_node *prev;          /**< pointer to the previous sibling node \note Note that this pointer is
-                                          never NULL. If there is no sibling node, pointer points to the node
-                                          itself. In case of the first node, this pointer points to the last
-                                          node in the list. */
-    const char *name;                /**< node name (mandatory) */
-    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
-    struct lysc_node *child;
-};
-
-struct lysc_node_uses {
-    uint16_t nodetype;               /**< LYS_CHOICE */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) */
-    struct lys_module *module;       /**< module structure */
-    struct lysp_node *sp;            /**< simply parsed (SP) original of the node, NULL if the SP schema was removed or in case of implicit case node. */
-    struct lysc_node *parent;        /**< parent node (NULL in case of top level node) */
-    struct lysc_node *next;          /**< next sibling node (NULL if there is no one) */
-    struct lysc_node *prev;          /**< pointer to the previous sibling node \note Note that this pointer is
-                                          never NULL. If there is no sibling node, pointer points to the node
-                                          itself. In case of the first node, this pointer points to the last
-                                          node in the list. */
-    const char *name;                /**< node name (mandatory) */
-    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-
-    struct lysc_node *child;
-};
-
 /**
  * @brief Compiled YANG schema tree structure representing YANG module.
  *
@@ -1438,10 +1416,7 @@
  * @{
  */
 #define LYSC_OPT_FREE_SP 1           /**< Free the input printable schema */
-
-/**
- * @}
- */
+/** @} scflags */
 
 /**
  * @brief Compile printable schema into a validated schema linking all the references.
@@ -1453,6 +1428,68 @@
  */
 LY_ERR lys_compile(struct lys_module *mod, int options);
 
+/**
+ * @brief Get next schema tree (sibling) node element that can be instantiated in a data tree. Returned node can
+ * be from an augment.
+ *
+ * lys_getnext() is supposed to be called sequentially. In the first call, the \p last parameter is usually NULL
+ * and function starts returning i) the first \p parent's child or ii) the first top level element of the \p module.
+ * Consequent calls suppose to provide the previously returned node as the \p last parameter and still the same
+ * \p parent and \p module parameters.
+ *
+ * Without options, the function is used to traverse only the schema nodes that can be paired with corresponding
+ * data nodes in a data tree. By setting some \p options the behavior can be modified to the extent that
+ * all the schema nodes are iteratively returned.
+ *
+ * @param[in] last Previously returned schema tree node, or NULL in case of the first call.
+ * @param[in] parent Parent of the subtree where the function starts processing.
+ * @param[in] module In case of iterating on top level elements, the \p parent is NULL and
+ * module must be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Next schema tree node that can be instantiated in a data tree, NULL in case there is no such element.
+ */
+const struct lysc_node *lys_getnext(const struct lysc_node *last, const struct lysc_node *parent,
+                                    const struct lysc_module *module, int options);
+
+/**
+ * @defgroup sgetnextflags lys_getnext() flags
+ * @ingroup schematree
+ *
+ * @{
+ */
+#define LYS_GETNEXT_WITHCHOICE   0x01 /**< lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
+#define LYS_GETNEXT_INTONPCONT   0x40 /**< lys_getnext() option to look into non-presence container, instead of returning container itself */
+#define LYS_GETNEXT_NOSTATECHECK 0x100 /**< lys_getnext() option to skip checking module validity (import-only, disabled) and
+                                            relevant if-feature conditions state */
+/** @} sgetnextflags */
+
+/**
+ * @brief Get child node according to the specified criteria.
+ *
+ * @param[in] parent Optional parent of the node to find. If not specified, the module's top-level nodes are searched.
+ * @param[in] module module of the node to find. It is also limitation for the children node of the given parent.
+ * @param[in] name Name of the node to find.
+ * @param[in] name_len Optional length of the name in case it is not NULL-terminated string.
+ * @param[in] nodetype Optional criteria (to speedup) specifying nodetype(s) of the node to find.
+ * Used as a bitmask, so multiple nodetypes can be specified.
+ * @param[in] options [ORed options](@ref sgetnextflags).
+ * @return Found node if any.
+ */
+const struct lysc_node *lys_child(const struct lysc_node *parent, const struct lys_module *module,
+                                  const char *name, size_t name_len, uint16_t nodetype, int options);
+
+/**
+ * @brief Check if the schema node is disabled in the schema tree, i.e. there is any disabled if-feature statement
+ * affecting the node.
+ *
+ * @param[in] node Schema node to check.
+ * @param[in] recursive - 0 to check if-feature only in the \p node schema node,
+ * - 1 to check if-feature in all ascendant schema nodes until there is a node possibly having an instance in a data tree
+ * @return - NULL if enabled,
+ * - pointer to the node with the unsatisfied (disabling) if-feature expression.
+ */
+const struct lysc_iffeature *lys_is_disabled(const struct lysc_node *node, int recursive);
+
 /** @} */
 
 #ifdef __cplusplus
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 63bc3c5..93fa536 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -493,6 +493,19 @@
     return ret;
 }
 
+/**
+ * @brief Find and process the referenced base identities from another identity or identityref
+ *
+ * For bases in identity se backlinks to them from the base identities. For identityref, store
+ * the array of pointers to the base identities. So one of the ident or bases parameter must be set
+ * to distinguish these two use cases.
+ *
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] bases_p Array of names (including prefix if necessary) of base identities.
+ * @param[in] ident Referencing identity to work with.
+ * @param[in] bases Array of bases of identityref to fill in.
+ * @return LY_ERR value.
+ */
 static LY_ERR
 lys_compile_identity_bases(struct lysc_ctx *ctx, const char **bases_p,  struct lysc_ident *ident, struct lysc_ident ***bases)
 {
@@ -560,6 +573,13 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief For the given array of identities, set the backlinks from all their base identities.
+ * @param[in] ctx Compile context, not only for logging but also to get the current module to resolve prefixes.
+ * @param[in] idents_p Array of identities definitions from the parsed schema structure.
+ * @param[in] idents Array of referencing identities to which the backlinks are supposed to be set.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
 static LY_ERR
 lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident *idents)
 {
@@ -574,6 +594,14 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief Create compiled feature structure.
+ * @param[in] ctx Compile context.
+ * @param[in] feature_p Parsed feature definition to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] feature Compiled feature structure to fill.
+ * @return LY_ERR value.
+ */
 static LY_ERR
 lys_compile_feature(struct lysc_ctx *ctx, struct lysp_feature *feature_p, int options, struct lysc_feature *feature)
 {
@@ -602,6 +630,19 @@
     return ret;
 }
 
+/**
+ * @brief Validate and normalize numeric value from a range definition.
+ * @param[in] ctx Compile context.
+ * @param[in] basetype Base YANG built-in type of the node connected with the range restriction. Actually only LY_TYPE_DEC64 is important to
+ * allow processing of the fractions. The fraction point is extracted from the value which is then normalize according to given frdigits into
+ * valcopy to allow easy parsing and storing of the value. libyang stores decimal number without the decimal point which is always recovered from
+ * the known fraction-digits value. So, with fraction-digits 2, number 3.14 is stored as 314 and number 1 is stored as 100.
+ * @param[in] frdigits The fraction-digits of the type in case of LY_TYPE_DEC64.
+ * @param[in] value String value of the range boundary.
+ * @param[out] len Number of the processed bytes from the value. Processing stops on the first character which is not part of the number boundary.
+ * @param[out] valcopy NULL-terminated string with the numeric value to parse and store.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID (no number) or LY_EINVAL (decimal64 not matching fraction-digits value).
+ */
 static LY_ERR
 range_part_check_value_syntax(struct lysc_ctx *ctx, LY_DATA_TYPE basetype, uint8_t frdigits, const char *value, size_t *len, char **valcopy)
 {
@@ -668,8 +709,15 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief Check that values in range are in ascendant order.
+ * @param[in] unsigned_value Flag to note that we are working with unsigned values.
+ * @param[in] value Current value to check.
+ * @param[in] prev_value The last seen value.
+ * @return LY_SUCCESS or LY_EEXIST for invalid order.
+ */
 static LY_ERR
-range_part_check_ascendance(int unsigned_value, int64_t value, int64_t prev_value)
+range_part_check_ascendancy(int unsigned_value, int64_t value, int64_t prev_value)
 {
     if (unsigned_value) {
         if ((uint64_t)prev_value >= (uint64_t)value) {
@@ -683,6 +731,21 @@
     return LY_SUCCESS;
 }
 
+/**
+ * @brief Set min/max value of the range part.
+ * @param[in] ctx Compile context.
+ * @param[in] part Range part structure to fill.
+ * @param[in] max Flag to distinguish if storing min or max value.
+ * @param[in] prev The last seen value to check that all values in range are specified in ascendant order.
+ * @param[in] basetype Type of the value to get know implicit min/max values and other checking rules.
+ * @param[in] first Flag for the first value of the range to avoid ascendancy order.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in,out] value Numeric range value to be stored, if not provided the type's min/max value is set.
+ * @return LY_ERR value - LY_SUCCESS, LY_EDENIED (value brokes type's boundaries), LY_EVALID (not a number),
+ * LY_EEXIST (value is smaller than the previous one), LY_EINVAL (decimal64 value does not corresponds with the
+ * frdigits value), LY_EMEM.
+ */
 static LY_ERR
 range_part_minmax(struct lysc_ctx *ctx, struct lysc_range_part *part, int max, int64_t prev, LY_DATA_TYPE basetype, int first, int length_restr,
                   uint8_t frdigits, const char **value)
@@ -706,7 +769,7 @@
             part->min_u64 = UINT64_C(0);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_DEC64: /* range */
@@ -719,7 +782,7 @@
             part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_INT8: /* range */
@@ -731,7 +794,7 @@
             part->min_64 = INT64_C(-128);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_INT16: /* range */
@@ -743,7 +806,7 @@
             part->min_64 = INT64_C(-32768);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_INT32: /* range */
@@ -755,7 +818,7 @@
             part->min_64 = INT64_C(-2147483648);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_INT64: /* range */
@@ -768,7 +831,7 @@
             part->min_64 = INT64_C(-9223372036854775807) - INT64_C(1);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(0, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(0, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_UINT8: /* range */
@@ -780,7 +843,7 @@
             part->min_u64 = UINT64_C(0);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_UINT16: /* range */
@@ -792,7 +855,7 @@
             part->min_u64 = UINT64_C(0);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_UINT32: /* range */
@@ -804,7 +867,7 @@
             part->min_u64 = UINT64_C(0);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_UINT64: /* range */
@@ -816,7 +879,7 @@
             part->min_u64 = UINT64_C(0);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
     case LY_TYPE_STRING: /* length */
@@ -828,7 +891,7 @@
             part->min_u64 = UINT64_C(0);
         }
         if (!ret && !first) {
-            ret = range_part_check_ascendance(1, max ? part->max_64 : part->min_64, prev);
+            ret = range_part_check_ascendancy(1, max ? part->max_64 : part->min_64, prev);
         }
         break;
     default:
@@ -857,6 +920,18 @@
     return ret;
 }
 
+/**
+ * @brief Compile the parsed range restriction.
+ * @param[in] ctx Compile context.
+ * @param[in] range_p Parsed range structure to compile.
+ * @param[in] basetype Base YANG built-in type of the node with the range restriction.
+ * @param[in] length_restr Flag to distinguish between range and length restrictions. Only for logging.
+ * @param[in] frdigits The fraction-digits value in case of LY_TYPE_DEC64 basetype.
+ * @param[in] base_range Range restriction of the type from which the current type is derived. The current
+ * range restriction must be more restrictive than the base_range.
+ * @param[in,out] range Pointer to the created current range structure.
+ * @return LY_ERR value.
+ */
 static LY_ERR
 lys_compile_type_range(struct lysc_ctx *ctx, struct lysp_restr *range_p, LY_DATA_TYPE basetype, int length_restr, uint8_t frdigits,
                        struct lysc_range *base_range, struct lysc_range **range)
@@ -1080,9 +1155,10 @@
 /**
  * @brief Checks pattern syntax.
  *
+ * @param[in] ctx Compile context.
  * @param[in] pattern Pattern to check.
- * @param[out] pcre_precomp Precompiled PCRE pattern. Can be NULL.
- * @return EXIT_SUCCESS on success, EXIT_FAILURE otherwise.
+ * @param[in,out] pcre_precomp Precompiled PCRE pattern. If NULL, the compiled information used to validate pattern are freed.
+ * @return LY_ERR value - LY_SUCCESS, LY_EMEM, LY_EVALID.
  */
 static LY_ERR
 lys_compile_type_pattern_check(struct lysc_ctx *ctx, const char *pattern, pcre **pcre_precomp)
@@ -1287,6 +1363,16 @@
 #undef URANGE_LEN
 }
 
+/**
+ * @brief Compile parsed pattern restriction in conjunction with the patterns from base type.
+ * @param[in] ctx Compile context.
+ * @param[in] patterns_p Array of parsed patterns from the current type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_patterns Compiled patterns from the type from which the current type is derived.
+ * Patterns from the base type are inherited to have all the patterns that have to match at one place.
+ * @param[out] patterns Pointer to the storage for the patterns of the current type.
+ * @return LY_ERR LY_SUCCESS, LY_EMEM, LY_EVALID.
+ */
 static LY_ERR
 lys_compile_type_patterns(struct lysc_ctx *ctx, struct lysp_restr *patterns_p, int options,
                           struct lysc_pattern **base_patterns, struct lysc_pattern ***patterns)
@@ -1326,6 +1412,9 @@
     return ret;
 }
 
+/**
+ * @brief map of the possible restrictions combination for the specific built-in type.
+ */
 static uint16_t type_substmt_map[LY_DATA_TYPE_COUNT] = {
     0 /* LY_TYPE_UNKNOWN */,
     LYS_SET_LENGTH /* LY_TYPE_BINARY */,
@@ -1349,6 +1438,16 @@
     LYS_SET_RANGE /* LY_TYPE_UINT64 */
 };
 
+/**
+ * @brief Compile parsed type's enum structures (for enumeration and bits types).
+ * @param[in] ctx Compile context.
+ * @param[in] enums_p Array of the parsed enum structures to compile.
+ * @param[in] basetype Base YANG built-in type from which the current type is derived. Only LY_TYPE_ENUM and LY_TYPE_BITS are expected.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] base_enums Array of the compiled enums information from the (latest) base type to check if the current enums are compatible.
+ * @param[out] enums Newly created array of the compiled enums information for the current type.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
 static LY_ERR
 lys_compile_type_enums(struct lysc_ctx *ctx, struct lysp_type_enum *enums_p, LY_DATA_TYPE basetype, int options,
                        struct lysc_type_enum_item *base_enums, struct lysc_type_enum_item **enums)
@@ -1484,6 +1583,384 @@
     return ret;
 }
 
+#define MOVE_PATH_PARENT(NODE, LIMIT_COND, TERM, ERR_MSG, ...) \
+    for ((NODE) = (NODE)->parent; \
+         (NODE) && !((NODE)->nodetype & (LYS_CONTAINER | LYS_LIST | LYS_ACTION | LYS_NOTIF | LYS_ACTION)); \
+         (NODE) = (NODE)->parent); \
+    if (!(NODE) && (LIMIT_COND)) { /* we are going higher than top-level */ \
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, ERR_MSG, ##__VA_ARGS__); \
+        TERM; \
+    }
+
+/**
+ * @brief Validate the predicate(s) from the leafref path.
+ * @param[in] ctx Compile context
+ * @param[in, out] predicate Pointer to the predicate in the leafref path. The pointer is moved after the validated predicate(s).
+ * Since there can be multiple adjacent predicates for lists with multiple keys, all such predicates are validated.
+ * @param[in] context_node Predicate context node (where the predicate is placed).
+ * @param[in] startnode Path context node (where the leafref path begins/is placed).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
+static LY_ERR
+lys_compile_leafref_predicate_validate(struct lysc_ctx *ctx, const char **predicate,
+                                       const struct lysc_node *context_node, const struct lysc_node *startnode)
+{
+    LY_ERR ret = LY_EVALID;
+    const struct lys_module *mod;
+    const struct lysc_node *src_node, *dst_node;
+    const char *path_key_expr, *pke_start, *src, *src_prefix, *dst, *dst_prefix;
+    size_t src_len, src_prefix_len, dst_len, dst_prefix_len;
+    unsigned int dest_parent_times;
+    const char *start, *end, *pke_end;
+    struct ly_set keys = {0};
+    int i;
+
+    while (**predicate == '[') {
+        start = (*predicate)++;
+
+        while (isspace(**predicate)) {
+            ++(*predicate);
+        }
+        LY_CHECK_GOTO(lys_parse_nodeid(predicate, &src_prefix, &src_prefix_len, &src, &src_len), cleanup);
+        while (isspace(**predicate)) {
+            ++(*predicate);
+        }
+        if (**predicate != '=') {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - missing \"=\".", *predicate - start + 1, *predicate);
+            goto cleanup;
+        }
+        ++(*predicate);
+        while (isspace(**predicate)) {
+            ++(*predicate);
+        }
+
+        if ((end = pke_end = strchr(*predicate, ']')) == NULL) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%s\" - missing predicate termination.", start);
+            goto cleanup;
+        }
+        --pke_end;
+        while (isspace(*pke_end)) {
+            --pke_end;
+        }
+        /* localize path-key-expr */
+        pke_start = path_key_expr = *predicate;
+        /* move after the current predicate */
+        *predicate = end + 1;
+
+        /* source (must be leaf or leaf-list) */
+        if (src_prefix) {
+            mod = lys_module_find_prefix(startnode->module, src_prefix, src_prefix_len);
+        } else {
+            mod = startnode->module;
+        }
+        src_node = lys_child(context_node, mod, src, src_len,
+                             mod->compiled->version < LYS_VERSION_1_1 ? LYS_LEAF : LYS_LEAF | LYS_LEAFLIST, LYS_GETNEXT_NOSTATECHECK);
+        if (!src_node) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - key node \"%.*s\" from module \"%s\" not found.",
+                   *predicate - start, start, src_len, src, mod->compiled->name);
+            goto cleanup;
+        }
+        /* TODO - check the src_node is really a key of the context_node */
+
+        /* check that there is only one predicate for the */
+        i = ly_set_add(&keys, (void*)src_node, 0);
+        LY_CHECK_GOTO(i == -1, cleanup);
+        if (keys.count != (unsigned int)i + 1) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - multiple equality test for the key %s.",
+                   *predicate - start, start, src_node->name);
+            goto cleanup;
+        }
+
+        /* destination */
+        dest_parent_times = 0;
+        dst_node = context_node;
+
+        /* current-function-invocation *WSP "/" *WSP rel-path-keyexpr */
+        if (strncmp(path_key_expr, "current()", 9)) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - missing current-function-invocation.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+        path_key_expr += 9;
+        while (isspace(*path_key_expr)) {
+            ++path_key_expr;
+        }
+
+        if (*path_key_expr != '/') {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - missing \"/\" after current-function-invocation.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+        ++path_key_expr;
+        while (isspace(*path_key_expr)) {
+            ++path_key_expr;
+        }
+
+        /* rel-path-keyexpr:
+         * 1*(".." *WSP "/" *WSP) *(node-identifier *WSP "/" *WSP) node-identifier */
+        while (!strncmp(path_key_expr, "..", 2)) {
+            ++dest_parent_times;
+            path_key_expr += 2;
+            while (isspace(*path_key_expr)) {
+                ++path_key_expr;
+            }
+            if (*path_key_expr != '/') {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path predicate \"%.*s\" - missing \"/\" in \"../\" rel-path-keyexpr pattern.",
+                       *predicate - start, start);
+                goto cleanup;
+            }
+            ++path_key_expr;
+            while (isspace(*path_key_expr)) {
+                ++path_key_expr;
+            }
+
+            /* 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(dst_node, !strncmp(path_key_expr, "..", 2), goto cleanup,
+                             "Invalid leafref path predicate \"%.*s\" - too many \"..\" in rel-path-keyexpr.",
+                             *predicate - start, start);
+        }
+        if (!dest_parent_times) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - at least one \"..\" is expected in rel-path-keyexpr.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+        if (path_key_expr == pke_end) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - at least one node-identifier is expected in rel-path-keyexpr.",
+                   *predicate - start, start);
+            goto cleanup;
+        }
+
+        while(path_key_expr != pke_end) {
+            if (lys_parse_nodeid(&path_key_expr, &dst_prefix, &dst_prefix_len, &dst, &dst_len)) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
+                       "Invalid node identifier in leafref path predicate - character %d (%.*s).",
+                       path_key_expr - start, *predicate - start, start);
+                goto cleanup;
+            }
+
+            if (dst_prefix) {
+                mod = lys_module_find_prefix(startnode->module, dst_prefix, dst_prefix_len);
+            } else {
+                mod = startnode->module;
+            }
+            if (!mod) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path predicate \"%.*s\" - unable to find module of the node \"%.*s\" in rel-path_keyexpr.",
+                       *predicate - start, start, dst_len, dst);
+                goto cleanup;
+            }
+
+            dst_node = lys_child(dst_node, mod, dst, dst_len, 0, LYS_GETNEXT_NOSTATECHECK);
+            if (!dst_node) {
+                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                       "Invalid leafref path predicate \"%.*s\" - unable to find node \"%.*s\" in the rel-path_keyexpr.",
+                       *predicate - start, start, path_key_expr - pke_start, pke_start);
+                goto cleanup;
+            }
+        }
+        if (!(dst_node->nodetype & (dst_node->module->compiled->version < LYS_VERSION_1_1 ? LYS_LEAF : LYS_LEAF | LYS_LEAFLIST))) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+                   "Invalid leafref path predicate \"%.*s\" - rel-path_keyexpr \"%.*s\" refers %s.",
+                   *predicate - start, start, path_key_expr - pke_start, pke_start, lys_nodetype2str(dst_node->nodetype));
+            goto cleanup;
+        }
+    }
+
+    ret = LY_SUCCESS;
+cleanup:
+    ly_set_erase(&keys, NULL);
+    return ret;
+}
+
+/**
+ * @brief Parse path-arg (leafref). Get tokens of the path by repetitive calls of the function.
+ *
+ * path-arg            = absolute-path / relative-path
+ * absolute-path       = 1*("/" (node-identifier *path-predicate))
+ * relative-path       = 1*(".." "/") descendant-path
+ *
+ * @param[in,out] path Path to parse.
+ * @param[out] prefix Prefix of the token, NULL if there is not any.
+ * @param[out] pref_len Length of the prefix, 0 if there is not any.
+ * @param[out] name Name of the token.
+ * @param[out] nam_len Length of the name.
+ * @param[out] parent_times Number of leading ".." in the path. Must be 0 on the first call,
+ *                          must not be changed between consecutive calls. -1 if the
+ *                          path is absolute.
+ * @param[out] has_predicate Flag to mark whether there is a predicate specified.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the path.
+ */
+static LY_ERR
+lys_path_token(const char **path, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len,
+               int *parent_times, int *has_predicate)
+{
+    int par_times = 0;
+
+    assert(path && *path);
+    assert(parent_times);
+    assert(prefix);
+    assert(prefix_len);
+    assert(name);
+    assert(name_len);
+    assert(has_predicate);
+
+    *prefix = NULL;
+    *prefix_len = 0;
+    *name = NULL;
+    *name_len = 0;
+    *has_predicate = 0;
+
+    if (!*parent_times) {
+        if (!strncmp(*path, "..", 2)) {
+            *path += 2;
+            ++par_times;
+            while (!strncmp(*path, "/..", 3)) {
+                *path += 3;
+                ++par_times;
+            }
+        }
+        if (par_times) {
+            *parent_times = par_times;
+        } else {
+            *parent_times = -1;
+        }
+    }
+
+    if (**path != '/') {
+        return LY_EINVAL;
+    }
+    /* skip '/' */
+    ++(*path);
+
+    /* node-identifier ([prefix:]name) */
+    LY_CHECK_RET(lys_parse_nodeid(path, prefix, prefix_len, name, name_len));
+
+    if ((**path == '/' && (*path)[1]) || !**path) {
+        /* path continues by another token or this is the last token */
+        return LY_SUCCESS;
+    } else if ((*path)[0] != '[') {
+        /* unexpected character */
+        return LY_EINVAL;
+    } else {
+        /* predicate starting with [ */
+        *has_predicate = 1;
+        return LY_SUCCESS;
+    }
+}
+
+/**
+ * @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.
+ * @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)
+{
+    const struct lysc_node *node = NULL, *parent = NULL;
+    const struct lys_module *mod;
+    const char *id, *prefix, *name;
+    size_t prefix_len, name_len;
+    int parent_times = 0, has_predicate;
+    unsigned int iter, u;
+    LY_ERR ret = LY_SUCCESS;
+
+    assert(ctx);
+    assert(startnode);
+    assert(path);
+
+    iter = 0;
+    id = 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 */
+            if (parent_times > 0) {
+                /* move from the context node */
+                for (u = 0, parent = startnode; u < (unsigned int)parent_times; u++) {
+                    /* 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);
+                }
+            }
+        }
+
+        if (prefix) {
+            mod = lys_module_find_prefix(startnode->module, prefix, prefix_len);
+        } else {
+            mod = startnode->module;
+        }
+        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);
+            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);
+            return LY_EVALID;
+        }
+        parent = node;
+
+        if (has_predicate) {
+            /* we have predicate, so the current result must be list */
+            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));
+                return LY_EVALID;
+            }
+
+            LY_CHECK_RET(lys_compile_leafref_predicate_validate(ctx, &id, node, startnode), 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);
+        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));
+        return LY_EVALID;
+    }
+
+    /* check status */
+    if (lysc_check_status(ctx, startnode->flags, startnode->module, startnode->name, node->flags, node->module, node->name)) {
+        return LY_EVALID;
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief The core of the lys_compile_type() - compile information about the given type (from typedef or leaf/leaf-list).
+ * @param[in] ctx Compile context.
+ * @param[in] type_p Parsed type to compile.
+ * @param[in] basetype Base YANG built-in type of the type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] tpdfname Name of the type's typedef, serves as a flag - if it is leaf/leaf-list's type, it is NULL.
+ * @param[in] base The latest base (compiled) type from which the current type is being derived.
+ * @param[out] type Newly created type structure with the filled information about the type.
+ * @return LY_ERR value.
+ */
 static LY_ERR
 lys_compile_type_(struct lysc_ctx *ctx, struct lysp_type *type_p, LY_DATA_TYPE basetype, int options, const char *tpdfname,
                   struct lysc_type *base,  struct lysc_type **type)
@@ -1715,6 +2192,32 @@
             *type = calloc(1, sizeof(struct lysc_type_identityref));
         }
         break;
+    case LY_TYPE_LEAFREF:
+        /* 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 {
+            /* default is true */
+            ((struct lysc_type_leafref*)(*type))->require_instance = 1;
+        }
+        if (type_p->path) {
+            DUP_STRING(ctx->ctx, (void*)type_p->path, ((struct lysc_type_leafref*)(*type))->path);
+        } else if (base) {
+            DUP_STRING(ctx->ctx, ((struct lysc_type_leafref*)base)->path, ((struct lysc_type_leafref*)(*type))->path);
+        } else if (tpdfname) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type ", tpdfname);
+            return LY_EVALID;
+        } else {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_MISSCHILDSTMT, "path", "leafref type", "");
+            free(*type);
+            *type = NULL;
+            return LY_EVALID;
+        }
+        if (tpdfname) {
+            type_p->compiled = *type;
+            *type = calloc(1, sizeof(struct lysc_type_leafref));
+        }
+        break;
     case LY_TYPE_INST:
         /* RFC 7950 9.9.3 - require-instance */
         if (type_p->flags & LYS_SET_REQINST) {
@@ -1739,6 +2242,14 @@
     return ret;
 }
 
+/**
+ * @brief Compile information about the leaf/leaf-list's type.
+ * @param[in] ctx Compile context.
+ * @param[in] leaf_p Parsed leaf with the type to compile.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[out] type Newly created (or reused with increased refcount) type structure with the filled information about the type.
+ * @return LY_ERR value.
+ */
 static LY_ERR
 lys_compile_type(struct lysc_ctx *ctx, struct lysp_node_leaf *leaf_p, int options, struct lysc_type **type)
 {
@@ -1860,7 +2371,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;
@@ -1876,8 +2387,8 @@
     }
 
     /* process the type definition in leaf */
-    if (leaf_p->type.flags || !base) {
-        /* get restrictions from the node itself, finalize the type structure */
+    if (leaf_p->type.flags || !base || basetype == LY_TYPE_LEAFREF) {
+        /* get restrictions from the node itself */
         (*type)->basetype = basetype;
         ++(*type)->refcount;
         ret = lys_compile_type_(ctx, &leaf_p->type, basetype, options, NULL, base, type);
@@ -1898,6 +2409,15 @@
 
 static LY_ERR lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent);
 
+/**
+ * @brief Compile parsed container node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed container node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the container-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
 static LY_ERR
 lys_compile_node_container(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
 {
@@ -1922,6 +2442,15 @@
     return ret;
 }
 
+/**
+ * @brief Compile parsed leaf node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed leaf node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in,out] node Pre-prepared structure from lys_compile_node() with filled generic node information
+ * is enriched with the leaf-specific information.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
 static LY_ERR
 lys_compile_node_leaf(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *node)
 {
@@ -1937,12 +2466,27 @@
     ret = lys_compile_type(ctx, leaf_p, options, &leaf->type);
     LY_CHECK_GOTO(ret, done);
 
+    if (leaf->type->basetype == LY_TYPE_LEAFREF) {
+        /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
+        ly_set_add(&ctx->unres, leaf, 0);
+    }
+
     DUP_STRING(ctx->ctx, leaf_p->units, leaf->units);
     DUP_STRING(ctx->ctx, leaf_p->dflt, leaf->dflt);
 done:
     return ret;
 }
 
+/**
+ * @brief Compile parsed schema node information.
+ * @param[in] ctx Compile context
+ * @param[in] node_p Parsed schema node.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @param[in] parent Compiled parent node where the current node is supposed to be connected. It is
+ * NULL for top-level nodes, in such a case the module where the node will be connected is taken from
+ * the compile context.
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
 static LY_ERR
 lys_compile_node(struct lysc_ctx *ctx, struct lysp_node *node_p, int options, struct lysc_node *parent)
 {
@@ -1966,15 +2510,9 @@
     case LYS_LEAFLIST:
         node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_leaflist));
         break;
-    case LYS_CASE:
-        node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_case));
-        break;
     case LYS_CHOICE:
         node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_choice));
         break;
-    case LYS_USES:
-        node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_uses));
-        break;
     case LYS_ANYXML:
     case LYS_ANYDATA:
         node = (struct lysc_node*)calloc(1, sizeof(struct lysc_node_anydata));
@@ -2035,15 +2573,19 @@
     LY_CHECK_GOTO(node_compile_spec(ctx, node_p, options, node), error);
 
     /* insert into parent's children */
-    if (parent && (children = lysc_node_children(parent))) {
-        if (!(*children)) {
-            /* first child */
-            *children = node;
-        } else {
-            /* insert at the end of the parent's children list */
-            (*children)->prev->next = node;
-            node->prev = (*children)->prev;
-            (*children)->prev = node;
+    if (parent) {
+        if (parent->nodetype == LYS_CHOICE) {
+            /* TODO exception for cases */
+        } else if ((children = lysc_node_children(parent))) {
+            if (!(*children)) {
+                /* first child */
+                *children = node;
+            } else {
+                /* insert at the end of the parent's children list */
+                (*children)->prev->next = node;
+                node->prev = (*children)->prev;
+                (*children)->prev = node;
+            }
         }
     } else {
         /* top-level element */
@@ -2064,11 +2606,18 @@
     return ret;
 }
 
+/**
+ * @brief Compile the given YANG module.
+ * @param[in] mod Module structure where the parsed schema is expected and the compiled schema will be placed.
+ * @param[in] options Various options to modify compiler behavior, see [compile flags](@ref scflags).
+ * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
+ */
 LY_ERR
 lys_compile(struct lys_module *mod, int options)
 {
     struct lysc_ctx ctx = {0};
     struct lysc_module *mod_c;
+    struct lysc_type *type;
     struct lysp_module *sp;
     struct lysp_node *node_p;
     unsigned int u;
@@ -2114,6 +2663,20 @@
 
     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 (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);
+                LY_CHECK_GOTO(ret, error);
+            }
+        }
+    }
+    ly_set_erase(&ctx.unres, NULL);
+
     if (options & LYSC_OPT_FREE_SP) {
         lysp_module_free(mod->parsed);
         ((struct lys_module*)mod)->parsed = NULL;
@@ -2123,6 +2686,7 @@
     return LY_SUCCESS;
 
 error:
+    ly_set_erase(&ctx.unres, NULL);
     lysc_module_free(mod_c, NULL);
     ((struct lys_module*)mod)->compiled = NULL;
     return ret;
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 7e74603..1e52dd8 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -516,6 +516,12 @@
     FREE_ARRAY(ctx, item->exts, lysc_ext_instance_free);
 }
 
+static void lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type);
+static void
+lysc_type2_free(struct ly_ctx *ctx, struct lysc_type **type)
+{
+    lysc_type_free(ctx, *type);
+}
 static void
 lysc_type_free(struct ly_ctx *ctx, struct lysc_type *type)
 {
@@ -552,6 +558,12 @@
     case LY_TYPE_IDENT:
         LY_ARRAY_FREE(((struct lysc_type_identityref*)type)->bases);
         break;
+    case LY_TYPE_UNION:
+        FREE_ARRAY(ctx, ((struct lysc_type_union*)type)->types, lysc_type2_free);
+        break;
+    case LY_TYPE_LEAFREF:
+        FREE_STRING(ctx, ((struct lysc_type_leafref*)type)->path);
+        break;
     case LY_TYPE_INST:
     case LY_TYPE_BOOL:
     case LY_TYPE_EMPTY:
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 52b6f61..6f3815c 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -27,6 +27,63 @@
 #include "libyang.h"
 #include "tree_schema_internal.h"
 
+/**
+ * @brief Parse an identifier.
+ *
+ * ;; An identifier MUST NOT start with (('X'|'x') ('M'|'m') ('L'|'l'))
+ * identifier          = (ALPHA / "_")
+ *                       *(ALPHA / DIGIT / "_" / "-" / ".")
+ *
+ * @param[in,out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid starting character.
+ */
+static LY_ERR
+lys_parse_id(const char **id)
+{
+    assert(id && *id);
+
+    if (!isalpha(**id) && (**id != '_')) {
+        return LY_EINVAL;
+    }
+    ++(*id);
+
+    while (isalnum(**id) || (**id == '_') || (**id == '-') || (**id == '.')) {
+        ++(*id);
+    }
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len)
+{
+    assert(id && *id);
+    assert(prefix && prefix_len);
+    assert(name && name_len);
+
+    *prefix = *id;
+    *prefix_len = 0;
+    *name = NULL;
+    *name_len = 0;
+
+    LY_CHECK_RET(lys_parse_id(id));
+    if (**id == ':') {
+        /* there is prefix */
+        *prefix_len = *id - *prefix;
+        ++(*id);
+        *name = *id;
+
+        LY_CHECK_RET(lys_parse_id(id));
+        *name_len = *id - *name;
+    } else {
+        /* there is no prefix, so what we have as prefix now is actually the name */
+        *name = *prefix;
+        *name_len = *id - *name;
+        *prefix = NULL;
+    }
+
+    return LY_SUCCESS;
+}
+
 LY_ERR
 lysp_check_prefix(struct ly_parser_ctx *ctx, struct lysp_module *module, const char **value)
 {
@@ -737,7 +794,7 @@
     }
 
 struct lysc_module *
-lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
+lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len)
 {
     const struct lys_module *m = NULL;
 
@@ -746,7 +803,7 @@
 }
 
 struct lysp_module *
-lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len)
+lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len)
 {
     const struct lys_module *m = NULL;
 
@@ -755,7 +812,7 @@
 }
 
 struct lys_module *
-lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len)
+lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len)
 {
     const struct lys_module *m = NULL;
 
@@ -767,6 +824,29 @@
     return (struct lys_module*)m;
 }
 
+const char *
+lys_nodetype2str(uint16_t nodetype)
+{
+    switch(nodetype) {
+    case LYS_CONTAINER:
+        return "container";
+    case LYS_CHOICE:
+        return "choice";
+    case LYS_LEAF:
+        return "leaf";
+    case LYS_LEAFLIST:
+        return "leaf-list";
+    case LYS_LIST:
+        return "list";
+    case LYS_ANYXML:
+        return "anyxml";
+    case LYS_ANYDATA:
+        return "anydata";
+    default:
+        return "unknown";
+    }
+}
+
 struct lysp_tpdf **
 lysp_node_typedefs(struct lysp_node *node)
 {
@@ -851,20 +931,20 @@
 }
 
 struct lysc_node **
-lysc_node_children(struct lysc_node *node)
+lysc_node_children(const struct lysc_node *node)
 {
     assert(node);
     switch (node->nodetype) {
     case LYS_CONTAINER:
         return &((struct lysc_node_container*)node)->child;
     case LYS_CHOICE:
-        return &((struct lysc_node_choice*)node)->child;
+        if (((struct lysc_node_choice*)node)->cases) {
+            return &((struct lysc_node_choice*)node)->cases[0].child;
+        } else {
+            return NULL;
+        }
     case LYS_LIST:
         return &((struct lysc_node_list*)node)->child;
-    case LYS_CASE:
-        return &((struct lysc_node_case*)node)->child;
-    case LYS_USES:
-        return &((struct lysc_node_uses*)node)->child;
 /* TODO
     case LYS_INOUT:
         return &((struct lysc_action_inout*)node)->child;
@@ -876,3 +956,27 @@
     }
 }
 
+struct lysc_iffeature **
+lysc_node_iff(const struct lysc_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysc_node_container*)node)->iffeatures;
+    case LYS_LEAF:
+        return &((struct lysc_node_leaf*)node)->iffeatures;
+/* TODO
+    case LYS_LIST:
+        return &((struct lysc_node_list*)node)->iffeatures;
+    case LYS_CASE:
+        return &((struct lysc_node_case*)node)->iffeatures;
+    case LYS_USES:
+        return &((struct lysc_node_uses*)node)->iffeatures;
+    case LYS_NOTIF:
+        return &((struct lysc_notif*)node)->child;
+*/
+    default:
+        return NULL;
+    }
+}
+
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 1ca6d4c..cb7e2a4 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -56,6 +56,7 @@
 struct lysc_ctx {
     struct ly_ctx *ctx;
     struct lys_module *mod;
+    struct ly_set unres;        /* to validate leafref's target and xpath of when/must */
     uint16_t path_len;
 #define LYSC_CTX_BUFSIZE 4078
     char path[LYSC_CTX_BUFSIZE];
@@ -178,7 +179,14 @@
  * @param[in] node Node to check.
  * @return Address of the node's child member if any, NULL otherwise.
  */
-struct lysc_node **lysc_node_children(struct lysc_node *node);
+struct lysc_node **lysc_node_children(const struct lysc_node *node);
+
+/**
+ * @brief Get address of a node's iffeatures pointer if any.
+ * @param[in] node Node to check.
+ * @return Address of the node's iffeature member if any, NULL otherwise.
+ */
+struct lysc_iffeature **lysc_node_iff(const struct lysc_node *node);
 
 /**
  * @brief Find the module referenced by prefix in the provided parsed mod.
@@ -188,7 +196,7 @@
  * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
  * @return Pointer to the module or NULL if the module is not found.
  */
-struct lysp_module *lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len);
+struct lysp_module *lysp_module_find_prefix(const struct lysp_module *mod, const char *prefix, size_t len);
 
 /**
  * @brief Find the module referenced by prefix in the provided compiled mod.
@@ -198,7 +206,7 @@
  * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
  * @return Pointer to the module or NULL if the module is not found.
  */
-struct lysc_module *lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len);
+struct lysc_module *lysc_module_find_prefix(const struct lysc_module *mod, const char *prefix, size_t len);
 
 /**
  * @brief Check statement's status for invalid combination.
@@ -221,6 +229,20 @@
                          uint16_t flags2, void *mod2, const char *name2);
 
 /**
+ * @brief Parse a node-identifier.
+ *
+ * node-identifier     = [prefix ":"] identifier
+ *
+ * @param[in, out] id Identifier to parse. When returned, it points to the first character which is not part of the identifier.
+ * @param[out] prefix Node's prefix, NULL if there is not any.
+ * @param[out] prefix_len Length of the node's prefix, 0 if there is not any.
+ * @param[out] name Node's name.
+ * @param[out] nam_len Length of the node's name.
+ * @return LY_ERR value: LY_SUCCESS or LY_EINVAL in case of invalid character in the id.
+ */
+LY_ERR lys_parse_nodeid(const char **id, const char **prefix, size_t *prefix_len, const char **name, size_t *name_len);
+
+/**
  * @brief Find the module referenced by prefix in the provided mod.
  *
  * @param[in] mod Schema module where the prefix was used.
@@ -228,7 +250,14 @@
  * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
  * @return Pointer to the module or NULL if the module is not found.
  */
-struct lys_module *lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len);
+struct lys_module *lys_module_find_prefix(const struct lys_module *mod, const char *prefix, size_t len);
+
+/**
+ * @brief Stringify schema nodetype.
+ * @param[in] nodetype Nodetype to stringify.
+ * @return Constant string with the name of the node's type.
+ */
+const char *lys_nodetype2str(uint16_t nodetype);
 
 /**
  * @brief Parse YANG module and submodule from a string.
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index e3970c5..4e0d973 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -1211,6 +1211,133 @@
     ly_ctx_destroy(ctx, NULL);
 }
 
+static void
+test_type_leafref(void **state)
+{
+    *state = test_type_leafref;
+
+    struct ly_ctx *ctx;
+    struct lys_module *mod;
+    struct lysc_type *type;
+    const char *path, *name, *prefix;
+    size_t prefix_len, name_len;
+    int parent_times, has_predicate;
+
+    assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
+
+    /* lys_path_token() */
+    path = "invalid_path";
+    parent_times = 0;
+    assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    path = "..";
+    parent_times = 0;
+    assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    path = "..[";
+    parent_times = 0;
+    assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    path = "../";
+    parent_times = 0;
+    assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    path = "/";
+    parent_times = 0;
+    assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+
+    path = "../../pref:id/xxx[predicate]/invalid!!!";
+    parent_times = 0;
+    assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    assert_string_equal("/xxx[predicate]/invalid!!!", path);
+    assert_int_equal(4, prefix_len);
+    assert_int_equal(0, strncmp("pref", prefix, prefix_len));
+    assert_int_equal(2, name_len);
+    assert_int_equal(0, strncmp("id", name, name_len));
+    assert_int_equal(2, parent_times);
+    assert_int_equal(0, has_predicate);
+    assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    assert_string_equal("[predicate]/invalid!!!", path);
+    assert_int_equal(0, prefix_len);
+    assert_null(prefix);
+    assert_int_equal(3, name_len);
+    assert_int_equal(0, strncmp("xxx", name, name_len));
+    assert_int_equal(1, has_predicate);
+    path += 11;
+    assert_int_equal(LY_EINVAL, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    assert_string_equal("!!!", path);
+    assert_int_equal(0, prefix_len);
+    assert_null(prefix);
+    assert_int_equal(7, name_len);
+    assert_int_equal(0, strncmp("invalid", name, name_len));
+
+    path = "/absolute/prefix:path";
+    parent_times = 0;
+    assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    assert_string_equal("/prefix:path", path);
+    assert_int_equal(0, prefix_len);
+    assert_null(prefix);
+    assert_int_equal(8, name_len);
+    assert_int_equal(0, strncmp("absolute", name, name_len));
+    assert_int_equal(-1, parent_times);
+    assert_int_equal(0, has_predicate);
+    assert_int_equal(LY_SUCCESS, lys_path_token(&path, &prefix, &prefix_len, &name, &name_len, &parent_times, &has_predicate));
+    assert_int_equal(0, *path);
+    assert_int_equal(6, prefix_len);
+    assert_int_equal(0, strncmp("prefix", prefix, prefix_len));
+    assert_int_equal(4, name_len);
+    assert_int_equal(0, strncmp("path", name, name_len));
+    assert_int_equal(0, has_predicate);
+
+    /* complete leafref paths */
+    assert_non_null(mod = lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a;"
+                                        "leaf ref1 {type leafref {path /a:target1;}} leaf ref2 {type leafref {path /a/target2; require-instance false;}}"
+                                        "leaf target1 {type string;}container a {leaf target2 {type uint8;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
+    type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
+    assert_non_null(type);
+    assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+    assert_string_equal("/a:target1", ((struct lysc_type_leafref*)type)->path);
+    assert_int_equal(1, ((struct lysc_type_leafref*)type)->require_instance);
+    type = ((struct lysc_node_leaf*)mod->compiled->data->next)->type;
+    assert_non_null(type);
+    assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+    assert_string_equal("/a/target2", ((struct lysc_type_leafref*)type)->path);
+    assert_int_equal(0, ((struct lysc_type_leafref*)type)->require_instance);
+
+    /* TODO target in list with predicates */
+
+
+    /* invalid paths */
+    assert_non_null(mod = lys_parse_mem(ctx, "module aa {namespace urn:aa;prefix aa;container a {leaf target2 {type uint8;}}"
+                                        "leaf ref1 {type leafref {path ../a/invalid;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid leafref path - unable to find \"../a/invalid\".");
+    assert_non_null(mod = lys_parse_mem(ctx, "module bb {namespace urn:bb;prefix bb;container a {leaf target2 {type uint8;}}"
+                                        "leaf ref1 {type leafref {path ../../toohigh;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid leafref path \"../../toohigh\" - too many \"..\" in the path.");
+    assert_non_null(mod = lys_parse_mem(ctx, "module cc {namespace urn:cc;prefix cc;container a {leaf target2 {type uint8;}}"
+                                        "leaf ref1 {type leafref {path /a:invalid;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid leafref path - unable to find module connected with the prefix of the node \"/a:invalid\".");
+    assert_non_null(mod = lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;leaf target1 {type string;}container a {leaf target2 {type uint8;}}"
+                                        "leaf ref1 {type leafref {path '/a[target2 = current()/../target1]/target2';}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid leafref path - node \"/a\" is expected to be a list, but it is container.");
+    assert_non_null(mod = lys_parse_mem(ctx, "module ee {namespace urn:ee;prefix ee;container a {leaf target2 {type uint8;}}"
+                                        "leaf ref1 {type leafref {path /a!invalid;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid leafref path at character 3 (/a!invalid).");
+    assert_non_null(mod = lys_parse_mem(ctx, "module ff {namespace urn:ff;prefix ff;container a {leaf target2 {type uint8;}}"
+                                        "leaf ref1 {type leafref {path /a;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid leafref path \"/a\" - target node is container instead of leaf or leaf-list.");
+    assert_non_null(mod = lys_parse_mem(ctx, "module gg {namespace urn:gg;prefix gg;container a {leaf target2 {type uint8; status deprecated;}}"
+                                        "leaf ref1 {type leafref {path /a/target2;}}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("A current definition \"ref1\" is not allowed to reference deprecated definition \"target2\".");
+
+    *state = NULL;
+    ly_ctx_destroy(ctx, NULL);
+}
+
 int main(void)
 {
     const struct CMUnitTest tests[] = {
@@ -1225,6 +1352,7 @@
         cmocka_unit_test_setup_teardown(test_type_dec64, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_type_instanceid, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_type_identityref, logger_setup, logger_teardown),
+        cmocka_unit_test_setup_teardown(test_type_leafref, logger_setup, logger_teardown),
         cmocka_unit_test_setup_teardown(test_node_container, logger_setup, logger_teardown),
     };
 
diff --git a/tests/src/test_tree_schema_helpers.c b/tests/src/test_tree_schema_helpers.c
index 29b7e8c..1c5ca3d 100644
--- a/tests/src/test_tree_schema_helpers.c
+++ b/tests/src/test_tree_schema_helpers.c
@@ -66,6 +66,18 @@
     return 0;
 }
 
+static int
+logger_teardown(void **state)
+{
+    (void) state; /* unused */
+#if ENABLE_LOGGER_CHECKING
+    if (*state) {
+        fprintf(stderr, "%s\n", logbuf);
+    }
+#endif
+    return 0;
+}
+
 void
 logbuf_clean(void)
 {
@@ -81,7 +93,7 @@
 static void
 test_date(void **state)
 {
-    (void) state; /* unused */
+    *state = test_date;
 
     assert_int_equal(LY_EINVAL, lysp_check_date(NULL, NULL, 0, "date"));
     logbuf_assert("Invalid argument date (lysp_check_date()).");
@@ -105,6 +117,8 @@
     assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-11-11", 10, "date"));
     assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2018-02-28", 10, "date"));
     assert_int_equal(LY_SUCCESS, lysp_check_date(NULL, "2016-02-29", 10, "date"));
+
+    *state = NULL;
 }
 
 static void
@@ -149,7 +163,7 @@
 static void
 test_typedef(void **state)
 {
-    (void) state; /* unused */
+    *state = test_typedef;
 
     struct ly_ctx *ctx = NULL;
     const char *str;
@@ -252,15 +266,48 @@
     assert_null(lys_parse_mem(ctx, str, LYS_IN_YANG));
     logbuf_assert("Invalid name \"x\" of typedef - scoped type collide with a top-level type.");
 
+    *state = NULL;
     ly_ctx_destroy(ctx, NULL);
 }
 
+static void
+test_parse_nodeid(void **state)
+{
+    (void) state; /* unused */
+    const char *str;
+    const char *prefix, *name;
+    size_t prefix_len, name_len;
+
+    str = "123";
+    assert_int_equal(LY_EINVAL, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+
+    str = "a12_-.!";
+    assert_int_equal(LY_SUCCESS, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+    assert_null(prefix);
+    assert_int_equal(0, prefix_len);
+    assert_non_null(name);
+    assert_int_equal(6, name_len);
+    assert_int_equal(0, strncmp("a12_-.", name, name_len));
+    assert_string_equal("!", str);
+
+    str = "a12_-.:_b2 xxx";
+    assert_int_equal(LY_SUCCESS, lys_parse_nodeid(&str, &prefix, &prefix_len, &name, &name_len));
+    assert_non_null(prefix);
+    assert_int_equal(6, prefix_len);
+    assert_int_equal(0, strncmp("a12_-.", prefix, prefix_len));
+    assert_non_null(name);
+    assert_int_equal(3, name_len);
+    assert_int_equal(0, strncmp("_b2", name, name_len));
+    assert_string_equal(" xxx", str);
+}
+
 int main(void)
 {
     const struct CMUnitTest tests[] = {
-        cmocka_unit_test_setup(test_date, logger_setup),
+        cmocka_unit_test_setup_teardown(test_date, logger_setup, logger_teardown),
         cmocka_unit_test_setup(test_revisions, logger_setup),
-        cmocka_unit_test_setup(test_typedef, logger_setup),
+        cmocka_unit_test_setup_teardown(test_typedef, logger_setup, logger_teardown),
+        cmocka_unit_test_setup(test_parse_nodeid, logger_setup),
     };
 
     return cmocka_run_group_tests(tests, NULL, NULL);