schema compile BUGFIX evaluate if-features on uses

Fixes #1858
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index ebb8322..02a1e51 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -3710,6 +3710,78 @@
 }
 
 /**
+ * @brief Compile uses grouping children.
+ *
+ * @param[in] ctx Compile context.
+ * @param[in] uses_p Parsed uses.
+ * @param[in] uses_flags Special uses flags to use.
+ * @param[in] child First grouping child to compile.
+ * @param[in] grp_mod Grouping parsed module.
+ * @param[in] parent Uses compiled parent, may be NULL if top-level.
+ * @param[in,out] child_set Set of all compiled child nodes.
+ * @param[in] child_unres_disabled Whether the children are to be put into unres disabled set or not.
+ * @return LY_SUCCESS on success.
+ * @return LY_EVALID on failure.
+ */
+static LY_ERR
+lys_compile_uses_children(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, uint16_t uses_flags, struct lysp_node *child,
+        struct lysp_module *grp_mod, struct lysc_node *parent, struct ly_set *child_set, ly_bool child_unres_disabled)
+{
+    LY_ERR rc = LY_SUCCESS;
+    struct lysp_module *mod_old = ctx->pmod;
+    uint32_t child_i, opt_prev = ctx->compile_opts;
+    ly_bool enabled;
+    struct lysp_node *pnode;
+    struct lysc_node *node;
+    struct lysc_when *when_shared = NULL;
+
+    assert(child_set);
+
+    child_i = 0;
+    LY_LIST_FOR(child, pnode) {
+        /* compile the nodes with their parsed (grouping) module */
+        ctx->pmod = grp_mod;
+        LY_CHECK_GOTO(rc = lys_compile_node(ctx, pnode, parent, uses_flags, child_set), cleanup);
+
+        /* eval if-features again for the rest of this node processing */
+        LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), cleanup);
+        if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+            ctx->compile_opts |= LYS_COMPILE_DISABLED;
+        }
+
+        /* restore the parsed module */
+        ctx->pmod = mod_old;
+
+        /* since the uses node is not present in the compiled tree, we need to pass some of its
+         * statements to all its children */
+        while (child_i < child_set->count) {
+            node = child_set->snodes[child_i];
+
+            if (uses_p->when) {
+                /* pass uses when to all the children */
+                rc = lys_compile_when(ctx, uses_p->when, uses_flags, parent, lysc_data_node(parent), node, &when_shared);
+                LY_CHECK_GOTO(rc, cleanup);
+            }
+
+            if (child_unres_disabled) {
+                /* child is disabled by the uses if-features */
+                ly_set_add(&ctx->unres->disabled, node, 1, NULL);
+            }
+
+            /* child processed */
+            ++child_i;
+        }
+
+        /* next iter */
+        ctx->compile_opts = opt_prev;
+    }
+
+cleanup:
+    ctx->compile_opts = opt_prev;
+    return rc;
+}
+
+/**
  * @brief Special bits combination marking the uses_status value and propagated by ::lys_compile_uses() function.
  */
 #define LYS_STATUS_USES LYS_CONFIG_MASK
@@ -3723,19 +3795,18 @@
  * @param[in] parent Compiled parent node where the content of the referenced grouping 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.
+ * @param[in] child_set Optional set of all the compiled children.
  * @return LY_ERR value - LY_SUCCESS or LY_EVALID.
  */
 static LY_ERR
 lys_compile_uses(struct lysc_ctx *ctx, struct lysp_node_uses *uses_p, struct lysc_node *parent, struct ly_set *child_set)
 {
-    struct lysp_node *pnode;
-    struct lysc_node *child;
+    LY_ERR rc = LY_SUCCESS;
+    ly_bool enabled, child_unres_disabled = 0;
+    uint32_t i, grp_stack_count, opt_prev = ctx->compile_opts;
     struct lysp_node_grp *grp = NULL;
-    uint32_t i, grp_stack_count;
     uint16_t uses_flags;
-    struct lysp_module *grp_mod, *mod_old = ctx->pmod;
-    LY_ERR ret = LY_SUCCESS;
-    struct lysc_when *when_shared = NULL;
+    struct lysp_module *grp_mod;
     struct ly_set uses_child_set = {0};
 
     /* find the referenced grouping */
@@ -3751,81 +3822,57 @@
         return LY_EVALID;
     }
 
+    /* nodetype checks */
+    if (grp->actions && (parent && !lysc_node_actions_p(parent))) {
+        LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid child %s \"%s\" of uses parent %s \"%s\" node.",
+                grp->actions->name, lys_nodetype2str(grp->actions->nodetype),
+                parent->name, lys_nodetype2str(parent->nodetype));
+        rc = LY_EVALID;
+        goto cleanup;
+    }
+    if (grp->notifs && (parent && !lysc_node_notifs_p(parent))) {
+        LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid child %s \"%s\" of uses parent %s \"%s\" node.",
+                grp->notifs->name, lys_nodetype2str(grp->notifs->nodetype),
+                parent->name, lys_nodetype2str(parent->nodetype));
+        rc = LY_EVALID;
+        goto cleanup;
+    }
+
     /* check status */
-    ret = lysc_check_status(ctx, uses_p->flags, ctx->pmod, uses_p->name, grp->flags, grp_mod, grp->name);
-    LY_CHECK_GOTO(ret, cleanup);
+    rc = lysc_check_status(ctx, uses_p->flags, ctx->pmod, uses_p->name, grp->flags, grp_mod, grp->name);
+    LY_CHECK_GOTO(rc, cleanup);
 
     /* compile any augments and refines so they can be applied during the grouping nodes compilation */
-    ret = lys_precompile_uses_augments_refines(ctx, uses_p, parent);
-    LY_CHECK_GOTO(ret, cleanup);
+    rc = lys_precompile_uses_augments_refines(ctx, uses_p, parent);
+    LY_CHECK_GOTO(rc, cleanup);
 
     /* compile special uses status flags */
     uses_flags = uses_p->flags;
-    ret = lys_compile_status(ctx, &uses_flags, "<uses>", parent ? parent->flags : 0, parent ? parent->name : NULL, 0);
-    LY_CHECK_GOTO(ret, cleanup);
+    rc = lys_compile_status(ctx, &uses_flags, "<uses>", parent ? parent->flags : 0, parent ? parent->name : NULL, 0);
+    LY_CHECK_GOTO(rc, cleanup);
     uses_flags |= LYS_STATUS_USES;
 
-    /* switch context's parsed module being processed */
-    ctx->pmod = grp_mod;
-
-    /* compile data nodes */
-    LY_LIST_FOR(grp->child, pnode) {
-        ret = lys_compile_node(ctx, pnode, parent, uses_flags, &uses_child_set);
-        LY_CHECK_GOTO(ret, cleanup);
+    /* uses if-features */
+    LY_CHECK_GOTO(rc = lys_eval_iffeatures(ctx->ctx, uses_p->iffeatures, &enabled), cleanup);
+    if (!enabled && !(ctx->compile_opts & (LYS_COMPILE_NO_DISABLED | LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
+        ctx->compile_opts |= LYS_COMPILE_DISABLED;
+        child_unres_disabled = 1;
     }
 
-    if (child_set) {
-        /* add these children to our compiled child_set as well since uses is a schema-only node */
-        LY_CHECK_GOTO(ret = ly_set_merge(child_set, &uses_child_set, 1, NULL), cleanup);
-    }
+    /* uses grouping children */
+    rc = lys_compile_uses_children(ctx, uses_p, uses_flags, grp->child, grp_mod, parent,
+            child_set ? child_set : &uses_child_set, child_unres_disabled);
+    LY_CHECK_GOTO(rc, cleanup);
 
-    /* compile actions */
-    if (grp->actions) {
-        struct lysc_node_action **actions;
-        actions = parent ? lysc_node_actions_p(parent) : &ctx->cur_mod->compiled->rpcs;
-        if (!actions) {
-            LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid child %s \"%s\" of uses parent %s \"%s\" node.",
-                    grp->actions->name, lys_nodetype2str(grp->actions->nodetype),
-                    parent->name, lys_nodetype2str(parent->nodetype));
-            ret = LY_EVALID;
-            goto cleanup;
-        }
-        LY_LIST_FOR((struct lysp_node *)grp->actions, pnode) {
-            ret = lys_compile_node(ctx, pnode, parent, uses_flags, &uses_child_set);
-            LY_CHECK_GOTO(ret, cleanup);
-        }
-    }
+    /* uses grouping RPCs/actions */
+    rc = lys_compile_uses_children(ctx, uses_p, uses_flags, (struct lysp_node *)grp->actions, grp_mod, parent,
+            child_set ? child_set : &uses_child_set, child_unres_disabled);
+    LY_CHECK_GOTO(rc, cleanup);
 
-    /* compile notifications */
-    if (grp->notifs) {
-        struct lysc_node_notif **notifs;
-        notifs = parent ? lysc_node_notifs_p(parent) : &ctx->cur_mod->compiled->notifs;
-        if (!notifs) {
-            LOGVAL(ctx->ctx, LYVE_REFERENCE, "Invalid child %s \"%s\" of uses parent %s \"%s\" node.",
-                    grp->notifs->name, lys_nodetype2str(grp->notifs->nodetype),
-                    parent->name, lys_nodetype2str(parent->nodetype));
-            ret = LY_EVALID;
-            goto cleanup;
-        }
-
-        LY_LIST_FOR((struct lysp_node *)grp->notifs, pnode) {
-            ret = lys_compile_node(ctx, pnode, parent, uses_flags, &uses_child_set);
-            LY_CHECK_GOTO(ret, cleanup);
-        }
-    }
-
-    /* restore the previous context parsed module where uses is defined */
-    ctx->pmod = mod_old;
-
-    if (uses_p->when) {
-        /* pass uses's when to all the data children */
-        for (i = 0; i < uses_child_set.count; ++i) {
-            child = uses_child_set.snodes[i];
-
-            ret = lys_compile_when(ctx, uses_p->when, uses_flags, parent, lysc_data_node(parent), child, &when_shared);
-            LY_CHECK_GOTO(ret, cleanup);
-        }
-    }
+    /* uses grouping notifications */
+    rc = lys_compile_uses_children(ctx, uses_p, uses_flags, (struct lysp_node *)grp->notifs, grp_mod, parent,
+            child_set ? child_set : &uses_child_set, child_unres_disabled);
+    LY_CHECK_GOTO(rc, cleanup);
 
     /* check that all augments were applied */
     for (i = 0; i < ctx->uses_augs.count; ++i) {
@@ -3836,33 +3883,33 @@
 
         LOGVAL(ctx->ctx, LYVE_REFERENCE, "Augment target node \"%s\" in grouping \"%s\" was not found.",
                 ((struct lysc_augment *)ctx->uses_augs.objs[i])->nodeid->expr, grp->name);
-        ret = LY_ENOTFOUND;
+        rc = LY_ENOTFOUND;
     }
-    LY_CHECK_GOTO(ret, cleanup);
+    LY_CHECK_GOTO(rc, cleanup);
 
     /* check that all refines were applied */
     for (i = 0; i < ctx->uses_rfns.count; ++i) {
         if (((struct lysc_refine *)ctx->uses_rfns.objs[i])->uses_p != uses_p) {
-            /* refine of some paretn uses, irrelevant now */
+            /* refine of some parent uses, irrelevant now */
             continue;
         }
 
         LOGVAL(ctx->ctx, LYVE_REFERENCE, "Refine(s) target node \"%s\" in grouping \"%s\" was not found.",
                 ((struct lysc_refine *)ctx->uses_rfns.objs[i])->nodeid->expr, grp->name);
-        ret = LY_ENOTFOUND;
+        rc = LY_ENOTFOUND;
     }
-    LY_CHECK_GOTO(ret, cleanup);
+    LY_CHECK_GOTO(rc, cleanup);
 
 cleanup:
-    /* restore previous context's parsed module being processed */
-    ctx->pmod = mod_old;
+    /* restore previous context */
+    ctx->compile_opts = opt_prev;
 
     /* remove the grouping from the stack for circular groupings dependency check */
     ly_set_rm_index(&ctx->groupings, ctx->groupings.count - 1, NULL);
     assert(ctx->groupings.count == grp_stack_count);
 
     ly_set_erase(&uses_child_set, NULL);
-    return ret;
+    return rc;
 }
 
 static int
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index d776e82..074a13a 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -2498,6 +2498,14 @@
     assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module k {namespace urn:k;prefix k;import j {prefix j;}"
             "container a {uses j:grp;}}", LYS_IN_YANG, NULL));
 
+    /* if-features */
+    assert_int_equal(LY_SUCCESS, lys_parse_mem(UTEST_LYCTX, "module l {namespace urn:l;prefix l;"
+            "feature f;"
+            "grouping grp {container g; leaf l{type string;}}"
+            "uses grp {if-feature f;}}",
+            LYS_IN_YANG, &mod));
+    assert_null(mod->compiled->data);
+
     /* invalid */
     assert_int_equal(LY_EVALID, lys_parse_mem(UTEST_LYCTX, "module aa {namespace urn:aa;prefix aa;uses missinggrp;}", LYS_IN_YANG, &mod));
     CHECK_LOG_CTX("Grouping \"missinggrp\" referenced by a uses statement not found.", "/aa:{uses='missinggrp'}");