schema compile CHANGE check if-features applicable to leafref and its target
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 4ac55c1..ad66bfb 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -1876,6 +1876,62 @@
 }
 
 /**
+ * @brief Check the features used in if-feature statements applicable to the leafref and its target.
+ *
+ * The set of features used for target must be a subset of features used for the leafref.
+ * This is not a perfect, we should compare the truth tables but it could require too much resources
+ * and RFC 7950 does not require it explicitely, so we simplify that.
+ *
+ * @param[in] refnode The leafref node.
+ * @param[in] target Tha target node of the leafref.
+ * @return LY_SUCCESS or LY_EVALID;
+ */
+static LY_ERR
+lys_compile_leafref_features_validate(const struct lysc_node *refnode, const struct lysc_node *target)
+{
+    LY_ERR ret = LY_EVALID;
+    const struct lysc_node *iter;
+    struct lysc_iffeature **iff;
+    unsigned int u, v, count;
+    struct ly_set features = {0};
+
+    for (iter = refnode; iter; iter = iter->parent) {
+        iff = lysc_node_iff(iter);
+        if (iff && *iff) {
+            LY_ARRAY_FOR(*iff, u) {
+                LY_ARRAY_FOR((*iff)[u].features, v) {
+                    LY_CHECK_GOTO(ly_set_add(&features, (*iff)[u].features[v], 0) == -1, cleanup);
+                }
+            }
+        }
+    }
+
+    /* we should have, in features set, a superset of features applicable to the target node.
+     * So when adding features applicable to the target into the features set, we should not be
+     * able to actually add any new feature, otherwise it is not a subset of features applicable
+     * to the leafref itself. */
+    count = features.count;
+    for (iter = target; iter; iter = iter->parent) {
+        iff = lysc_node_iff(iter);
+        if (iff && *iff) {
+            LY_ARRAY_FOR(*iff, u) {
+                LY_ARRAY_FOR((*iff)[u].features, v) {
+                    if ((unsigned int)ly_set_add(&features, (*iff)[u].features[v], 0) >= count) {
+                        /* new feature was added (or LY_EMEM) */
+                        goto cleanup;
+                    }
+                }
+            }
+        }
+    }
+    ret = LY_SUCCESS;
+
+cleanup:
+    ly_set_erase(&features, NULL);
+    return ret;
+}
+
+/**
  * @brief Validate the leafref path.
  * @param[in] ctx Compile context
  * @param[in] startnode Path context node (where the leafref path begins/is placed).
@@ -1979,6 +2035,14 @@
         }
     }
 
+    /* check if leafref and its target are under common if-features */
+    if (lys_compile_leafref_features_validate(startnode, node)) {
+        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
+               "Invalid leafref path \"%s\" - set of features applicable to the leafref target is not a subset of features applicable to the leafref itself.",
+               leafref->path);
+        return LY_EVALID;
+    }
+
     return LY_SUCCESS;
 }
 
@@ -2500,7 +2564,6 @@
 
     COMPILE_MEMBER_GOTO(ctx, leaf_p->when, leaf->when, options, lys_compile_when, ret, done);
     COMPILE_ARRAY_GOTO(ctx, leaf_p->iffeatures, leaf->iffeatures, options, u, lys_compile_iffeature, ret, done);
-
     COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, options, u, lys_compile_must, ret, done);
     ret = lys_compile_type(ctx, leaf_p, options, &leaf->type);
     LY_CHECK_GOTO(ret, done);
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index 1e52dd8..24ad2db 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -460,6 +460,22 @@
 }
 
 static void
+lysc_when_free(struct ly_ctx *ctx, struct lysc_when *w)
+{
+    lyxp_expr_free(ctx, w->cond);
+    FREE_ARRAY(ctx, w->exts, lysc_ext_instance_free);
+}
+
+static void
+lysc_must_free(struct ly_ctx *ctx, struct lysc_must *must)
+{
+    lyxp_expr_free(ctx, must->cond);
+    FREE_STRING(ctx, must->emsg);
+    FREE_STRING(ctx, must->eapptag);
+    FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
+}
+
+static void
 lysc_import_free(struct ly_ctx *ctx, struct lysc_import *import)
 {
     /* imported module is freed directly from the context's list */
@@ -581,17 +597,27 @@
 {
     struct lysc_node *child, *child_next;
 
+    FREE_MEMBER(ctx, node->when, lysc_when_free);
+    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
     LY_LIST_FOR_SAFE(node->child, child_next, child) {
         lysc_node_free(ctx, child);
     }
+    FREE_ARRAY(ctx, node->musts, lysc_must_free);
+
+    /* TODO actions, notifs */
 }
 
 static void
 lysc_node_leaf_free(struct ly_ctx *ctx, struct lysc_node_leaf *node)
 {
+    FREE_MEMBER(ctx, node->when, lysc_when_free);
+    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
+    FREE_ARRAY(ctx, node->musts, lysc_must_free);
     if (node->type) {
         lysc_type_free(ctx, node->type);
     }
+    FREE_STRING(ctx, node->units);
+    FREE_STRING(ctx, node->dflt);
 }
 
 void
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 5484999..b815bbc 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -968,10 +968,6 @@
 /* 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;
 */
diff --git a/tests/src/test_tree_schema_compile.c b/tests/src/test_tree_schema_compile.c
index ebf36eb..9323bd9 100644
--- a/tests/src/test_tree_schema_compile.c
+++ b/tests/src/test_tree_schema_compile.c
@@ -1350,6 +1350,20 @@
     assert_ptr_not_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
     assert_int_equal(1, ((struct lysc_type_leafref* )type)->require_instance);
 
+    /* conditional leafrefs */
+    assert_non_null(mod = lys_parse_mem(ctx, "module d {yang-version 1.1;namespace urn:d;prefix d;feature f1; feature f2;"
+                                        "leaf ref1 {if-feature 'f1 and f2';type leafref {path /target;}}"
+                                        "leaf target {if-feature f1; type boolean;}}", 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(1, type->refcount);
+    assert_int_equal(LY_TYPE_LEAFREF, type->basetype);
+    assert_string_equal("/target", ((struct lysc_type_leafref* )type)->path);
+    assert_ptr_equal(mod, ((struct lysc_type_leafref*)type)->path_context);
+    assert_non_null(((struct lysc_type_leafref*)type)->realtype);
+    assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref*)type)->realtype->basetype);
+
     /* TODO target in list with predicates */
 
 
@@ -1391,6 +1405,12 @@
     assert_int_equal(LY_EVALID, lys_compile(mod, 0));
     logbuf_assert("Missing path substatement for leafref type mytype.");
 
+    assert_non_null(mod = lys_parse_mem(ctx, "module jj {namespace urn:jj;prefix jj;feature f;"
+                                        "leaf ref {type leafref {path /target;}}leaf target {if-feature f;type string;}}", LYS_IN_YANG));
+    assert_int_equal(LY_EVALID, lys_compile(mod, 0));
+    logbuf_assert("Invalid leafref path \"/target\" - set of features applicable to the leafref target is not a subset of features "
+                  "applicable to the leafref itself.");
+
     /* circular chain */
     assert_non_null(mod = lys_parse_mem(ctx, "module aaa {namespace urn:aaa;prefix aaa;"
                                         "leaf ref1 {type leafref {path /ref2;}}"