schema tree REFACTOR evaluate features during compilation

So that the compiled schema tree reflects the state
of all the features.
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 89702d1..5e7491b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -118,6 +118,7 @@
     src/schema_compile.c
     src/schema_compile_node.c
     src/schema_compile_amend.c
+    src/schema_features.c
     src/tree_schema.c
     src/tree_schema_free.c
     src/tree_schema_helpers.c
diff --git a/src/common.h b/src/common.h
index 7e3f23b..d5209c5 100644
--- a/src/common.h
+++ b/src/common.h
@@ -206,7 +206,6 @@
 #define LY_VCODE_NOUNIQ         LYVE_DATA, "Unique data leaf(s) \"%s\" not satisfied in \"%s\" and \"%s\"."
 #define LY_VCODE_DUP            LYVE_DATA, "Duplicate instance of \"%s\"."
 #define LY_VCODE_DUPCASE        LYVE_DATA, "Data for both cases \"%s\" and \"%s\" exist."
-#define LY_VCODE_NOIFF          LYVE_DATA, "Data are disabled by \"%s\" schema node if-feature."
 #define LY_VCODE_INNODE         LYVE_DATA, "Invalid %s data node \"%s\" found."
 #define LY_VCODE_NOKEY          LYVE_DATA, "List instance is missing its key \"%s\"."
 
@@ -614,6 +613,19 @@
         --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1))
 
 /**
+ * @brief Decrement the items counter in a ([sized array](@ref sizedarrays)) and free the whole array
+ * in case it was decremented to 0.
+ *
+ * @param[in] ARRAY Pointer to the array to affect.
+ */
+#define LY_ARRAY_DECREMENT_FREE(ARRAY) \
+        --(*((LY_ARRAY_COUNT_TYPE*)(ARRAY) - 1)); \
+        if (!LY_ARRAY_COUNT(ARRAY)) { \
+            LY_ARRAY_FREE(ARRAY); \
+            (ARRAY) = NULL; \
+        }
+
+/**
  * @brief Free the space allocated for the ([sized array](@ref sizedarrays)).
  *
  * The items inside the array are not freed.
diff --git a/src/context.c b/src/context.c
index 9457436..48bd11b 100644
--- a/src/context.c
+++ b/src/context.c
@@ -178,13 +178,13 @@
 }
 
 API const struct lys_module *
-ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision)
+ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision, const char **features)
 {
     struct lys_module *result = NULL;
 
     LY_CHECK_ARG_RET(ctx, ctx, name, NULL);
 
-    LY_CHECK_RET(lysp_load_module(ctx, name, revision, 1, 0, &result), NULL);
+    LY_CHECK_RET(lysp_load_module(ctx, name, revision, 1, 0, features, &result), NULL);
     return result;
 }
 
@@ -253,7 +253,7 @@
     for (i = 0; i < ((options & LY_CTX_NO_YANGLIBRARY) ? (LY_INTERNAL_MODS_COUNT - 2) : LY_INTERNAL_MODS_COUNT); i++) {
         ly_in_memory(in, internal_modules[i].data);
         LY_CHECK_GOTO(rc = lys_create_module(ctx, in, internal_modules[i].format, internal_modules[i].implemented,
-                NULL, NULL, &module), error);
+                NULL, NULL, NULL, &module), error);
     }
 
     ly_in_free(in, 0);
@@ -612,21 +612,32 @@
 }
 
 static LY_ERR
-ylib_feature(struct lyd_node *parent, const struct lys_module *cur_mod)
+ylib_feature(struct lyd_node *parent, const struct lysp_module *pmod)
 {
-    LY_ARRAY_COUNT_TYPE i;
+    LY_ARRAY_COUNT_TYPE u;
+    struct lysp_feature *f;
 
-    if (!cur_mod->implemented) {
+    if (!pmod->mod->implemented) {
         /* no features can be enabled */
         return LY_SUCCESS;
     }
 
-    LY_ARRAY_FOR(cur_mod->features, i) {
-        if (!(cur_mod->features[i].flags & LYS_FENABLED)) {
+    LY_ARRAY_FOR(pmod->features, struct lysp_feature, f) {
+        if (!(f->flags & LYS_FENABLED)) {
             continue;
         }
 
-        LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", cur_mod->features[i].name, NULL));
+        LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, NULL));
+    }
+
+    LY_ARRAY_FOR(pmod->includes, u) {
+        LY_ARRAY_FOR(pmod->includes[u].submodule->features, struct lysp_feature, f) {
+            if (!(f->flags & LYS_FENABLED)) {
+                continue;
+            }
+
+            LY_CHECK_RET(lyd_new_term(parent, NULL, "feature", f->name, NULL));
+        }
     }
 
     return LY_SUCCESS;
@@ -658,7 +669,7 @@
 }
 
 static LY_ERR
-ylib_submodules(struct lyd_node *parent, const struct lys_module *cur_mod, ly_bool bis)
+ylib_submodules(struct lyd_node *parent, const struct lysp_module *pmod, ly_bool bis)
 {
     LY_ERR ret;
     LY_ARRAY_COUNT_TYPE i;
@@ -667,8 +678,8 @@
     int r;
     char *str;
 
-    LY_ARRAY_FOR(cur_mod->parsed->includes, i) {
-        submod = cur_mod->parsed->includes[i].submodule;
+    LY_ARRAY_FOR(pmod->includes, i) {
+        submod = pmod->includes[i].submodule;
 
         if (bis) {
             LY_CHECK_RET(lyd_new_list(parent, NULL, "submodule", &cont, submod->name));
@@ -683,7 +694,7 @@
 
         if (submod->filepath) {
             r = asprintf(&str, "file://%s", submod->filepath);
-            LY_CHECK_ERR_RET(r == -1, LOGMEM(cur_mod->ctx), LY_EMEM);
+            LY_CHECK_ERR_RET(r == -1, LOGMEM(pmod->mod->ctx), LY_EMEM);
 
             ret = lyd_new_term(cont, NULL, bis ? "location" : "schema", str, NULL);
             free(str);
@@ -734,6 +745,10 @@
 
     for (i = 0; i < ctx->list.count; ++i) {
         mod = ctx->list.objs[i];
+        if (!mod->parsed) {
+            LOGERR(ctx, LY_ENOTFOUND, "Parsed module \"%s\" missing in the context.", mod->name);
+            goto error;
+        }
 
         /*
          * deprecated legacy
@@ -755,7 +770,7 @@
         LY_CHECK_GOTO(ret = lyd_new_term(cont, NULL, "namespace", mod->ns, NULL), error);
 
         /* feature leaf-list */
-        LY_CHECK_GOTO(ret = ylib_feature(cont, mod), error);
+        LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error);
 
         /* deviation list */
         LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 0), error);
@@ -765,7 +780,7 @@
                 NULL), error);
 
         /* submodule list */
-        LY_CHECK_GOTO(ret = ylib_submodules(cont, mod, 0), error);
+        LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 0), error);
 
         /*
          * current revision
@@ -797,10 +812,10 @@
             }
 
             /* submodule list */
-            LY_CHECK_GOTO(ret = ylib_submodules(cont, mod, 1), error);
+            LY_CHECK_GOTO(ret = ylib_submodules(cont, mod->parsed, 1), error);
 
             /* feature list */
-            LY_CHECK_GOTO(ret = ylib_feature(cont, mod), error);
+            LY_CHECK_GOTO(ret = ylib_feature(cont, mod->parsed), error);
 
             /* deviation */
             LY_CHECK_GOTO(ret = ylib_deviation(cont, mod, 1), error);
diff --git a/src/context.h b/src/context.h
index 758b419..ac82a01 100644
--- a/src/context.h
+++ b/src/context.h
@@ -469,9 +469,12 @@
  * @param[in] ctx Context to add to.
  * @param[in] name Name of the module to load.
  * @param[in] revision Optional revision date of the module. If not specified, the latest revision is loaded.
+ * @param[in] features Optional array of features ended with NULL to be enabled if the module is being implemented.
+ * NULL for all features disabled and '*' for all enabled.
  * @return Pointer to the data model structure, NULL if not found or some error occurred.
  */
-const struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision);
+const struct lys_module *ly_ctx_load_module(struct ly_ctx *ctx, const char *name, const char *revision,
+        const char **features);
 
 /**
  * @brief Get current ID of the modules set. The value is available also
diff --git a/src/parser_json.c b/src/parser_json.c
index 02428af..9fa5334 100644
--- a/src/parser_json.c
+++ b/src/parser_json.c
@@ -188,9 +188,7 @@
         size_t name_len, const struct lyd_node_inner *parent, const struct lysc_node **snode_p)
 {
     struct lys_module *mod = NULL;
-
-    /* leave if-feature check for validation */
-    uint32_t getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+    uint32_t getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
 
     /* init return value */
     *snode_p = NULL;
@@ -362,7 +360,7 @@
 
     /* get all keys into a set (keys do not have if-features or anything) */
     snode = NULL;
-    while ((snode = lys_getnext(snode, list, NULL, LYS_GETNEXT_NOSTATECHECK)) && (snode->flags & LYS_KEY)) {
+    while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
         ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
         LY_CHECK_GOTO(ret, cleanup);
     }
diff --git a/src/parser_lyb.c b/src/parser_lyb.c
index 2f4a0c7..1076bdc 100644
--- a/src/parser_lyb.c
+++ b/src/parser_lyb.c
@@ -588,8 +588,7 @@
     uint32_t getnext_opts;
 
     *snode = NULL;
-    /* leave if-feature check for validation */
-    getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lybctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+    getnext_opts = lybctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
 
     /* read the first hash */
     lyb_read(&hash[0], sizeof *hash, lybctx->lybctx);
diff --git a/src/parser_schema.h b/src/parser_schema.h
index 57a3928..afef7fe 100644
--- a/src/parser_schema.h
+++ b/src/parser_schema.h
@@ -98,10 +98,12 @@
  * @param[in] ctx libyang context where to process the data model.
  * @param[in] in The input handle to provide the dumped data model in the specified format.
  * @param[in] format Format of the schema to parse.
+ * @param[in] features Array of features to enable ended with NULL. If NULL, no features are enabled.
  * @param[out] module Optional parsed module.
  * @return LY_ERR value.
  */
-LY_ERR lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const struct lys_module **module);
+LY_ERR lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char **features,
+        const struct lys_module **module);
 
 /**
  * @brief Load a schema into the specified context.
diff --git a/src/parser_xml.c b/src/parser_xml.c
index 8647e69..f3abaa4 100644
--- a/src/parser_xml.c
+++ b/src/parser_xml.c
@@ -215,7 +215,7 @@
 
     /* get all keys into a set (keys do not have if-features or anything) */
     snode = NULL;
-    while ((snode = lys_getnext(snode, list, NULL, LYS_GETNEXT_NOSTATECHECK)) && (snode->flags & LYS_KEY)) {
+    while ((snode = lys_getnext(snode, list, NULL, 0)) && (snode->flags & LYS_KEY)) {
         ret = ly_set_add(&key_set, (void *)snode, 1, NULL);
         LY_CHECK_GOTO(ret, cleanup);
     }
@@ -387,8 +387,7 @@
 
     xmlctx = lydctx->xmlctx;
     ctx = xmlctx->ctx;
-    /* leave if-feature check for validation */
-    getnext_opts = LYS_GETNEXT_NOSTATECHECK | (lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0);
+    getnext_opts = lydctx->int_opts & LYD_INTOPT_REPLY ? LYS_GETNEXT_OUTPUT : 0;
 
     assert(xmlctx->status == LYXML_ELEMENT);
 
diff --git a/src/path.c b/src/path.c
index f590fbc..7086a97 100644
--- a/src/path.c
+++ b/src/path.c
@@ -407,7 +407,7 @@
                 LOGVAL_P(ctx, cur_node, LYVE_XPATH, "Not implemented module \"%s\" in path.", (*mod)->name);
                 return LY_EVALID;
             }
-            LY_CHECK_RET(lys_set_implemented((struct lys_module *)*mod));
+            LY_CHECK_RET(lys_set_implemented((struct lys_module *)*mod, NULL));
         }
     } else {
         switch (format) {
@@ -479,7 +479,7 @@
             /* NameTest, find the key */
             LY_CHECK_RET(ly_path_compile_prefix(ctx, cur_node, cur_mod, ctx_node, expr, *tok_idx, LY_PATH_LREF_FALSE,
                     format, prefix_data, &mod, &name, &name_len));
-            key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+            key = lys_find_child(ctx_node, mod, name, name_len, 0, 0);
             if (!key) {
                 LOGVAL_P(ctx, cur_node, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
                 return LY_ENOTFOUND;
@@ -629,7 +629,7 @@
         /* NameTest, find the key */
         LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node, cur_node->module, ctx_node, expr, *tok_idx,
                 LY_PATH_LREF_TRUE, format, prefix_data, &mod, &name, &name_len));
-        key = lys_find_child(ctx_node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+        key = lys_find_child(ctx_node, mod, name, name_len, 0, 0);
         if (!key) {
             LOGVAL_P(cur_node->module->ctx, cur_node, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
             return LY_EVALID;
@@ -688,7 +688,7 @@
             assert(expr->tokens[*tok_idx] == LYXP_TOKEN_NAMETEST);
             LY_CHECK_RET(ly_path_compile_prefix(cur_node->module->ctx, cur_node, cur_node->module, node, expr, *tok_idx,
                     LY_PATH_LREF_TRUE, format, prefix_data, &mod, &name, &name_len));
-            node2 = lys_find_child(node, mod, name, name_len, 0, LYS_GETNEXT_NOSTATECHECK);
+            node2 = lys_find_child(node, mod, name, name_len, 0, 0);
             if (!node2) {
                 LOGVAL_P(cur_node->module->ctx, cur_node, LYVE_XPATH, "Not found node \"%.*s\" in path.", name_len, name);
                 return LY_EVALID;
@@ -747,9 +747,9 @@
     *path = NULL;
 
     if (oper == LY_PATH_OPER_OUTPUT) {
-        getnext_opts = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_OUTPUT;
+        getnext_opts = LYS_GETNEXT_OUTPUT;
     } else {
-        getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+        getnext_opts = 0;
     }
 
     if (expr->tokens[tok_idx] == LYXP_TOKEN_OPER_PATH) {
diff --git a/src/plugins_exts.c b/src/plugins_exts.c
index 9158e09..f5e3ce4 100644
--- a/src/plugins_exts.c
+++ b/src/plugins_exts.c
@@ -67,8 +67,6 @@
         return "bit";
     case LYEXT_PAR_TYPE_ENUM:
         return "enum";
-    case LYEXT_PAR_FEATURE:
-        return "feature";
     case LYEXT_PAR_MUST:
         return "must";
     case LYEXT_PAR_PATTERN:
diff --git a/src/plugins_exts.h b/src/plugins_exts.h
index 0569468..a10a3dd 100644
--- a/src/plugins_exts.h
+++ b/src/plugins_exts.h
@@ -84,6 +84,7 @@
 /**
  * @brief Compile substatements of an extension instance.
  * TODO
+ * @return LY_ENOT if the extension is disabled and should be ignored.
  */
 LY_ERR lys_compile_extension_instance(struct lysc_ctx *ctx, const struct lysp_ext_instance *ext, struct lysc_ext_substmt *substmts);
 
@@ -125,6 +126,7 @@
  * for later use (data validation or use of external tool).
  * @return LY_SUCCESS in case of success.
  * @return LY_EVALID in case of non-conforming parsed data.
+ * @return LY_ENOT in case the extension instance is not supported and should be removed.
  */
 typedef LY_ERR (*lyext_clb_compile)(struct lysc_ctx *cctx, const struct lysp_ext_instance *p_ext, struct lysc_ext_instance *c_ext);
 
diff --git a/src/plugins_types.c b/src/plugins_types.c
index 9927a22..5049f98 100644
--- a/src/plugins_types.c
+++ b/src/plugins_types.c
@@ -1034,7 +1034,7 @@
     size_t buf_size = 0;
     char *buf = NULL;
     size_t index;
-    LY_ARRAY_COUNT_TYPE u, v;
+    LY_ARRAY_COUNT_TYPE u;
     char *errmsg = NULL;
     struct lysc_type_bits *type_bits = (struct lysc_type_bits *)type;
     ly_bool iscanonical = 1;
@@ -1068,15 +1068,6 @@
                 /* we have the match */
                 uint32_t inserted;
 
-                /* check that the bit is not disabled */
-                LY_ARRAY_FOR(type_bits->bits[u].iffeatures, v) {
-                    if (lysc_iffeature_value(&type_bits->bits[u].iffeatures[v]) == LY_ENOT) {
-                        rc = asprintf(&errmsg, "Bit \"%s\" is disabled by its %" LY_PRI_ARRAY_COUNT_TYPE ". if-feature condition.",
-                                type_bits->bits[u].name, v + 1);
-                        goto cleanup;
-                    }
-                }
-
                 if (iscanonical && items->count && (type_bits->bits[u].position < ((struct lysc_type_bitenum_item *)items->objs[items->count - 1])->position)) {
                     iscanonical = 0;
                 }
@@ -1215,7 +1206,7 @@
         uint32_t options, LY_PREFIX_FORMAT UNUSED(format), void *UNUSED(prefix_data), uint32_t hints,
         const struct lysc_node *UNUSED(ctx_node), struct lyd_value *storage, struct ly_err_item **err)
 {
-    LY_ARRAY_COUNT_TYPE u, v;
+    LY_ARRAY_COUNT_TYPE u;
     char *errmsg = NULL;
     struct lysc_type_enum *type_enum = (struct lysc_type_enum *)type;
     int rc = 0;
@@ -1226,16 +1217,7 @@
     /* find the matching enumeration value item */
     LY_ARRAY_FOR(type_enum->enums, u) {
         if (!ly_strncmp(type_enum->enums[u].name, value, value_len)) {
-            /* we have the match */
-
-            /* check that the enumeration value is not disabled */
-            LY_ARRAY_FOR(type_enum->enums[u].iffeatures, v) {
-                if (lysc_iffeature_value(&type_enum->enums[u].iffeatures[v]) == LY_ENOT) {
-                    rc = asprintf(&errmsg, "Enumeration \"%s\" is disabled by its %" LY_PRI_ARRAY_COUNT_TYPE ". if-feature condition.",
-                            type_enum->enums[u].name, v + 1);
-                    goto error;
-                }
-            }
+            /* we have a match */
             goto match;
         }
     }
@@ -1448,7 +1430,7 @@
     } else if (!mod->implemented) {
         /* non-implemented module */
         if (options & LY_TYPE_STORE_IMPLEMENT) {
-            LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod));
+            LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod, NULL));
         } else {
             rc = asprintf(&errmsg, "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
                     (int)value_len, value, mod->name);
diff --git a/src/printer_lyb.c b/src/printer_lyb.c
index a1188bd..860d5a0 100644
--- a/src/printer_lyb.c
+++ b/src/printer_lyb.c
@@ -130,8 +130,7 @@
     mod = sibling->module;
 
     sibling = NULL;
-    /* ignore features so that their state does not affect hashes */
-    while ((sibling = (struct lysc_node *)lys_getnext(sibling, parent, mod->compiled, LYS_GETNEXT_NOSTATECHECK))) {
+    while ((sibling = (struct lysc_node *)lys_getnext(sibling, parent, mod->compiled, 0))) {
         /* find the first non-colliding hash (or specifically non-colliding hash sequence) */
         for (i = 0; i < LYB_HASH_BITS; ++i) {
             /* check that we are not colliding with nodes inserted with a lower collision ID than ours */
diff --git a/src/printer_yang.c b/src/printer_yang.c
index b60b0c4..147f860 100644
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -461,78 +461,6 @@
 }
 
 static void
-yprc_iffeature(struct ypr_ctx *ctx, struct lysc_iffeature *feat, size_t *index_e, size_t *index_f)
-{
-    ly_bool brackets_flag = *index_e ? 1 : 0;
-    uint8_t op;
-
-    op = lysc_iff_getop(feat->expr, *index_e);
-    (*index_e)++;
-
-    switch (op) {
-    case LYS_IFF_F:
-        if (ctx->module == feat->features[*index_f]->module) {
-            ly_print_(ctx->out, "%s", feat->features[*index_f]->name);
-        } else {
-            ly_print_(ctx->out, "%s:%s", feat->features[*index_f]->module->prefix, feat->features[*index_f]->name);
-        }
-        (*index_f)++;
-        break;
-    case LYS_IFF_NOT:
-        ly_print_(ctx->out, "not ");
-        yprc_iffeature(ctx, feat, index_e, index_f);
-        break;
-    case LYS_IFF_AND:
-        if (brackets_flag) {
-            /* AND need brackets only if previous op was not */
-            if ((*index_e < 2) || (lysc_iff_getop(feat->expr, *index_e - 2) != LYS_IFF_NOT)) {
-                brackets_flag = 0;
-            }
-        }
-    /* falls through */
-    case LYS_IFF_OR:
-        if (brackets_flag) {
-            ly_print_(ctx->out, "(");
-        }
-        yprc_iffeature(ctx, feat, index_e, index_f);
-        ly_print_(ctx->out, " %s ", op == LYS_IFF_OR ? "or" : "and");
-        yprc_iffeature(ctx, feat, index_e, index_f);
-        if (brackets_flag) {
-            ly_print_(ctx->out, ")");
-        }
-    }
-}
-
-static void
-yprc_iffeatures(struct ypr_ctx *ctx, struct lysc_iffeature *iff, struct lysc_ext_instance *exts, ly_bool *flag)
-{
-    LY_ARRAY_COUNT_TYPE u, v;
-    ly_bool extflag;
-
-    LY_ARRAY_FOR(iff, u) {
-        size_t index_e = 0, index_f = 0;
-
-        ypr_open(ctx->out, flag);
-        extflag = 0;
-
-        ly_print_(ctx->out, "%*sif-feature \"", INDENT);
-        yprc_iffeature(ctx, iff, &index_e, &index_f);
-        ly_print_(ctx->out, "\"");
-
-        /* extensions */
-        LEVEL++;
-        LY_ARRAY_FOR(exts, v) {
-            if ((exts[v].insubstmt != LYEXT_SUBSTMT_IFFEATURE) || (exts[v].insubstmt_index != u)) {
-                continue;
-            }
-            yprc_extension_instances(ctx, LYEXT_SUBSTMT_IFFEATURE, u, &exts[v], &extflag, 1);
-        }
-        LEVEL--;
-        ypr_close(ctx, extflag);
-    }
-}
-
-static void
 yprp_extension(struct ypr_ctx *ctx, const struct lysp_ext *ext)
 {
     ly_bool flag = 0, flag2 = 0;
@@ -589,22 +517,6 @@
 }
 
 static void
-yprc_feature(struct ypr_ctx *ctx, const struct lysc_feature *feat)
-{
-    ly_bool flag = 0;
-
-    ly_print_(ctx->out, "\n%*sfeature %s", INDENT, feat->name);
-    LEVEL++;
-    yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, feat->exts, &flag, 0);
-    yprc_iffeatures(ctx, feat->iffeatures, feat->exts, &flag);
-    ypr_status(ctx, feat->flags, feat->exts, &flag);
-    ypr_description(ctx, feat->dsc, feat->exts, &flag);
-    ypr_reference(ctx, feat->ref, feat->exts, &flag);
-    LEVEL--;
-    ypr_close(ctx, flag);
-}
-
-static void
 yprp_identity(struct ypr_ctx *ctx, const struct lysp_ident *ident)
 {
     ly_bool flag = 0;
@@ -639,7 +551,6 @@
     LEVEL++;
 
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, ident->exts, &flag, 0);
-    yprc_iffeatures(ctx, ident->iffeatures, ident->exts, &flag);
 
     LY_ARRAY_FOR(ident->derived, u) {
         ypr_open(ctx->out, &flag);
@@ -987,7 +898,6 @@
             ly_print_(ctx->out, "\"");
             LEVEL++;
             yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, item->exts, &inner_flag, 0);
-            yprc_iffeatures(ctx, item->iffeatures, item->exts, &inner_flag);
             if (type->basetype == LY_TYPE_BITS) {
                 ypr_unsigned(ctx, LYEXT_SUBSTMT_POSITION, 0, item->exts, item->position, &inner_flag);
             } else { /* LY_TYPE_ENUM */
@@ -1231,7 +1141,6 @@
 
     LEVEL++;
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, notif->exts, &flag, 0);
-    yprc_iffeatures(ctx, notif->iffeatures, notif->exts, &flag);
 
     LY_ARRAY_FOR(notif->musts, u) {
         yprc_must(ctx, &notif->musts[u], &flag);
@@ -1292,7 +1201,6 @@
 
     LEVEL++;
     yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, action->exts, &flag, 0);
-    yprc_iffeatures(ctx, action->iffeatures, action->exts, &flag);
     ypr_status(ctx, action->flags, action->exts, &flag);
     ypr_description(ctx, action->dsc, action->exts, &flag);
     ypr_reference(ctx, action->ref, action->exts, &flag);
@@ -1327,7 +1235,6 @@
     LY_ARRAY_FOR(node->when, u) {
         yprc_when(ctx, node->when[u], flag);
     }
-    yprc_iffeatures(ctx, node->iffeatures, node->exts, flag);
 }
 
 /* macr oto unify the code */
@@ -2341,10 +2248,6 @@
         yprc_extension_instances(ctx, LYEXT_SUBSTMT_SELF, 0, module->compiled->exts, NULL, 0);
     }
 
-    LY_ARRAY_FOR(module->features, u) {
-        yprc_feature(ctx, &module->features[u]);
-    }
-
     LY_ARRAY_FOR(module->identities, u) {
         yprc_identity(ctx, &module->identities[u]);
     }
diff --git a/src/schema_compile.c b/src/schema_compile.c
index 24bc995..e32c84b 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -37,6 +37,7 @@
 #include "plugins_types.h"
 #include "schema_compile_amend.h"
 #include "schema_compile_node.h"
+#include "schema_features.h"
 #include "set.h"
 #include "tree.h"
 #include "tree_data.h"
@@ -45,16 +46,6 @@
 #include "tree_schema_internal.h"
 #include "xpath.h"
 
-#define COMPILE_CHECK_UNIQUENESS_ARRAY(CTX, ARRAY, MEMBER, EXCL, STMT, IDENT) \
-    if (ARRAY) { \
-        for (LY_ARRAY_COUNT_TYPE u__ = 0; u__ < LY_ARRAY_COUNT(ARRAY); ++u__) { \
-            if (&(ARRAY)[u__] != EXCL && (void*)((ARRAY)[u__].MEMBER) == (void*)(IDENT)) { \
-                LOGVAL((CTX)->ctx, LY_VLOG_STR, (CTX)->path, LY_VCODE_DUPIDENT, IDENT, STMT); \
-                return LY_EVALID; \
-            } \
-        } \
-    }
-
 /**
  * @brief Fill in the prepared compiled extensions definition structure according to the parsed extension definition.
  */
@@ -317,319 +308,6 @@
 }
 
 /**
- * @brief Stack for processing if-feature expressions.
- */
-struct iff_stack {
-    size_t size;    /**< number of items in the stack */
-    size_t index;   /**< first empty item */
-    uint8_t *stack; /**< stack - array of @ref ifftokens to create the if-feature expression in prefix format */
-};
-
-/**
- * @brief Add @ref ifftokens into the stack.
- * @param[in] stack The if-feature stack to use.
- * @param[in] value One of the @ref ifftokens to store in the stack.
- * @return LY_EMEM in case of memory allocation error
- * @return LY_ESUCCESS if the value successfully stored.
- */
-static LY_ERR
-iff_stack_push(struct iff_stack *stack, uint8_t value)
-{
-    if (stack->index == stack->size) {
-        stack->size += 4;
-        stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
-        LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
-    }
-    stack->stack[stack->index++] = value;
-    return LY_SUCCESS;
-}
-
-/**
- * @brief Get (and remove) the last item form the stack.
- * @param[in] stack The if-feature stack to use.
- * @return The value from the top of the stack.
- */
-static uint8_t
-iff_stack_pop(struct iff_stack *stack)
-{
-    assert(stack && stack->index);
-
-    stack->index--;
-    return stack->stack[stack->index];
-}
-
-/**
- * @brief Clean up the stack.
- * @param[in] stack The if-feature stack to use.
- */
-static void
-iff_stack_clean(struct iff_stack *stack)
-{
-    stack->size = 0;
-    free(stack->stack);
-}
-
-/**
- * @brief Store the @ref ifftokens (@p op) on the given position in the 2bits array
- * (libyang format of the if-feature expression).
- * @param[in,out] list The 2bits array to modify.
- * @param[in] op The operand (@ref ifftokens) to store.
- * @param[in] pos Position (0-based) where to store the given @p op.
- */
-static void
-iff_setop(uint8_t *list, uint8_t op, size_t pos)
-{
-    uint8_t *item;
-    uint8_t mask = 3;
-
-    assert(op <= 3); /* max 2 bits */
-
-    item = &list[pos / 4];
-    mask = mask << 2 * (pos % 4);
-    *item = (*item) & ~mask;
-    *item = (*item) | (op << 2 * (pos % 4));
-}
-
-#define LYS_IFF_LP 0x04 /**< Additional, temporary, value of @ref ifftokens: ( */
-#define LYS_IFF_RP 0x08 /**< Additional, temporary, value of @ref ifftokens: ) */
-
-/**
- * @brief Find a feature of the given name and referenced in the given module.
- *
- * If the compiled schema is available (the schema is implemented), the feature from the compiled schema is
- * returned. Otherwise, the special array of pre-compiled features is used to search for the feature. Such
- * features are always disabled (feature from not implemented schema cannot be enabled), but in case the schema
- * will be made implemented in future (no matter if implicitly via augmenting/deviating it or explicitly via
- * ly_ctx_module_implement()), the compilation of these feature structure is finished, but the pointers
- * assigned till that time will be still valid.
- *
- * @param[in] pmod Module where the feature was referenced (used to resolve prefix of the feature).
- * @param[in] name Name of the feature including possible prefix.
- * @param[in] len Length of the string representing the feature identifier in the name variable (mandatory!).
- * @return Pointer to the feature structure if found, NULL otherwise.
- */
-static struct lysc_feature *
-lys_feature_find(const struct lysp_module *pmod, const char *name, size_t len)
-{
-    LY_ARRAY_COUNT_TYPE u;
-    struct lysc_feature *f;
-    const struct lys_module *mod;
-    const char *ptr;
-
-    assert(pmod);
-
-    if ((ptr = ly_strnchr(name, ':', len))) {
-        /* we have a prefixed feature */
-        mod = lysp_module_find_prefix(pmod, name, ptr - name);
-        LY_CHECK_RET(!mod, NULL);
-
-        len = len - (ptr - name) - 1;
-        name = ptr + 1;
-    } else {
-        /* local feature */
-        mod = pmod->mod;
-    }
-
-    /* we have the correct module, get the feature */
-    LY_ARRAY_FOR(mod->features, u) {
-        f = &mod->features[u];
-        if (!ly_strncmp(f->name, name, len)) {
-            return f;
-        }
-    }
-
-    return NULL;
-}
-
-LY_ERR
-lys_compile_iffeature(struct lysc_ctx *ctx, struct lysp_qname *qname, struct lysc_iffeature *iff)
-{
-    LY_ERR rc = LY_SUCCESS;
-    const char *c = qname->str;
-    int64_t i, j;
-    int8_t op_len, last_not = 0, checkversion = 0;
-    LY_ARRAY_COUNT_TYPE f_size = 0, expr_size = 0, f_exp = 1;
-    uint8_t op;
-    struct iff_stack stack = {0, 0, NULL};
-    struct lysc_feature *f;
-
-    assert(c);
-
-    /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
-    for (i = j = 0; c[i]; i++) {
-        if (c[i] == '(') {
-            j++;
-            checkversion = 1;
-            continue;
-        } else if (c[i] == ')') {
-            j--;
-            continue;
-        } else if (isspace(c[i])) {
-            checkversion = 1;
-            continue;
-        }
-
-        if (!strncmp(&c[i], "not", op_len = 3) || !strncmp(&c[i], "and", op_len = 3) || !strncmp(&c[i], "or", op_len = 2)) {
-            uint64_t spaces;
-            for (spaces = 0; c[i + op_len + spaces] && isspace(c[i + op_len + spaces]); spaces++) {}
-            if (c[i + op_len + spaces] == '\0') {
-                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                        "Invalid value \"%s\" of if-feature - unexpected end of expression.", qname->str);
-                return LY_EVALID;
-            } else if (!isspace(c[i + op_len])) {
-                /* feature name starting with the not/and/or */
-                last_not = 0;
-                f_size++;
-            } else if (c[i] == 'n') { /* not operation */
-                if (last_not) {
-                    /* double not */
-                    expr_size = expr_size - 2;
-                    last_not = 0;
-                } else {
-                    last_not = 1;
-                }
-            } else { /* and, or */
-                if (f_exp != f_size) {
-                    LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                            "Invalid value \"%s\" of if-feature - missing feature/expression before \"%.*s\" operation.",
-                            qname->str, op_len, &c[i]);
-                    return LY_EVALID;
-                }
-                f_exp++;
-
-                /* not a not operation */
-                last_not = 0;
-            }
-            i += op_len;
-        } else {
-            f_size++;
-            last_not = 0;
-        }
-        expr_size++;
-
-        while (!isspace(c[i])) {
-            if (!c[i] || (c[i] == ')') || (c[i] == '(')) {
-                i--;
-                break;
-            }
-            i++;
-        }
-    }
-    if (j) {
-        /* not matching count of ( and ) */
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", qname->str);
-        return LY_EVALID;
-    }
-    if (f_exp != f_size) {
-        /* features do not match the needed arguments for the logical operations */
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                "Invalid value \"%s\" of if-feature - number of features in expression does not match "
-                "the required number of operands for the operations.", qname->str);
-        return LY_EVALID;
-    }
-
-    if (checkversion || (expr_size > 1)) {
-        /* check that we have 1.1 module */
-        if (qname->mod->version != LYS_VERSION_1_1) {
-            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                    "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", qname->str);
-            return LY_EVALID;
-        }
-    }
-
-    /* allocate the memory */
-    LY_ARRAY_CREATE_RET(ctx->ctx, iff->features, f_size, LY_EMEM);
-    iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
-    stack.stack = malloc(expr_size * sizeof *stack.stack);
-    LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx->ctx); rc = LY_EMEM, error);
-
-    stack.size = expr_size;
-    f_size--; expr_size--; /* used as indexes from now */
-
-    for (i--; i >= 0; i--) {
-        if (c[i] == ')') {
-            /* push it on stack */
-            iff_stack_push(&stack, LYS_IFF_RP);
-            continue;
-        } else if (c[i] == '(') {
-            /* pop from the stack into result all operators until ) */
-            while ((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
-                iff_setop(iff->expr, op, expr_size--);
-            }
-            continue;
-        } else if (isspace(c[i])) {
-            continue;
-        }
-
-        /* end of operator or operand -> find beginning and get what is it */
-        j = i + 1;
-        while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
-            i--;
-        }
-        i++; /* go back by one step */
-
-        if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
-            if (stack.index && (stack.stack[stack.index - 1] == LYS_IFF_NOT)) {
-                /* double not */
-                iff_stack_pop(&stack);
-            } else {
-                /* not has the highest priority, so do not pop from the stack
-                 * as in case of AND and OR */
-                iff_stack_push(&stack, LYS_IFF_NOT);
-            }
-        } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
-            /* as for OR - pop from the stack all operators with the same or higher
-             * priority and store them to the result, then push the AND to the stack */
-            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
-                op = iff_stack_pop(&stack);
-                iff_setop(iff->expr, op, expr_size--);
-            }
-            iff_stack_push(&stack, LYS_IFF_AND);
-        } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
-            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
-                op = iff_stack_pop(&stack);
-                iff_setop(iff->expr, op, expr_size--);
-            }
-            iff_stack_push(&stack, LYS_IFF_OR);
-        } else {
-            /* feature name, length is j - i */
-
-            /* add it to the expression */
-            iff_setop(iff->expr, LYS_IFF_F, expr_size--);
-
-            /* now get the link to the feature definition */
-            f = lys_feature_find(qname->mod, &c[i], j - i);
-            LY_CHECK_ERR_GOTO(!f, LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                    "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", qname->str, j - i, &c[i]);
-                    rc = LY_EVALID, error)
-            iff->features[f_size] = f;
-            LY_ARRAY_INCREMENT(iff->features);
-            f_size--;
-        }
-    }
-    while (stack.index) {
-        op = iff_stack_pop(&stack);
-        iff_setop(iff->expr, op, expr_size--);
-    }
-
-    if (++expr_size || ++f_size) {
-        /* not all expected operators and operands found */
-        LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
-                "Invalid value \"%s\" of if-feature - processing error.", qname->str);
-        rc = LY_EINT;
-    } else {
-        rc = LY_SUCCESS;
-    }
-
-error:
-    /* cleanup */
-    iff_stack_clean(&stack);
-
-    return rc;
-}
-
-/**
  * @brief Compile information in the import statement - make sure there is the target module
  * @param[in] ctx Compile context.
  * @param[in] imp_p The parsed import statement structure to fill the module to.
@@ -652,7 +330,7 @@
                 LOGINT(ctx->ctx);
             } else {
                 LY_CHECK_RET(lys_parse(ctx->ctx, in, !strcmp(&imp_p->module->filepath[strlen(imp_p->module->filepath - 4)],
-                        ".yin") ? LYS_IN_YIN : LYS_IN_YANG, &mod));
+                        ".yin") ? LYS_IN_YIN : LYS_IN_YANG, NULL, &mod));
                 if (mod != imp_p->module) {
                     LOGERR(ctx->ctx, LY_EINT, "Filepath \"%s\" of the module \"%s\" does not match.",
                             imp_p->module->filepath, imp_p->module->name);
@@ -662,7 +340,7 @@
             ly_in_free(in, 1);
         }
         if (!mod) {
-            if (lysp_load_module(ctx->ctx, imp_p->module->name, imp_p->module->revision, 0, 1, (struct lys_module **)&mod)) {
+            if (lysp_load_module(ctx->ctx, imp_p->module->name, imp_p->module->revision, 0, 1, NULL, (struct lys_module **)&mod)) {
                 LOGERR(ctx->ctx, LY_ENOTFOUND, "Unable to reload \"%s\" module to import it into \"%s\", source data not found.",
                         imp_p->module->name, ctx->cur_mod->name);
                 return LY_ENOTFOUND;
@@ -677,9 +355,11 @@
 lys_identity_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
         struct lysp_ident *identities_p, struct lysc_ident **identities)
 {
-    LY_ARRAY_COUNT_TYPE offset = 0, u, v;
+    LY_ARRAY_COUNT_TYPE u;
     struct lysc_ctx context = {0};
+    struct lysc_ident *ident;
     LY_ERR ret = LY_SUCCESS;
+    ly_bool enabled;
 
     assert(ctx_sc || ctx);
 
@@ -695,27 +375,27 @@
     if (!identities_p) {
         return LY_SUCCESS;
     }
-    if (*identities) {
-        offset = LY_ARRAY_COUNT(*identities);
-    }
 
     lysc_update_path(ctx_sc, NULL, "{identity}");
-    LY_ARRAY_CREATE_RET(ctx_sc->ctx, *identities, LY_ARRAY_COUNT(identities_p), LY_EMEM);
     LY_ARRAY_FOR(identities_p, u) {
+        /* evaluate if-features */
+        LY_CHECK_RET(lys_eval_iffeatures(ctx, identities_p[u].iffeatures, &enabled));
+        if (!enabled) {
+            continue;
+        }
+
         lysc_update_path(ctx_sc, NULL, identities_p[u].name);
 
-        LY_ARRAY_INCREMENT(*identities);
-        COMPILE_CHECK_UNIQUENESS_ARRAY(ctx_sc, *identities, name, &(*identities)[offset + u], "identity", identities_p[u].name);
-        DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].name, (*identities)[offset + u].name, ret, done);
-        DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].dsc, (*identities)[offset + u].dsc, ret, done);
-        DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].ref, (*identities)[offset + u].ref, ret, done);
-        (*identities)[offset + u].module = ctx_sc->cur_mod;
-        COMPILE_ARRAY_GOTO(ctx_sc, identities_p[u].iffeatures, (*identities)[offset + u].iffeatures, v,
-                lys_compile_iffeature, ret, done);
+        /* add new compiled identity */
+        LY_ARRAY_NEW_RET(ctx_sc->ctx, *identities, ident, LY_EMEM);
+
+        DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].name, ident->name, ret, done);
+        DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].dsc, ident->dsc, ret, done);
+        DUP_STRING_GOTO(ctx_sc->ctx, identities_p[u].ref, ident->ref, ret, done);
+        ident->module = ctx_sc->cur_mod;
         /* backlinks (derived) can be added no sooner than when all the identities in the current module are present */
-        COMPILE_EXTS_GOTO(ctx_sc, identities_p[u].exts, (*identities)[offset + u].exts, &(*identities)[offset + u],
-                LYEXT_PAR_IDENT, ret, done);
-        (*identities)[offset + u].flags = identities_p[u].flags;
+        COMPILE_EXTS_GOTO(ctx_sc, identities_p[u].exts, ident->exts, ident, LYEXT_PAR_IDENT, ret, done);
+        ident->flags = identities_p[u].flags;
 
         lysc_update_path(ctx_sc, NULL, NULL);
     }
@@ -782,14 +462,14 @@
 
 LY_ERR
 lys_compile_identity_bases(struct lysc_ctx *ctx, const struct lysp_module *base_pmod, const char **bases_p,
-        struct lysc_ident *ident, struct lysc_ident ***bases)
+        struct lysc_ident *ident, struct lysc_ident ***bases, ly_bool *enabled)
 {
     LY_ARRAY_COUNT_TYPE u, v;
     const char *s, *name;
     const struct lys_module *mod;
     struct lysc_ident **idref;
 
-    assert(ident || bases);
+    assert((ident && enabled) || bases);
 
     if ((LY_ARRAY_COUNT(bases_p) > 1) && (ctx->pmod->version < LYS_VERSION_1_1)) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
@@ -841,6 +521,13 @@
         }
         if (!idref || !(*idref)) {
             if (ident) {
+                /* look into the parsed module to check whether the identity is not merely disabled */
+                LY_ARRAY_FOR(mod->parsed->identities, v) {
+                    if (!strcmp(mod->parsed->identities[v].name, name)) {
+                        *enabled = 0;
+                        return LY_SUCCESS;
+                    }
+                }
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                         "Unable to find base (%s) of identity \"%s\".", bases_p[u], ident->name);
             } else {
@@ -850,6 +537,10 @@
             return LY_EVALID;
         }
     }
+
+    if (ident) {
+        *enabled = 1;
+    }
     return LY_SUCCESS;
 }
 
@@ -857,263 +548,63 @@
  * @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.
+ * @param[in,out] idents Array of referencing identities to which the backlinks are supposed to be set. Any
+ * identities with disabled bases are removed.
  * @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)
+lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident **idents)
 {
-    LY_ARRAY_COUNT_TYPE u;
+    LY_ARRAY_COUNT_TYPE u, v;
+    ly_bool enabled;
 
     lysc_update_path(ctx, NULL, "{identity}");
-    for (u = 0; u < LY_ARRAY_COUNT(idents_p); ++u) {
-        if (!idents_p[u].bases) {
+
+restart:
+    for (u = 0, v = 0; *idents && (u < LY_ARRAY_COUNT(*idents)); ++u) {
+        /* find matching parsed identity, the disabled ones are missing in the compiled array */
+        while (v < LY_ARRAY_COUNT(idents_p)) {
+            if (idents_p[v].name == (*idents)[u].name) {
+                break;
+            }
+            ++v;
+        }
+        assert(v < LY_ARRAY_COUNT(idents_p));
+
+        if (!idents_p[v].bases) {
             continue;
         }
-        lysc_update_path(ctx, NULL, idents[u].name);
-        LY_CHECK_RET(lys_compile_identity_bases(ctx, idents[u].module->parsed, idents_p[u].bases, &idents[u], NULL));
+        lysc_update_path(ctx, NULL, (*idents)[u].name);
+        LY_CHECK_RET(lys_compile_identity_bases(ctx, (*idents)[u].module->parsed, idents_p[v].bases, &(*idents)[u], NULL,
+                &enabled));
         lysc_update_path(ctx, NULL, NULL);
+
+        if (!enabled) {
+            /* remove the identity */
+            lysc_ident_free(ctx->ctx, &(*idents)[u]);
+            LY_ARRAY_DECREMENT(*idents);
+            if (u < LY_ARRAY_COUNT(*idents)) {
+                memmove(&(*idents)[u], &(*idents)[u + 1], (LY_ARRAY_COUNT(*idents) - u) * sizeof **idents);
+            }
+            if (!LY_ARRAY_COUNT(*idents)) {
+                LY_ARRAY_FREE(*idents);
+                *idents = NULL;
+            }
+
+            /* revert compilation of all the previous identities */
+            for (v = 0; v < u; ++v) {
+                LY_ARRAY_FREE((*idents)[v].derived);
+            }
+
+            /* restart the whole process without this identity */
+            goto restart;
+        }
     }
+
     lysc_update_path(ctx, NULL, NULL);
     return LY_SUCCESS;
 }
 
-LY_ERR
-lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
-        struct lysp_feature *features_p, struct lysc_feature **features)
-{
-    LY_ERR ret = LY_SUCCESS;
-    LY_ARRAY_COUNT_TYPE offset = 0, u;
-    struct lysc_ctx context = {0};
-
-    assert(ctx_sc || ctx);
-
-    if (!ctx_sc) {
-        context.ctx = ctx;
-        context.cur_mod = parsed_mod->mod;
-        context.pmod = parsed_mod;
-        context.path_len = 1;
-        context.path[0] = '/';
-        ctx_sc = &context;
-    }
-
-    if (!features_p) {
-        return LY_SUCCESS;
-    }
-    if (*features) {
-        offset = LY_ARRAY_COUNT(*features);
-    }
-
-    lysc_update_path(ctx_sc, NULL, "{feature}");
-    LY_ARRAY_CREATE_RET(ctx_sc->ctx, *features, LY_ARRAY_COUNT(features_p), LY_EMEM);
-    LY_ARRAY_FOR(features_p, u) {
-        lysc_update_path(ctx_sc, NULL, features_p[u].name);
-
-        LY_ARRAY_INCREMENT(*features);
-        COMPILE_CHECK_UNIQUENESS_ARRAY(ctx_sc, *features, name, &(*features)[offset + u], "feature", features_p[u].name);
-        DUP_STRING_GOTO(ctx_sc->ctx, features_p[u].name, (*features)[offset + u].name, ret, done);
-        DUP_STRING_GOTO(ctx_sc->ctx, features_p[u].dsc, (*features)[offset + u].dsc, ret, done);
-        DUP_STRING_GOTO(ctx_sc->ctx, features_p[u].ref, (*features)[offset + u].ref, ret, done);
-        (*features)[offset + u].flags = features_p[u].flags;
-        (*features)[offset + u].module = ctx_sc->cur_mod;
-
-        lysc_update_path(ctx_sc, NULL, NULL);
-    }
-    lysc_update_path(ctx_sc, NULL, NULL);
-
-done:
-    return ret;
-}
-
-/**
- * @brief Check circular dependency of features - feature MUST NOT reference itself (via their if-feature statement).
- *
- * The function works in the same way as lys_compile_identity_circular_check() with different structures and error messages.
- *
- * @param[in] ctx Compile context for logging.
- * @param[in] feature The feature referenced in if-feature statement (its depfeatures list is being extended by the feature
- *            being currently processed).
- * @param[in] depfeatures The list of depending features of the feature being currently processed (not the one provided as @p feature)
- * @return LY_SUCCESS if everything is ok.
- * @return LY_EVALID if the feature references indirectly itself.
- */
-static LY_ERR
-lys_compile_feature_circular_check(struct lysc_ctx *ctx, struct lysc_feature *feature, struct lysc_feature **depfeatures)
-{
-    LY_ERR ret = LY_SUCCESS;
-    LY_ARRAY_COUNT_TYPE u, v;
-    struct ly_set recursion = {0};
-    struct lysc_feature *drv;
-
-    if (!depfeatures) {
-        return LY_SUCCESS;
-    }
-
-    for (u = 0; u < LY_ARRAY_COUNT(depfeatures); ++u) {
-        if (feature == depfeatures[u]) {
-            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                    "Feature \"%s\" is indirectly referenced from itself.", feature->name);
-            ret = LY_EVALID;
-            goto cleanup;
-        }
-        ret = ly_set_add(&recursion, depfeatures[u], 0, NULL);
-        LY_CHECK_GOTO(ret, cleanup);
-    }
-
-    for (v = 0; v < recursion.count; ++v) {
-        drv = recursion.objs[v];
-        if (!drv->depfeatures) {
-            continue;
-        }
-        for (u = 0; u < LY_ARRAY_COUNT(drv->depfeatures); ++u) {
-            if (feature == drv->depfeatures[u]) {
-                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                        "Feature \"%s\" is indirectly referenced from itself.", feature->name);
-                ret = LY_EVALID;
-                goto cleanup;
-            }
-            ly_set_add(&recursion, drv->depfeatures[u], 0, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-        }
-    }
-
-cleanup:
-    ly_set_erase(&recursion, NULL);
-    return ret;
-}
-
-/**
- * @brief Create pre-compiled features array.
- *
- * See lys_feature_precompile() for more details.
- *
- * @param[in] ctx Compile context.
- * @param[in] feature_p Parsed feature definition to compile.
- * @param[in,out] features List of already (pre)compiled features to find the corresponding precompiled feature structure.
- * @return LY_ERR value.
- */
-static LY_ERR
-lys_feature_precompile_finish(struct lysc_ctx *ctx, struct lysp_feature *feature_p, struct lysc_feature *features)
-{
-    LY_ARRAY_COUNT_TYPE u, v, x;
-    struct lysc_feature *feature, **df;
-    LY_ERR ret = LY_SUCCESS;
-
-    /* find the preprecompiled feature */
-    LY_ARRAY_FOR(features, x) {
-        if (strcmp(features[x].name, feature_p->name)) {
-            continue;
-        }
-        feature = &features[x];
-        lysc_update_path(ctx, NULL, "{feature}");
-        lysc_update_path(ctx, NULL, feature_p->name);
-
-        /* finish compilation started in lys_feature_precompile() */
-        COMPILE_EXTS_GOTO(ctx, feature_p->exts, feature->exts, feature, LYEXT_PAR_FEATURE, ret, done);
-        COMPILE_ARRAY_GOTO(ctx, feature_p->iffeatures, feature->iffeatures, u, lys_compile_iffeature, ret, done);
-        if (feature->iffeatures) {
-            for (u = 0; u < LY_ARRAY_COUNT(feature->iffeatures); ++u) {
-                if (feature->iffeatures[u].features) {
-                    for (v = 0; v < LY_ARRAY_COUNT(feature->iffeatures[u].features); ++v) {
-                        /* check for circular dependency - direct reference first,... */
-                        if (feature == feature->iffeatures[u].features[v]) {
-                            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                                    "Feature \"%s\" is referenced from itself.", feature->name);
-                            return LY_EVALID;
-                        }
-                        /* ... and indirect circular reference */
-                        LY_CHECK_RET(lys_compile_feature_circular_check(ctx, feature->iffeatures[u].features[v], feature->depfeatures));
-
-                        /* add itself into the dependants list */
-                        LY_ARRAY_NEW_RET(ctx->ctx, feature->iffeatures[u].features[v]->depfeatures, df, LY_EMEM);
-                        *df = feature;
-                    }
-                }
-            }
-        }
-        lysc_update_path(ctx, NULL, NULL);
-        lysc_update_path(ctx, NULL, NULL);
-done:
-        return ret;
-    }
-
-    LOGINT(ctx->ctx);
-    return LY_EINT;
-}
-
-void
-lys_feature_precompile_revert(struct lysc_ctx *ctx, struct lys_module *mod)
-{
-    LY_ARRAY_COUNT_TYPE u, v;
-
-    /* in the dis_features list, remove all the parts (from finished compiling process)
-     * which may points into the data being freed here */
-    LY_ARRAY_FOR(mod->features, u) {
-        LY_ARRAY_FOR(mod->features[u].iffeatures, v) {
-            lysc_iffeature_free(ctx->ctx, &mod->features[u].iffeatures[v]);
-        }
-        LY_ARRAY_FREE(mod->features[u].iffeatures);
-        mod->features[u].iffeatures = NULL;
-
-        LY_ARRAY_FOR(mod->features[u].exts, v) {
-            lysc_ext_instance_free(ctx->ctx, &(mod->features[u].exts)[v]);
-        }
-        LY_ARRAY_FREE(mod->features[u].exts);
-        mod->features[u].exts = NULL;
-    }
-}
-
-/**
- * @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;
-    LY_ARRAY_COUNT_TYPE u, v;
-    struct ly_set features = {0};
-
-    for (iter = refnode; iter; iter = iter->parent) {
-        if (iter->iffeatures) {
-            LY_ARRAY_FOR(iter->iffeatures, u) {
-                LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
-                    LY_CHECK_GOTO(ly_set_add(&features, iter->iffeatures[u].features[v], 0, NULL), cleanup);
-                }
-            }
-        }
-    }
-
-    /* we should have, in features set, a superset of features applicable to the target node.
-     * If the feature is not present, we don;t have a subset of features applicable
-     * to the leafref itself. */
-    for (iter = target; iter; iter = iter->parent) {
-        if (iter->iffeatures) {
-            LY_ARRAY_FOR(iter->iffeatures, u) {
-                LY_ARRAY_FOR(iter->iffeatures[u].features, v) {
-                    if (!ly_set_contains(&features, iter->iffeatures[u].features[v], NULL)) {
-                        /* feature not present */
-                        goto cleanup;
-                    }
-                }
-            }
-        }
-    }
-    ret = LY_SUCCESS;
-
-cleanup:
-    ly_set_erase(&features, NULL);
-    return ret;
-}
-
 static void *
 lys_compile_extension_instance_storage(enum ly_stmt stmt, struct lysc_ext_substmt *substmts)
 {
@@ -1131,8 +622,9 @@
     LY_ERR ret = LY_EVALID, r;
     LY_ARRAY_COUNT_TYPE u;
     struct lysp_stmt *stmt;
-    struct lysp_qname qname;
+    struct lysp_qname *iffeatures, *iffeat;
     void *parsed = NULL, **compiled = NULL;
+    ly_bool enabled;
 
     /* check for invalid substatements */
     for (stmt = ext->child; stmt; stmt = stmt->next) {
@@ -1216,26 +708,19 @@
                     LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
                     break;
                 }
-                case LY_STMT_IF_FEATURE: {
-                    struct lysc_iffeature *iff = NULL;
-
-                    if (substmts[u].cardinality < LY_STMT_CARD_SOME) {
-                        /* single item */
-                        if (((struct lysc_iffeature *)substmts[u].storage)->features) {
-                            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LY_VCODE_DUPSTMT, stmt->stmt);
-                            goto cleanup;
-                        }
-                        iff = (struct lysc_iffeature *)substmts[u].storage;
-                    } else {
-                        /* sized array */
-                        struct lysc_iffeature **iffs = (struct lysc_iffeature **)substmts[u].storage;
-                        LY_ARRAY_NEW_GOTO(ctx->ctx, *iffs, iff, ret, cleanup);
+                case LY_STMT_IF_FEATURE:
+                    iffeatures = NULL;
+                    LY_ARRAY_NEW_GOTO(ctx->ctx, iffeatures, iffeat, ret, cleanup);
+                    iffeat->str = stmt->arg;
+                    iffeat->mod = ctx->pmod;
+                    r = lys_eval_iffeatures(ctx->ctx, iffeatures, &enabled);
+                    LY_ARRAY_FREE(iffeatures);
+                    LY_CHECK_ERR_GOTO(r, ret = r, cleanup);
+                    if (!enabled) {
+                        /* it is disabled, remove the whole extension instance */
+                        return LY_ENOT;
                     }
-                    qname.str = stmt->arg;
-                    qname.mod = ctx->pmod;
-                    LY_CHECK_ERR_GOTO(r = lys_compile_iffeature(ctx, &qname, iff), ret = r, cleanup);
                     break;
-                }
                 /* TODO support other substatements (parse stmt to lysp and then compile lysp to lysc),
                  * also note that in many statements their extensions are not taken into account  */
                 default:
@@ -1401,7 +886,7 @@
         if (!mod->implemented) {
             /* unimplemented module found */
             if (implement) {
-                LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod));
+                LY_CHECK_RET(lys_set_implemented((struct lys_module *)mod, NULL));
             } else {
                 *mod_p = mod;
                 break;
@@ -1647,13 +1132,7 @@
         }
     }
 
-    /* check if leafref and its target are under common if-features */
-    if (lys_compile_leafref_features_validate(node, target)) {
-        LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, 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.", lref->path->expr);
-        return LY_EVALID;
-    }
+    /* TODO check if leafref and its target are under common if-features */
 
     return LY_SUCCESS;
 }
@@ -1688,7 +1167,17 @@
     ext_mod = ly_ctx_get_module_latest(ctx->ctx, "ietf-yang-metadata");
 
     /* compile the extension instance */
-    LY_CHECK_GOTO(ret = lys_compile_ext(ctx, ext_p, ext, mod->compiled, LYEXT_PAR_MODULE, ext_mod), cleanup);
+    ret = lys_compile_ext(ctx, ext_p, ext, mod->compiled, LYEXT_PAR_MODULE, ext_mod);
+    if (ret == LY_ENOT) {
+        /* free the extension */
+        lysc_ext_instance_free(ctx->ctx, ext);
+        LY_ARRAY_DECREMENT_FREE(mod->compiled->exts);
+
+        ret = LY_SUCCESS;
+        goto cleanup;
+    } else if (ret) {
+        goto cleanup;
+    }
 
 cleanup:
     lysp_ext_instance_free(ctx->ctx, ext_p);
@@ -1858,9 +1347,67 @@
     struct lysc_type_leafref *lref;
     struct lysc_augment *aug;
     struct lysc_deviation *dev;
+    struct ly_set disabled_op = {0};
     LY_ARRAY_COUNT_TYPE v;
     uint32_t i;
 
+#define ARRAY_DEL_ITEM(array, item) \
+    { \
+        LY_ARRAY_COUNT_TYPE u__; \
+        LY_ARRAY_FOR(array, u__) { \
+            if ((array) + u__ == item) { \
+                LY_ARRAY_DECREMENT(array); \
+                if (u__ < LY_ARRAY_COUNT(array)) { \
+                    memmove((array) + u__, (array) + u__ + 1, (LY_ARRAY_COUNT(array) - u__) * sizeof *(array)); \
+                } \
+                if (!LY_ARRAY_COUNT(array)) { \
+                    LY_ARRAY_FREE(array); \
+                    (array) = NULL; \
+                } \
+                break; \
+            } \
+        } \
+    }
+
+    /* remove all disabled nodes */
+    for (i = 0; i < ctx->disabled.count; ++i) {
+        node = ctx->disabled.snodes[i];
+        if (node->flags & LYS_KEY) {
+            LOGVAL(ctx->ctx, LY_VLOG_LYSC, node, LYVE_REFERENCE, "Key \"%s\" is disabled by its if-features.",
+                    node->name);
+            return LY_EVALID;
+        }
+
+        if (node->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) {
+            if (node->nodetype & (LYS_RPC | LYS_ACTION)) {
+                lysc_action_free(ctx->ctx, (struct lysc_action *)node);
+            } else {
+                lysc_notif_free(ctx->ctx, (struct lysc_notif *)node);
+            }
+            /* remember all freed RPCs/actions/notifs */
+            ly_set_add(&disabled_op, node, 1, NULL);
+        } else {
+            lysc_node_free(ctx->ctx, node, 1);
+        }
+    }
+
+    /* remove ops also from their arrays, from end so as not to move other items and change these pointer targets */
+    i = disabled_op.count;
+    while (i) {
+        --i;
+        node = disabled_op.snodes[i];
+        if (node->nodetype == LYS_RPC) {
+            ARRAY_DEL_ITEM(node->module->compiled->rpcs, (struct lysc_action *)node);
+        } else if (node->nodetype == LYS_ACTION) {
+            ARRAY_DEL_ITEM(*lysc_node_actions_p(node->parent), (struct lysc_action *)node);
+        } else if (node->parent) {
+            ARRAY_DEL_ITEM(*lysc_node_notifs_p(node->parent), (struct lysc_notif *)node);
+        } else {
+            ARRAY_DEL_ITEM(node->module->compiled->notifs, (struct lysc_notif *)node);
+        }
+    }
+    ly_set_erase(&disabled_op, NULL);
+
     /* for leafref, we need 2 rounds - first detects circular chain by storing the first referred type (which
      * can be also leafref, in case it is already resolved, go through the chain and check that it does not
      * point to the starting leafref type). The second round stores the first non-leafref type for later data validation. */
@@ -1937,50 +1484,7 @@
     }
 
     return LY_SUCCESS;
-}
-
-/**
- * @brief Compile features in the current module and all its submodules.
- *
- * @param[in] ctx Compile context.
- * @return LY_ERR value.
- */
-static LY_ERR
-lys_compile_features(struct lysc_ctx *ctx)
-{
-    struct lysp_submodule *submod;
-    LY_ARRAY_COUNT_TYPE u, v;
-
-    if (!ctx->cur_mod->features) {
-        /* features are compiled directly into the module structure,
-         * but it must be done in two steps to allow forward references (via if-feature) between the features themselves */
-        LY_CHECK_RET(lys_feature_precompile(ctx, NULL, NULL, ctx->cur_mod->parsed->features, &ctx->cur_mod->features));
-        LY_ARRAY_FOR(ctx->cur_mod->parsed->includes, v) {
-            submod = ctx->cur_mod->parsed->includes[v].submodule;
-            LY_CHECK_RET(lys_feature_precompile(ctx, NULL, NULL, submod->features, &ctx->cur_mod->features));
-        }
-    }
-
-    /* finish feature compilation, not only for the main module, but also for the submodules.
-     * Due to possible forward references, it must be done when all the features (including submodules)
-     * are present. */
-    LY_ARRAY_FOR(ctx->cur_mod->parsed->features, u) {
-        LY_CHECK_RET(lys_feature_precompile_finish(ctx, &ctx->cur_mod->parsed->features[u], ctx->cur_mod->features));
-    }
-
-    lysc_update_path(ctx, NULL, "{submodule}");
-    LY_ARRAY_FOR(ctx->cur_mod->parsed->includes, v) {
-        submod = ctx->cur_mod->parsed->includes[v].submodule;
-
-        lysc_update_path(ctx, NULL, submod->name);
-        LY_ARRAY_FOR(submod->features, u) {
-            LY_CHECK_RET(lys_feature_precompile_finish(ctx, &submod->features[u], ctx->cur_mod->features));
-        }
-        lysc_update_path(ctx, NULL, NULL);
-    }
-    lysc_update_path(ctx, NULL, NULL);
-
-    return LY_SUCCESS;
+#undef ARRAY_DEL_ITEM
 }
 
 /**
@@ -2004,7 +1508,7 @@
     }
 
     if (ctx->cur_mod->parsed->identities) {
-        LY_CHECK_RET(lys_compile_identities_derived(ctx, ctx->cur_mod->parsed->identities, ctx->cur_mod->identities));
+        LY_CHECK_RET(lys_compile_identities_derived(ctx, ctx->cur_mod->parsed->identities, &ctx->cur_mod->identities));
     }
     lysc_update_path(ctx, NULL, "{submodule}");
     LY_ARRAY_FOR(ctx->cur_mod->parsed->includes, u) {
@@ -2012,7 +1516,7 @@
         submod = ctx->cur_mod->parsed->includes[u].submodule;
         if (submod->identities) {
             lysc_update_path(ctx, NULL, submod->name);
-            LY_CHECK_RET(lys_compile_identities_derived(ctx, submod->identities, ctx->cur_mod->identities));
+            LY_CHECK_RET(lys_compile_identities_derived(ctx, submod->identities, &ctx->cur_mod->identities));
             lysc_update_path(ctx, NULL, NULL);
         }
     }
@@ -2021,6 +1525,86 @@
     return LY_SUCCESS;
 }
 
+static LY_ERR
+lys_recompile_mod(struct lys_module *mod, ly_bool log)
+{
+    LY_ERR ret;
+    uint32_t prev_lo;
+    struct lysp_import *imp;
+
+    if (!mod->implemented || mod->compiled) {
+        /* nothing to do */
+        return LY_SUCCESS;
+    }
+
+    /* we need all implemented imports to be recompiled */
+    LY_ARRAY_FOR(mod->parsed->imports, struct lysp_import, imp) {
+        LY_CHECK_RET(lys_recompile_mod(imp->module, log));
+    }
+
+    if (!log) {
+        /* recompile, must succeed because it was already compiled; hide messages because any
+         * warnings were already printed, are not really relevant, and would hide the real error */
+        prev_lo = ly_log_options(0);
+    }
+    ret = lys_compile(mod, 0);
+    if (!log) {
+        ly_log_options(prev_lo);
+    }
+
+    if (ret && !log) {
+        LOGERR(mod->ctx, ret, "Recompilation of module \"%s\" failed.", mod->name);
+    }
+    return ret;
+}
+
+LY_ERR
+lys_recompile(struct ly_ctx *ctx, const struct lys_module *skip)
+{
+    uint32_t idx;
+    struct lys_module *mod;
+    LY_ERR ret = LY_SUCCESS, r;
+
+    /* free all the modules */
+    for (idx = 0; idx < ctx->list.count; ++idx) {
+        mod = ctx->list.objs[idx];
+        /* skip this module */
+        if (skip && (mod == skip)) {
+            continue;
+        }
+
+        if (mod->compiled) {
+            /* free the module */
+            lysc_module_free(mod->compiled, NULL);
+            mod->compiled = NULL;
+        }
+
+        /* free precompiled iffeatures */
+        lys_free_feature_iffeatures(mod->parsed);
+    }
+
+    /* recompile all the modules */
+    for (idx = 0; idx < ctx->list.count; ++idx) {
+        mod = ctx->list.objs[idx];
+
+        /* skip this module */
+        if (skip && (mod == skip)) {
+            continue;
+        }
+
+        /* compile again */
+        r = lys_recompile_mod(mod, skip ? 1 : 0);
+        if (r) {
+            if (skip) {
+                return r;
+            }
+            ret = r;
+        }
+    }
+
+    return ret;
+}
+
 LY_ERR
 lys_compile(struct lys_module *mod, uint32_t options)
 {
@@ -2062,10 +1646,7 @@
         LY_CHECK_GOTO(ret = lys_compile_import(&ctx, &sp->imports[u]), error);
     }
 
-    /* features */
-    LY_CHECK_GOTO(ret = lys_compile_features(&ctx), error);
-
-    /* identities, work similarly to features with the precompilation */
+    /* identities */
     LY_CHECK_GOTO(ret = lys_compile_identities(&ctx), error);
 
     /* augments and deviations */
@@ -2161,7 +1742,8 @@
         LY_CHECK_GOTO(ret = lys_compile_ietf_netconf_wd_annotation(&ctx, mod), error);
     }
 
-    /* there can be no leftover deviations */
+    /* there can be no leftover deviations or augments */
+    LY_CHECK_ERR_GOTO(ctx.augs.count, LOGINT(ctx.ctx); ret = LY_EINT, error);
     LY_CHECK_ERR_GOTO(ctx.devs.count, LOGINT(ctx.ctx); ret = LY_EINT, error);
 
     for (i = 0; i < ctx.dflts.count; ++i) {
@@ -2172,6 +1754,7 @@
     ly_set_erase(&ctx.leafrefs, NULL);
     ly_set_erase(&ctx.groupings, NULL);
     ly_set_erase(&ctx.tpdf_chain, NULL);
+    ly_set_erase(&ctx.disabled, NULL);
     ly_set_erase(&ctx.augs, NULL);
     ly_set_erase(&ctx.devs, NULL);
     ly_set_erase(&ctx.uses_augs, NULL);
@@ -2181,7 +1764,6 @@
 
 error:
     lys_precompile_augments_deviations_revert(ctx.ctx, mod);
-    lys_feature_precompile_revert(&ctx, mod);
     for (i = 0; i < ctx.dflts.count; ++i) {
         lysc_unres_dflt_free(ctx.ctx, ctx.dflts.objs[i]);
     }
@@ -2190,6 +1772,7 @@
     ly_set_erase(&ctx.leafrefs, NULL);
     ly_set_erase(&ctx.groupings, NULL);
     ly_set_erase(&ctx.tpdf_chain, NULL);
+    ly_set_erase(&ctx.disabled, NULL);
     for (i = 0; i < ctx.augs.count; ++i) {
         lysc_augment_free(ctx.ctx, ctx.augs.objs[i]);
     }
diff --git a/src/schema_compile.h b/src/schema_compile.h
index 5266ffb..0e2c800 100644
--- a/src/schema_compile.h
+++ b/src/schema_compile.h
@@ -29,14 +29,18 @@
  *
  * @{
  */
-#define LYS_COMPILE_RPC_INPUT  LYS_CONFIG_W       /**< Internal option when compiling schema tree of RPC/action input */
-#define LYS_COMPILE_RPC_OUTPUT LYS_CONFIG_R       /**< Internal option when compiling schema tree of RPC/action output */
-#define LYS_COMPILE_RPC_MASK   LYS_CONFIG_MASK    /**< mask for the internal RPC options */
-#define LYS_COMPILE_NOTIFICATION 0x08             /**< Internal option when compiling schema tree of Notification */
+#define LYS_COMPILE_RPC_INPUT       LYS_CONFIG_W    /**< Internal option when compiling schema tree of RPC/action input */
+#define LYS_COMPILE_RPC_OUTPUT      LYS_CONFIG_R    /**< Internal option when compiling schema tree of RPC/action output */
+#define LYS_COMPILE_RPC_MASK        LYS_CONFIG_MASK /**< mask for the internal RPC options */
+#define LYS_COMPILE_NOTIFICATION    0x04            /**< Internal option when compiling schema tree of Notification */
 
-#define LYS_COMPILE_GROUPING   0x10               /** Compiling (validation) of a non-instantiated grouping.
-                                                      In this case not all the restrictions are checked since they can be valid only
-                                                      in the real placement of the grouping. TODO - what specifically is not done */
+#define LYS_COMPILE_GROUPING        0x08            /**< Compiling (validation) of a non-instantiated grouping.
+                                                      In this case not all the restrictions are checked since they can
+                                                      be valid only in the real placement of the grouping.
+                                                      TODO - what specifically is not done */
+#define LYS_COMPILE_DISABLED        0x10            /**< Compiling a disabled subtree (by its if-features). Meaning
+                                                      it will be removed at the end of compilation and should not be
+                                                      added to any unres sets. */
 /** @} scflags */
 
 /**
@@ -51,6 +55,7 @@
     struct ly_set leafrefs;     /**< to validate leafref's targets */
     struct ly_set dflts;        /**< set of incomplete default values */
     struct ly_set tpdf_chain;
+    struct ly_set disabled;     /**< set of compiled nodes whose if-feature(s) was not satisifed */
     struct ly_set augs;         /**< set of compiled non-applied top-level augments */
     struct ly_set devs;         /**< set of compiled non-applied deviations */
     struct ly_set uses_augs;    /**< set of compiled non-applied uses augments */
@@ -126,7 +131,12 @@
         for (LY_ARRAY_COUNT_TYPE __exts_iter = 0, __array_offset = LY_ARRAY_COUNT(EXT_C); __exts_iter < LY_ARRAY_COUNT(EXTS_P); ++__exts_iter) { \
             LY_ARRAY_INCREMENT(EXT_C); \
             RET = lys_compile_ext(CTX, &(EXTS_P)[__exts_iter], &(EXT_C)[__exts_iter + __array_offset], PARENT, PARENT_TYPE, NULL); \
-            LY_CHECK_GOTO(RET != LY_SUCCESS, GOTO); \
+            if (RET == LY_ENOT) { \
+                LY_ARRAY_DECREMENT_FREE(EXT_C); \
+                --__array_offset; \
+                RET = LY_SUCCESS; \
+            } \
+            LY_CHECK_GOTO(RET, GOTO); \
         } \
     }
 
@@ -139,21 +149,14 @@
  * @param[in] parent Extension instance parent.
  * @param[in] parent_type Extension instance parent type.
  * @param[in] ext_mod Optional module with the extension instance extension definition, set only for internal annotations.
- * @return LY_ERR value.
+ * @return LY_SUCCESS on success.
+ * @return LY_ENOT if the extension is disabled and should be ignored.
+ * @return LY_ERR on error.
  */
 LY_ERR lys_compile_ext(struct lysc_ctx *ctx, struct lysp_ext_instance *ext_p, struct lysc_ext_instance *ext, void *parent,
         LYEXT_PARENT parent_type, const struct lys_module *ext_mod);
 
 /**
- * @brief Compile information from the if-feature statement
- * @param[in] ctx Compile context.
- * @param[in] qname The if-feature argument to process. It is pointer-to-qname just to unify the compile functions.
- * @param[in,out] iff Prepared (empty) compiled if-feature structure to fill.
- * @return LY_ERR value.
- */
-LY_ERR lys_compile_iffeature(struct lysc_ctx *ctx, struct lysp_qname *qname, struct lysc_iffeature *iff);
-
-/**
  * @brief Compile information from the identity statement
  *
  * The backlinks to the identities derived from this one are supposed to be filled later via ::lys_compile_identity_bases().
@@ -182,44 +185,11 @@
  * @param[in] bases_p Array of names (including prefix if necessary) of base identities.
  * @param[in] ident Referencing identity to work with, NULL for identityref.
  * @param[in] bases Array of bases of identityref to fill in.
+ * @param[in] enabled Whether the base is disabled, must be set if @p ident is set.
  * @return LY_ERR value.
  */
 LY_ERR lys_compile_identity_bases(struct lysc_ctx *ctx, const struct lysp_module *base_pmod, const char **bases_p,
-        struct lysc_ident *ident, struct lysc_ident ***bases);
-
-/**
- * @brief Create pre-compiled features array.
- *
- * Features are compiled in two steps to allow forward references between them via their if-feature statements.
- * In case of not implemented schemas, the precompiled list of features is stored in lys_module structure and
- * the compilation is not finished (if-feature and extensions are missing) and all the features are permanently
- * disabled without a chance to change it. The list is used as target for any if-feature statement in any
- * implemented module to get valid data to evaluate its result. The compilation is finished via
- * ::lys_feature_precompile_finish() in implemented modules. In case a not implemented module becomes implemented,
- * the precompiled list is reused to finish the compilation to preserve pointers already used in various compiled
- * if-feature structures.
- *
- * @param[in] ctx_sc Compile context - alternative to the combination of @p ctx and @p parsed_mod.
- * @param[in] ctx libyang context.
- * @param[in] parsed_mod Module with the features.
- * @param[in] features_p Array of the parsed features definitions to precompile.
- * @param[in,out] features Pointer to the storage of the (pre)compiled features array where the new features are
- * supposed to be added. The storage is supposed to be initiated to NULL when the first parsed features are going
- * to be processed.
- * @return LY_ERR value.
- */
-LY_ERR lys_feature_precompile(struct lysc_ctx *ctx_sc, struct ly_ctx *ctx, struct lysp_module *parsed_mod,
-        struct lysp_feature *features_p, struct lysc_feature **features);
-
-/**
- * @brief Revert compiled list of features back to the precompiled state.
- *
- * Function is needed in case the compilation failed and the schema is expected to revert back to the non-compiled status.
- *
- * @param[in] ctx Compilation context.
- * @param[in] mod The module structure with the features to decompile.
- */
-void lys_feature_precompile_revert(struct lysc_ctx *ctx, struct lys_module *mod);
+        struct lysc_ident *ident, struct lysc_ident ***bases, ly_bool *enabled);
 
 /**
  * @brief Check statement's status for invalid combination.
diff --git a/src/schema_compile_amend.c b/src/schema_compile_amend.c
index 80f479c..74114da 100644
--- a/src/schema_compile_amend.c
+++ b/src/schema_compile_amend.c
@@ -110,7 +110,7 @@
 
             /* all the modules must be implemented */
             if (!mod->implemented) {
-                ret = lys_set_implemented(mod);
+                ret = lys_set_implemented(mod, NULL);
                 LY_CHECK_GOTO(ret, cleanup);
             }
         }
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index 803fec2..a391fa1 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -37,6 +37,7 @@
 #include "plugins_types.h"
 #include "schema_compile.h"
 #include "schema_compile_amend.h"
+#include "schema_features.h"
 #include "set.h"
 #include "tree.h"
 #include "tree_data.h"
@@ -69,6 +70,10 @@
     struct lysc_unres_dflt *r = NULL;
     uint32_t i;
 
+    if (ctx->options & LYS_COMPILE_DISABLED) {
+        return LY_SUCCESS;
+    }
+
     for (i = 0; i < ctx->dflts.count; ++i) {
         if (((struct lysc_unres_dflt *)ctx->dflts.objs[i])->leaf == leaf) {
             /* just replace the default */
@@ -113,6 +118,10 @@
     struct lysc_unres_dflt *r = NULL;
     uint32_t i;
 
+    if (ctx->options & LYS_COMPILE_DISABLED) {
+        return LY_SUCCESS;
+    }
+
     for (i = 0; i < ctx->dflts.count; ++i) {
         if (((struct lysc_unres_dflt *)ctx->dflts.objs[i])->llist == llist) {
             /* just replace the defaults */
@@ -275,7 +284,7 @@
         /* compile when */
         LY_CHECK_RET(lys_compile_when_(ctx, when_p, flags, ctx_node, new_when));
 
-        if (!(ctx->options & LYS_COMPILE_GROUPING)) {
+        if (!(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
             /* do not check "when" semantics in a grouping */
             LY_CHECK_RET(ly_set_add(&ctx->xpath, node, 0, NULL));
         }
@@ -289,7 +298,7 @@
         ++(*when_c)->refcount;
         *new_when = *when_c;
 
-        if (!(ctx->options & LYS_COMPILE_GROUPING)) {
+        if (!(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
             /* in this case check "when" again for all children because of dummy node check */
             LY_CHECK_RET(ly_set_add(&ctx->xpath, node, 0, NULL));
         }
@@ -1154,9 +1163,10 @@
 {
     LY_ERR ret = LY_SUCCESS;
     LY_ARRAY_COUNT_TYPE u, v, match = 0;
-    int32_t value = 0;
-    uint32_t position = 0;
+    int32_t value = 0, cur_val;
+    uint32_t position = 0, cur_pos;
     struct lysc_type_bitenum_item *e, storage;
+    ly_bool enabled;
 
     if (base_enums && (ctx->pmod->version < LYS_VERSION_1_1)) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG, "%s type can be subtyped only in YANG 1.1 modules.",
@@ -1165,48 +1175,43 @@
     }
 
     LY_ARRAY_FOR(enums_p, u) {
-        LY_ARRAY_NEW_RET(ctx->ctx, *enums, e, LY_EMEM);
-        DUP_STRING_GOTO(ctx->ctx, enums_p[u].name, e->name, ret, done);
-        DUP_STRING_GOTO(ctx->ctx, enums_p[u].ref, e->dsc, ret, done);
-        DUP_STRING_GOTO(ctx->ctx, enums_p[u].ref, e->ref, ret, done);
-        e->flags = enums_p[u].flags & LYS_FLAGS_COMPILED_MASK;
+        /* perform all checks */
         if (base_enums) {
             /* check the enum/bit presence in the base type - the set of enums/bits in the derived type must be a subset */
             LY_ARRAY_FOR(base_enums, v) {
-                if (!strcmp(e->name, base_enums[v].name)) {
+                if (!strcmp(enums_p[u].name, base_enums[v].name)) {
                     break;
                 }
             }
             if (v == LY_ARRAY_COUNT(base_enums)) {
                 LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                         "Invalid %s - derived type adds new item \"%s\".",
-                        basetype == LY_TYPE_ENUM ? "enumeration" : "bits", e->name);
+                        basetype == LY_TYPE_ENUM ? "enumeration" : "bits", enums_p[u].name);
                 return LY_EVALID;
             }
             match = v;
         }
 
         if (basetype == LY_TYPE_ENUM) {
-            e->flags |= LYS_ISENUM;
             if (enums_p[u].flags & LYS_SET_VALUE) {
-                e->value = (int32_t)enums_p[u].value;
-                if (!u || (e->value >= value)) {
-                    value = e->value + 1;
+                cur_val = (int32_t)enums_p[u].value;
+                if (!u || (cur_val >= value)) {
+                    value = cur_val + 1;
                 }
                 /* check collision with other values */
-                for (v = 0; v < LY_ARRAY_COUNT(*enums) - 1; ++v) {
-                    if (e->value == (*enums)[v].value) {
+                LY_ARRAY_FOR(*enums, v) {
+                    if (cur_val == (*enums)[v].value) {
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                                 "Invalid enumeration - value %d collide in items \"%s\" and \"%s\".",
-                                e->value, e->name, (*enums)[v].name);
+                                cur_val, enums_p[u].name, (*enums)[v].name);
                         return LY_EVALID;
                     }
                 }
             } else if (base_enums) {
                 /* inherit the assigned value */
-                e->value = base_enums[match].value;
-                if (!u || (e->value >= value)) {
-                    value = e->value + 1;
+                cur_val = base_enums[match].value;
+                if (!u || (cur_val >= value)) {
+                    value = cur_val + 1;
                 }
             } else {
                 /* assign value automatically */
@@ -1214,31 +1219,31 @@
                     /* counter overflow */
                     LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                             "Invalid enumeration - it is not possible to auto-assign enum value for "
-                            "\"%s\" since the highest value is already 2147483647.", e->name);
+                            "\"%s\" since the highest value is already 2147483647.", enums_p[u].name);
                     return LY_EVALID;
                 }
-                e->value = value++;
+                cur_val = value++;
             }
         } else { /* LY_TYPE_BITS */
             if (enums_p[u].flags & LYS_SET_VALUE) {
-                e->value = (int32_t)enums_p[u].value;
-                if (!u || ((uint32_t)e->value >= position)) {
-                    position = (uint32_t)e->value + 1;
+                cur_pos = (uint32_t)enums_p[u].value;
+                if (!u || (cur_pos >= position)) {
+                    position = cur_pos + 1;
                 }
                 /* check collision with other values */
-                for (v = 0; v < LY_ARRAY_COUNT(*enums) - 1; ++v) {
-                    if (e->value == (*enums)[v].value) {
+                LY_ARRAY_FOR(*enums, v) {
+                    if (cur_pos == (*enums)[v].position) {
                         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                                 "Invalid bits - position %u collide in items \"%s\" and \"%s\".",
-                                (uint32_t)e->value, e->name, (*enums)[v].name);
+                                cur_pos, enums_p[u].name, (*enums)[v].name);
                         return LY_EVALID;
                     }
                 }
             } else if (base_enums) {
                 /* inherit the assigned value */
-                e->value = base_enums[match].value;
-                if (!u || ((uint32_t)e->value >= position)) {
-                    position = (uint32_t)e->value + 1;
+                cur_pos = base_enums[match].position;
+                if (!u || (cur_pos >= position)) {
+                    position = cur_pos + 1;
                 }
             } else {
                 /* assign value automatically */
@@ -1246,31 +1251,51 @@
                     /* counter overflow */
                     LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                             "Invalid bits - it is not possible to auto-assign bit position for "
-                            "\"%s\" since the highest value is already 4294967295.", e->name);
+                            "\"%s\" since the highest value is already 4294967295.", enums_p[u].name);
                     return LY_EVALID;
                 }
-                e->value = position++;
+                cur_pos = position++;
             }
         }
 
+        /* the assigned values must not change from the derived type */
         if (base_enums) {
-            /* the assigned values must not change from the derived type */
-            if (e->value != base_enums[match].value) {
-                if (basetype == LY_TYPE_ENUM) {
+            if (basetype == LY_TYPE_ENUM) {
+                if (cur_val != base_enums[match].value) {
                     LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                             "Invalid enumeration - value of the item \"%s\" has changed from %d to %d in the derived type.",
-                            e->name, base_enums[match].value, e->value);
-                } else {
+                            enums_p[u].name, base_enums[match].value, cur_val);
+                    return LY_EVALID;
+                }
+            } else {
+                if (cur_pos != base_enums[match].position) {
                     LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SYNTAX_YANG,
                             "Invalid bits - position of the item \"%s\" has changed from %u to %u in the derived type.",
-                            e->name, (uint32_t)base_enums[match].value, (uint32_t)e->value);
+                            enums_p[u].name, base_enums[match].position, cur_pos);
+                    return LY_EVALID;
                 }
-                return LY_EVALID;
             }
         }
 
-        COMPILE_ARRAY_GOTO(ctx, enums_p[u].iffeatures, e->iffeatures, v, lys_compile_iffeature, ret, done);
-        COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, basetype == LY_TYPE_ENUM ? LYEXT_PAR_TYPE_ENUM : LYEXT_PAR_TYPE_BIT, ret, done);
+        /* evaluate if-ffeatures */
+        LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, enums_p[u].iffeatures, &enabled));
+        if (!enabled) {
+            continue;
+        }
+
+        /* add new enum/bit */
+        LY_ARRAY_NEW_RET(ctx->ctx, *enums, e, LY_EMEM);
+        DUP_STRING_GOTO(ctx->ctx, enums_p[u].name, e->name, ret, done);
+        DUP_STRING_GOTO(ctx->ctx, enums_p[u].dsc, e->dsc, ret, done);
+        DUP_STRING_GOTO(ctx->ctx, enums_p[u].ref, e->ref, ret, done);
+        e->flags = (enums_p[u].flags & LYS_FLAGS_COMPILED_MASK) | (basetype == LY_TYPE_ENUM ? LYS_ISENUM : 0);
+        if (basetype == LY_TYPE_ENUM) {
+            e->value = cur_val;
+        } else {
+            e->position = cur_pos;
+        }
+        COMPILE_EXTS_GOTO(ctx, enums_p[u].exts, e->exts, e, basetype == LY_TYPE_ENUM ? LYEXT_PAR_TYPE_ENUM :
+                LYEXT_PAR_TYPE_BIT, ret, done);
 
         if (basetype == LY_TYPE_BITS) {
             /* keep bits ordered by position */
@@ -1465,7 +1490,7 @@
                 }
                 return LY_EVALID;
             }
-            LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases));
+            LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases, NULL));
         }
 
         if (!base && !type_p->flags) {
@@ -1985,7 +2010,7 @@
         parent = lysc_data_parent(parent);
     }
 
-    getnext_flags = LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCHOICE;
+    getnext_flags = LYS_GETNEXT_WITHCHOICE;
     if (parent && (parent->nodetype & (LYS_RPC | LYS_ACTION)) && (exclude->flags & LYS_CONFIG_R)) {
         getnext_flags |= LYS_GETNEXT_OUTPUT;
     }
@@ -1999,7 +2024,7 @@
         /* we must compare with both the choice and all its nested data-definiition nodes (but not recursively) */
         if (iter->nodetype == LYS_CHOICE) {
             iter2 = NULL;
-            while ((iter2 = lys_getnext(iter2, iter, NULL, LYS_GETNEXT_NOSTATECHECK))) {
+            while ((iter2 = lys_getnext(iter2, iter, NULL, 0))) {
                 if (CHECK_NODE(iter2, exclude, name)) {
                     goto error;
                 }
@@ -2037,7 +2062,7 @@
     struct lysp_node *child_p, *dev_pnode = NULL, *dev_input_p = NULL, *dev_output_p = NULL;
     struct lysp_action_inout *inout_p;
     LY_ARRAY_COUNT_TYPE u;
-    ly_bool not_supported;
+    ly_bool not_supported, enabled;
     uint32_t opt_prev = ctx->options;
 
     lysc_update_path(ctx, parent, action_p->name);
@@ -2046,7 +2071,8 @@
     LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)action_p, parent, &dev_pnode, &not_supported));
     if (not_supported) {
         lysc_update_path(ctx, NULL, NULL);
-        return LY_EDENIED;
+        ret = LY_EDENIED;
+        goto cleanup;
     } else if (dev_pnode) {
         action_p = (struct lysp_action *)dev_pnode;
     }
@@ -2056,35 +2082,43 @@
     action->module = ctx->cur_mod;
     action->parent = parent;
 
-    LY_CHECK_RET(lys_compile_node_uniqness(ctx, parent, action_p->name, (struct lysc_node *)action));
+    LY_CHECK_GOTO(ret = lys_compile_node_uniqness(ctx, parent, action_p->name, (struct lysc_node *)action), cleanup);
 
     if (ctx->options & (LYS_COMPILE_RPC_MASK | LYS_COMPILE_NOTIFICATION)) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                 "Action \"%s\" is placed inside %s.", action_p->name,
                 ctx->options & LYS_COMPILE_RPC_MASK ? "another RPC/action" : "notification");
-        return LY_EVALID;
+        ret = LY_EVALID;
+        goto cleanup;
     }
 
     action->flags = action_p->flags & LYS_FLAGS_COMPILED_MASK;
 
+    /* if-features */
+    LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, action_p->iffeatures, &enabled));
+    if (!enabled && !(ctx->options & LYS_COMPILE_DISABLED)) {
+        ly_set_add(&ctx->disabled, action, 1, NULL);
+        ctx->options |= LYS_COMPILE_DISABLED;
+    }
+
     /* status - it is not inherited by specification, but it does not make sense to have
      * current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
-    LY_CHECK_RET(lys_compile_status(ctx, &action->flags, uses_status ? uses_status : (parent ? parent->flags : 0)));
+    ret = lys_compile_status(ctx, &action->flags, uses_status ? uses_status : (parent ? parent->flags : 0));
+    LY_CHECK_GOTO(ret, cleanup);
 
     DUP_STRING_GOTO(ctx->ctx, action_p->name, action->name, ret, cleanup);
     DUP_STRING_GOTO(ctx->ctx, action_p->dsc, action->dsc, ret, cleanup);
     DUP_STRING_GOTO(ctx->ctx, action_p->ref, action->ref, ret, cleanup);
-    COMPILE_ARRAY_GOTO(ctx, action_p->iffeatures, action->iffeatures, u, lys_compile_iffeature, ret, cleanup);
 
     /* connect any action augments */
-    LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)action));
+    LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)action), cleanup);
 
     /* input */
     lysc_update_path(ctx, (struct lysc_node *)action, "input");
 
     /* apply deviations on input */
-    LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->input, (struct lysc_node *)action,
-            &dev_input_p, &not_supported));
+    LY_CHECK_GOTO(ret = lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->input,
+            (struct lysc_node *)action, &dev_input_p, &not_supported), cleanup);
     if (not_supported) {
         inout_p = NULL;
     } else if (dev_input_p) {
@@ -2100,10 +2134,10 @@
         ctx->options |= LYS_COMPILE_RPC_INPUT;
 
         /* connect any input augments */
-        LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)&action->input));
+        LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)&action->input), cleanup);
 
         LY_LIST_FOR(inout_p->data, child_p) {
-            LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL));
+            LY_CHECK_GOTO(ret = lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL), cleanup);
         }
         ctx->options = opt_prev;
     }
@@ -2114,8 +2148,8 @@
     lysc_update_path(ctx, (struct lysc_node *)action, "output");
 
     /* apply deviations on output */
-    LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->output, (struct lysc_node *)action,
-            &dev_output_p, &not_supported));
+    LY_CHECK_GOTO(ret = lys_compile_node_deviations_refines(ctx, (struct lysp_node *)&action_p->output,
+            (struct lysc_node *)action, &dev_output_p, &not_supported), cleanup);
     if (not_supported) {
         inout_p = NULL;
     } else if (dev_output_p) {
@@ -2131,10 +2165,10 @@
         ctx->options |= LYS_COMPILE_RPC_OUTPUT;
 
         /* connect any output augments */
-        LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)&action->output));
+        LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)&action->output), cleanup);
 
         LY_LIST_FOR(inout_p->data, child_p) {
-            LY_CHECK_RET(lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL));
+            LY_CHECK_GOTO(ret = lys_compile_node(ctx, child_p, (struct lysc_node *)action, uses_status, NULL), cleanup);
         }
         ctx->options = opt_prev;
     }
@@ -2144,7 +2178,7 @@
     /* wait with extensions compilation until all the children are compiled */
     COMPILE_EXTS_GOTO(ctx, action_p->exts, action->exts, action, LYEXT_PAR_NODE, ret, cleanup);
 
-    if ((action->input.musts || action->output.musts) && !(ctx->options & LYS_COMPILE_GROUPING)) {
+    if ((action->input.musts || action->output.musts) && !(ctx->options & (LYS_COMPILE_DISABLED | LYS_COMPILE_GROUPING))) {
         /* do not check "must" semantics in a grouping */
         ret = ly_set_add(&ctx->xpath, action, 0, NULL);
         LY_CHECK_GOTO(ret, cleanup);
@@ -2167,7 +2201,7 @@
     LY_ERR ret = LY_SUCCESS;
     struct lysp_node *child_p, *dev_pnode = NULL;
     LY_ARRAY_COUNT_TYPE u;
-    ly_bool not_supported;
+    ly_bool not_supported, enabled;
     uint32_t opt_prev = ctx->options;
 
     lysc_update_path(ctx, parent, notif_p->name);
@@ -2175,7 +2209,8 @@
     LY_CHECK_RET(lys_compile_node_deviations_refines(ctx, (struct lysp_node *)notif_p, parent, &dev_pnode, &not_supported));
     if (not_supported) {
         lysc_update_path(ctx, NULL, NULL);
-        return LY_EDENIED;
+        ret = LY_EDENIED;
+        goto cleanup;
     } else if (dev_pnode) {
         notif_p = (struct lysp_notif *)dev_pnode;
     }
@@ -2185,17 +2220,25 @@
     notif->module = ctx->cur_mod;
     notif->parent = parent;
 
-    LY_CHECK_RET(lys_compile_node_uniqness(ctx, parent, notif_p->name, (struct lysc_node *)notif));
+    LY_CHECK_GOTO(ret = lys_compile_node_uniqness(ctx, parent, notif_p->name, (struct lysc_node *)notif), cleanup);
 
     if (ctx->options & (LYS_COMPILE_RPC_MASK | LYS_COMPILE_NOTIFICATION)) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                 "Notification \"%s\" is placed inside %s.", notif_p->name,
                 ctx->options & LYS_COMPILE_RPC_MASK ? "RPC/action" : "another notification");
-        return LY_EVALID;
+        ret = LY_EVALID;
+        goto cleanup;
     }
 
     notif->flags = notif_p->flags & LYS_FLAGS_COMPILED_MASK;
 
+    /* if-features */
+    LY_CHECK_RET(lys_eval_iffeatures(ctx->ctx, notif_p->iffeatures, &enabled));
+    if (!enabled && !(ctx->options & LYS_COMPILE_DISABLED)) {
+        ly_set_add(&ctx->disabled, notif, 1, NULL);
+        ctx->options |= LYS_COMPILE_DISABLED;
+    }
+
     /* status - it is not inherited by specification, but it does not make sense to have
      * current in deprecated or deprecated in obsolete, so we do print warning and inherit status */
     ret = lys_compile_status(ctx, &notif->flags, uses_status ? uses_status : (parent ? parent->flags : 0));
@@ -2204,9 +2247,8 @@
     DUP_STRING_GOTO(ctx->ctx, notif_p->name, notif->name, ret, cleanup);
     DUP_STRING_GOTO(ctx->ctx, notif_p->dsc, notif->dsc, ret, cleanup);
     DUP_STRING_GOTO(ctx->ctx, notif_p->ref, notif->ref, ret, cleanup);
-    COMPILE_ARRAY_GOTO(ctx, notif_p->iffeatures, notif->iffeatures, u, lys_compile_iffeature, ret, cleanup);
     COMPILE_ARRAY_GOTO(ctx, notif_p->musts, notif->musts, u, lys_compile_must, ret, cleanup);
-    if (notif_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+    if (notif_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
         /* do not check "must" semantics in a grouping */
         ret = ly_set_add(&ctx->xpath, notif, 0, NULL);
         LY_CHECK_GOTO(ret, cleanup);
@@ -2215,7 +2257,7 @@
     ctx->options |= LYS_COMPILE_NOTIFICATION;
 
     /* connect any notification augments */
-    LY_CHECK_RET(lys_compile_node_augments(ctx, (struct lysc_node *)notif));
+    LY_CHECK_GOTO(ret = lys_compile_node_augments(ctx, (struct lysc_node *)notif), cleanup);
 
     LY_LIST_FOR(notif_p->data, child_p) {
         ret = lys_compile_node(ctx, child_p, (struct lysc_node *)notif, uses_status, NULL);
@@ -2287,7 +2329,7 @@
     }
 
     COMPILE_ARRAY_GOTO(ctx, cont_p->musts, cont->musts, u, lys_compile_must, ret, done);
-    if (cont_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+    if (cont_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
         /* do not check "must" semantics in a grouping */
         ret = ly_set_add(&ctx->xpath, cont, 0, NULL);
         LY_CHECK_GOTO(ret, done);
@@ -2321,10 +2363,10 @@
         LY_CHECK_RET(lysc_unres_leaf_dflt_add(ctx, leaf, dflt));
     }
 
-    if (leaf->type->basetype == LY_TYPE_LEAFREF) {
+    if ((leaf->type->basetype == LY_TYPE_LEAFREF) && !(ctx->options & LYS_COMPILE_DISABLED)) {
         /* store to validate the path in the current context at the end of schema compiling when all the nodes are present */
         LY_CHECK_RET(ly_set_add(&ctx->leafrefs, leaf, 0, NULL));
-    } else if (leaf->type->basetype == LY_TYPE_UNION) {
+    } else if ((leaf->type->basetype == LY_TYPE_UNION) && !(ctx->options & LYS_COMPILE_DISABLED)) {
         LY_ARRAY_COUNT_TYPE u;
         LY_ARRAY_FOR(((struct lysc_type_union *)leaf->type)->types, u) {
             if (((struct lysc_type_union *)leaf->type)->types[u]->basetype == LY_TYPE_LEAFREF) {
@@ -2360,7 +2402,7 @@
     LY_ERR ret = LY_SUCCESS;
 
     COMPILE_ARRAY_GOTO(ctx, leaf_p->musts, leaf->musts, u, lys_compile_must, ret, done);
-    if (leaf_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+    if (leaf_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
         /* do not check "must" semantics in a grouping */
         ret = ly_set_add(&ctx->xpath, leaf, 0, NULL);
         LY_CHECK_GOTO(ret, done);
@@ -2408,7 +2450,7 @@
     LY_ERR ret = LY_SUCCESS;
 
     COMPILE_ARRAY_GOTO(ctx, llist_p->musts, llist->musts, u, lys_compile_must, ret, done);
-    if (llist_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+    if (llist_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
         /* do not check "must" semantics in a grouping */
         ret = ly_set_add(&ctx->xpath, llist, 0, NULL);
         LY_CHECK_GOTO(ret, done);
@@ -2550,7 +2592,7 @@
             }
         } else {
             ctx_node = lys_find_child(ctx_node, mod, name, name_len, 0,
-                    getnext_extra_flag | LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
+                    getnext_extra_flag | LYS_GETNEXT_WITHCHOICE | LYS_GETNEXT_WITHCASE);
             getnext_extra_flag = 0;
         }
         if (!ctx_node) {
@@ -2714,7 +2756,7 @@
     }
 
     COMPILE_ARRAY_GOTO(ctx, list_p->musts, list->musts, u, lys_compile_must, ret, done);
-    if (list_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+    if (list_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
         /* do not check "must" semantics in a grouping */
         LY_CHECK_RET(ly_set_add(&ctx->xpath, list, 0, NULL));
     }
@@ -2743,17 +2785,15 @@
         }
 
         /* key node must be present */
-        key = (struct lysc_node_leaf *)lys_find_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE | LYS_GETNEXT_NOSTATECHECK);
-        if (!(key)) {
-            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE,
-                    "The list's key \"%.*s\" not found.", len, keystr);
+        key = (struct lysc_node_leaf *)lys_find_child(node, node->module, keystr, len, LYS_LEAF, LYS_GETNEXT_NOCHOICE);
+        if (!key) {
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_REFERENCE, "The list's key \"%.*s\" not found.", len, keystr);
             return LY_EVALID;
         }
         /* keys must be unique */
         if (key->flags & LYS_KEY) {
             /* the node was already marked as a key */
-            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                    "Duplicated key identifier \"%.*s\".", len, keystr);
+            LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS, "Duplicated key identifier \"%.*s\".", len, keystr);
             return LY_EVALID;
         }
 
@@ -2777,11 +2817,7 @@
                         "List's key must not have any \"when\" statement.");
                 return LY_EVALID;
             }
-            if (key->iffeatures) {
-                LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
-                        "List's key must not have any \"if-feature\" statement.");
-                return LY_EVALID;
-            }
+            /* TODO check key, it cannot have any if-features */
         }
 
         /* check status */
@@ -2898,7 +2934,7 @@
         mod = node->module;
     }
 
-    ch->dflt = (struct lysc_node_case *)lys_find_child(node, mod, name, 0, LYS_CASE, LYS_GETNEXT_NOSTATECHECK | LYS_GETNEXT_WITHCASE);
+    ch->dflt = (struct lysc_node_case *)lys_find_child(node, mod, name, 0, LYS_CASE, LYS_GETNEXT_WITHCASE);
     if (!ch->dflt) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_SEMANTICS,
                 "Default case \"%s\" not found.", dflt->str);
@@ -3008,7 +3044,7 @@
     LY_ERR ret = LY_SUCCESS;
 
     COMPILE_ARRAY_GOTO(ctx, any_p->musts, any->musts, u, lys_compile_must, ret, done);
-    if (any_p->musts && !(ctx->options & LYS_COMPILE_GROUPING)) {
+    if (any_p->musts && !(ctx->options & (LYS_COMPILE_GROUPING | LYS_COMPILE_DISABLED))) {
         /* do not check "must" semantics in a grouping */
         ret = ly_set_add(&ctx->xpath, any, 0, NULL);
         LY_CHECK_GOTO(ret, done);
@@ -3511,7 +3547,7 @@
         .parent = NULL, .next = NULL,
         .prev = (struct lysc_node *)&fake_container,
         .name = "fake",
-        .dsc = NULL, .ref = NULL, .exts = NULL, .iffeatures = NULL, .when = NULL,
+        .dsc = NULL, .ref = NULL, .exts = NULL, .when = NULL,
         .child = NULL, .musts = NULL, .actions = NULL, .notifs = NULL
     };
 
@@ -3603,8 +3639,8 @@
     LY_ERR ret = LY_SUCCESS;
     struct lysc_node *node = NULL;
     struct lysp_node *dev_pnode = NULL;
-    LY_ARRAY_COUNT_TYPE u;
-    ly_bool not_supported;
+    ly_bool not_supported, enabled;
+    uint32_t prev_opts = ctx->options;
 
     LY_ERR (*node_compile_spec)(struct lysc_ctx *, struct lysp_node *, struct lysc_node *);
 
@@ -3657,12 +3693,11 @@
     LY_CHECK_ERR_RET(!node, LOGMEM(ctx->ctx), LY_EMEM);
 
     /* compile any deviations for this node */
-    LY_CHECK_ERR_RET(ret = lys_compile_node_deviations_refines(ctx, pnode, parent, &dev_pnode, &not_supported),
-            free(node), ret);
+    LY_CHECK_ERR_GOTO(ret = lys_compile_node_deviations_refines(ctx, pnode, parent, &dev_pnode, &not_supported),
+            free(node), cleanup);
     if (not_supported) {
         free(node);
-        lysc_update_path(ctx, NULL, NULL);
-        return LY_SUCCESS;
+        goto cleanup;
     } else if (dev_pnode) {
         pnode = dev_pnode;
     }
@@ -3672,6 +3707,13 @@
     node->prev = node;
     node->flags = pnode->flags & LYS_FLAGS_COMPILED_MASK;
 
+    /* if-features */
+    LY_CHECK_ERR_RET(ret = lys_eval_iffeatures(ctx->ctx, pnode->iffeatures, &enabled), free(node), ret);
+    if (!enabled && !(ctx->options & LYS_COMPILE_DISABLED)) {
+        ly_set_add(&ctx->disabled, node, 1, NULL);
+        ctx->options |= LYS_COMPILE_DISABLED;
+    }
+
     /* config */
     ret = lys_compile_config(ctx, node, parent);
     LY_CHECK_GOTO(ret, error);
@@ -3697,7 +3739,6 @@
     DUP_STRING_GOTO(ctx->ctx, pnode->name, node->name, ret, error);
     DUP_STRING_GOTO(ctx->ctx, pnode->dsc, node->dsc, ret, error);
     DUP_STRING_GOTO(ctx->ctx, pnode->ref, node->ref, ret, error);
-    COMPILE_ARRAY_GOTO(ctx, pnode->iffeatures, node->iffeatures, u, lys_compile_iffeature, ret, error);
 
     /* insert into parent's children/compiled module (we can no longer free the node separately on error) */
     LY_CHECK_GOTO(ret = lys_compile_node_connect(ctx, parent, node), cleanup);
@@ -3726,16 +3767,16 @@
         LY_CHECK_GOTO(ret = ly_set_add(child_set, node, 1, NULL), cleanup);
     }
 
-    lysc_update_path(ctx, NULL, NULL);
-    lysp_dev_node_free(ctx->ctx, dev_pnode);
-    return LY_SUCCESS;
+    goto cleanup;
 
 error:
-    lysc_node_free(ctx->ctx, node);
+    lysc_node_free(ctx->ctx, node, 0);
 cleanup:
-    if (dev_pnode) {
+    ctx->options = prev_opts;
+    if (ret && dev_pnode) {
         LOGVAL(ctx->ctx, LY_VLOG_STR, ctx->path, LYVE_OTHER, "Compilation of a deviated and/or refined node failed.");
-        lysp_dev_node_free(ctx->ctx, dev_pnode);
     }
+    lysp_dev_node_free(ctx->ctx, dev_pnode);
+    lysc_update_path(ctx, NULL, NULL);
     return ret;
 }
diff --git a/src/schema_features.c b/src/schema_features.c
new file mode 100644
index 0000000..4e1b6aa
--- /dev/null
+++ b/src/schema_features.c
@@ -0,0 +1,657 @@
+/**
+ * @file schema_features.c
+ * @author Radek Krejci <rkrejci@cesnet.cz>
+ * @brief Schema feature handling
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#define _GNU_SOURCE
+
+#include "schema_features.h"
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "compat.h"
+#include "context.h"
+#include "dict.h"
+#include "log.h"
+#include "in_internal.h"
+#include "parser_internal.h"
+#include "parser_schema.h"
+#include "path.h"
+#include "schema_compile.h"
+#include "schema_compile_amend.h"
+#include "set.h"
+#include "tree.h"
+#include "tree_schema_internal.h"
+#include "xpath.h"
+
+uint8_t
+lysc_iff_getop(uint8_t *list, size_t pos)
+{
+    uint8_t *item;
+    uint8_t mask = 3, result;
+
+    item = &list[pos / 4];
+    result = (*item) & (mask << 2 * (pos % 4));
+    return result >> 2 * (pos % 4);
+}
+
+static LY_ERR
+lysc_iffeature_value_(const struct lysc_iffeature *iff, size_t *index_e, size_t *index_f)
+{
+    uint8_t op;
+    LY_ERR a, b;
+
+    op = lysc_iff_getop(iff->expr, *index_e);
+    (*index_e)++;
+
+    switch (op) {
+    case LYS_IFF_F:
+        /* resolve feature */
+        return (iff->features[(*index_f)++]->flags & LYS_FENABLED) ? LY_SUCCESS : LY_ENOT;
+    case LYS_IFF_NOT:
+        /* invert result */
+        return lysc_iffeature_value_(iff, index_e, index_f) == LY_SUCCESS ? LY_ENOT : LY_SUCCESS;
+    case LYS_IFF_AND:
+    case LYS_IFF_OR:
+        a = lysc_iffeature_value_(iff, index_e, index_f);
+        b = lysc_iffeature_value_(iff, index_e, index_f);
+        if (op == LYS_IFF_AND) {
+            if ((a == LY_SUCCESS) && (b == LY_SUCCESS)) {
+                return LY_SUCCESS;
+            } else {
+                return LY_ENOT;
+            }
+        } else { /* LYS_IFF_OR */
+            if ((a == LY_SUCCESS) || (b == LY_SUCCESS)) {
+                return LY_SUCCESS;
+            } else {
+                return LY_ENOT;
+            }
+        }
+    }
+
+    return LY_ENOT;
+}
+
+API LY_ERR
+lysc_iffeature_value(const struct lysc_iffeature *iff)
+{
+    size_t index_e = 0, index_f = 0;
+
+    LY_CHECK_ARG_RET(NULL, iff, LY_EINVAL);
+
+    if (iff->expr) {
+        return lysc_iffeature_value_(iff, &index_e, &index_f);
+    }
+    return LY_ENOT;
+}
+
+API struct lysp_feature *
+lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod, uint32_t *idx)
+{
+    struct lysp_feature *features;
+
+    if (!*idx) {
+        /* module features */
+        features = pmod->features;
+    } else if (pmod->includes && ((*idx - 1) < LY_ARRAY_COUNT(pmod->includes))) {
+        /* submodule features */
+        features = pmod->includes[*idx - 1].submodule->features;
+    } else {
+        /* no more features */
+        return NULL;
+    }
+
+    /* get the next feature */
+    if (features && (!last || (&features[LY_ARRAY_COUNT(features) - 1] != last))) {
+        return !last ? &features[0] : (struct lysp_feature *)last + 1;
+    }
+
+    /* no more features in current (sub)module */
+    ++(*idx);
+    return lysp_feature_next(NULL, pmod, idx);
+}
+
+/**
+ * @brief Find a feature of the given name and referenced in the given module.
+ *
+ * @param[in] pmod Module where the feature was referenced (used to resolve prefix of the feature).
+ * @param[in] name Name of the feature including possible prefix.
+ * @param[in] len Length of the string representing the feature identifier in the name variable (mandatory!).
+ * @param[in] prefixed Whether the feature name can be prefixed.
+ * @return Pointer to the feature structure if found, NULL otherwise.
+ */
+static struct lysp_feature *
+lysp_feature_find(const struct lysp_module *pmod, const char *name, size_t len, ly_bool prefixed)
+{
+    const struct lys_module *mod;
+    const char *ptr;
+    struct lysp_feature *f = NULL;
+    uint32_t idx = 0;
+
+    assert(pmod);
+
+    if (prefixed && (ptr = ly_strnchr(name, ':', len))) {
+        /* we have a prefixed feature */
+        mod = ly_resolve_prefix(pmod->mod->ctx, name, ptr - name, LY_PREF_SCHEMA, (void *)pmod);
+        LY_CHECK_RET(!mod, NULL);
+
+        pmod = mod->parsed;
+        len = len - (ptr - name) - 1;
+        name = ptr + 1;
+    }
+
+    /* we have the correct module, get the feature */
+    while ((f = lysp_feature_next(f, pmod, &idx))) {
+        if (!ly_strncmp(f->name, name, len)) {
+            return f;
+        }
+    }
+
+    return NULL;
+}
+
+API LY_ERR
+lys_feature_value(const struct lys_module *module, const char *feature)
+{
+    const struct lysp_feature *f;
+
+    LY_CHECK_ARG_RET(NULL, module, module->parsed, feature, LY_EINVAL);
+
+    /* search for the specified feature */
+    f = lysp_feature_find(module->parsed, feature, strlen(feature), 0);
+    LY_CHECK_RET(!f, LY_ENOTFOUND);
+
+    /* feature disabled */
+    if (!(f->flags & LYS_FENABLED)) {
+        return LY_ENOT;
+    }
+
+    /* feature enabled */
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Stack for processing if-feature expressions.
+ */
+struct iff_stack {
+    size_t size;    /**< number of items in the stack */
+    size_t index;   /**< first empty item */
+    uint8_t *stack; /**< stack - array of @ref ifftokens to create the if-feature expression in prefix format */
+};
+
+/**
+ * @brief Add @ref ifftokens into the stack.
+ * @param[in] stack The if-feature stack to use.
+ * @param[in] value One of the @ref ifftokens to store in the stack.
+ * @return LY_EMEM in case of memory allocation error
+ * @return LY_ESUCCESS if the value successfully stored.
+ */
+static LY_ERR
+iff_stack_push(struct iff_stack *stack, uint8_t value)
+{
+    if (stack->index == stack->size) {
+        stack->size += 4;
+        stack->stack = ly_realloc(stack->stack, stack->size * sizeof *stack->stack);
+        LY_CHECK_ERR_RET(!stack->stack, LOGMEM(NULL); stack->size = 0, LY_EMEM);
+    }
+    stack->stack[stack->index++] = value;
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Get (and remove) the last item form the stack.
+ * @param[in] stack The if-feature stack to use.
+ * @return The value from the top of the stack.
+ */
+static uint8_t
+iff_stack_pop(struct iff_stack *stack)
+{
+    assert(stack && stack->index);
+
+    stack->index--;
+    return stack->stack[stack->index];
+}
+
+/**
+ * @brief Clean up the stack.
+ * @param[in] stack The if-feature stack to use.
+ */
+static void
+iff_stack_clean(struct iff_stack *stack)
+{
+    stack->size = 0;
+    free(stack->stack);
+}
+
+/**
+ * @brief Store the @ref ifftokens (@p op) on the given position in the 2bits array
+ * (libyang format of the if-feature expression).
+ * @param[in,out] list The 2bits array to modify.
+ * @param[in] op The operand (@ref ifftokens) to store.
+ * @param[in] pos Position (0-based) where to store the given @p op.
+ */
+static void
+iff_setop(uint8_t *list, uint8_t op, size_t pos)
+{
+    uint8_t *item;
+    uint8_t mask = 3;
+
+    assert(op <= 3); /* max 2 bits */
+
+    item = &list[pos / 4];
+    mask = mask << 2 * (pos % 4);
+    *item = (*item) & ~mask;
+    *item = (*item) | (op << 2 * (pos % 4));
+}
+
+#define LYS_IFF_LP 0x04 /**< Additional, temporary, value of @ref ifftokens: ( */
+#define LYS_IFF_RP 0x08 /**< Additional, temporary, value of @ref ifftokens: ) */
+
+static LY_ERR
+lys_compile_iffeature(const struct ly_ctx *ctx, struct lysp_qname *qname, struct lysc_iffeature *iff)
+{
+    LY_ERR rc = LY_SUCCESS;
+    const char *c = qname->str;
+    int64_t i, j;
+    int8_t op_len, last_not = 0, checkversion = 0;
+    LY_ARRAY_COUNT_TYPE f_size = 0, expr_size = 0, f_exp = 1;
+    uint8_t op;
+    struct iff_stack stack = {0, 0, NULL};
+    struct lysp_feature *f;
+
+    assert(c);
+
+    /* pre-parse the expression to get sizes for arrays, also do some syntax checks of the expression */
+    for (i = j = 0; c[i]; i++) {
+        if (c[i] == '(') {
+            j++;
+            checkversion = 1;
+            continue;
+        } else if (c[i] == ')') {
+            j--;
+            continue;
+        } else if (isspace(c[i])) {
+            checkversion = 1;
+            continue;
+        }
+
+        if (!strncmp(&c[i], "not", op_len = 3) || !strncmp(&c[i], "and", op_len = 3) || !strncmp(&c[i], "or", op_len = 2)) {
+            uint64_t spaces;
+            for (spaces = 0; c[i + op_len + spaces] && isspace(c[i + op_len + spaces]); spaces++) {}
+            if (c[i + op_len + spaces] == '\0') {
+                LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+                        "Invalid value \"%s\" of if-feature - unexpected end of expression.", qname->str);
+                return LY_EVALID;
+            } else if (!isspace(c[i + op_len])) {
+                /* feature name starting with the not/and/or */
+                last_not = 0;
+                f_size++;
+            } else if (c[i] == 'n') { /* not operation */
+                if (last_not) {
+                    /* double not */
+                    expr_size = expr_size - 2;
+                    last_not = 0;
+                } else {
+                    last_not = 1;
+                }
+            } else { /* and, or */
+                if (f_exp != f_size) {
+                    LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+                            "Invalid value \"%s\" of if-feature - missing feature/expression before \"%.*s\" operation.",
+                            qname->str, op_len, &c[i]);
+                    return LY_EVALID;
+                }
+                f_exp++;
+
+                /* not a not operation */
+                last_not = 0;
+            }
+            i += op_len;
+        } else {
+            f_size++;
+            last_not = 0;
+        }
+        expr_size++;
+
+        while (!isspace(c[i])) {
+            if (!c[i] || (c[i] == ')') || (c[i] == '(')) {
+                i--;
+                break;
+            }
+            i++;
+        }
+    }
+    if (j) {
+        /* not matching count of ( and ) */
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+                "Invalid value \"%s\" of if-feature - non-matching opening and closing parentheses.", qname->str);
+        return LY_EVALID;
+    }
+    if (f_exp != f_size) {
+        /* features do not match the needed arguments for the logical operations */
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+                "Invalid value \"%s\" of if-feature - number of features in expression does not match "
+                "the required number of operands for the operations.", qname->str);
+        return LY_EVALID;
+    }
+
+    if (checkversion || (expr_size > 1)) {
+        /* check that we have 1.1 module */
+        if (qname->mod->version != LYS_VERSION_1_1) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+                    "Invalid value \"%s\" of if-feature - YANG 1.1 expression in YANG 1.0 module.", qname->str);
+            return LY_EVALID;
+        }
+    }
+
+    /* allocate the memory */
+    LY_ARRAY_CREATE_RET(ctx, iff->features, f_size, LY_EMEM);
+    iff->expr = calloc((j = (expr_size / 4) + ((expr_size % 4) ? 1 : 0)), sizeof *iff->expr);
+    stack.stack = malloc(expr_size * sizeof *stack.stack);
+    LY_CHECK_ERR_GOTO(!stack.stack || !iff->expr, LOGMEM(ctx); rc = LY_EMEM, error);
+
+    stack.size = expr_size;
+    f_size--; expr_size--; /* used as indexes from now */
+
+    for (i--; i >= 0; i--) {
+        if (c[i] == ')') {
+            /* push it on stack */
+            iff_stack_push(&stack, LYS_IFF_RP);
+            continue;
+        } else if (c[i] == '(') {
+            /* pop from the stack into result all operators until ) */
+            while ((op = iff_stack_pop(&stack)) != LYS_IFF_RP) {
+                iff_setop(iff->expr, op, expr_size--);
+            }
+            continue;
+        } else if (isspace(c[i])) {
+            continue;
+        }
+
+        /* end of operator or operand -> find beginning and get what is it */
+        j = i + 1;
+        while (i >= 0 && !isspace(c[i]) && c[i] != '(') {
+            i--;
+        }
+        i++; /* go back by one step */
+
+        if (!strncmp(&c[i], "not", 3) && isspace(c[i + 3])) {
+            if (stack.index && (stack.stack[stack.index - 1] == LYS_IFF_NOT)) {
+                /* double not */
+                iff_stack_pop(&stack);
+            } else {
+                /* not has the highest priority, so do not pop from the stack
+                 * as in case of AND and OR */
+                iff_stack_push(&stack, LYS_IFF_NOT);
+            }
+        } else if (!strncmp(&c[i], "and", 3) && isspace(c[i + 3])) {
+            /* as for OR - pop from the stack all operators with the same or higher
+             * priority and store them to the result, then push the AND to the stack */
+            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_AND) {
+                op = iff_stack_pop(&stack);
+                iff_setop(iff->expr, op, expr_size--);
+            }
+            iff_stack_push(&stack, LYS_IFF_AND);
+        } else if (!strncmp(&c[i], "or", 2) && isspace(c[i + 2])) {
+            while (stack.index && stack.stack[stack.index - 1] <= LYS_IFF_OR) {
+                op = iff_stack_pop(&stack);
+                iff_setop(iff->expr, op, expr_size--);
+            }
+            iff_stack_push(&stack, LYS_IFF_OR);
+        } else {
+            /* feature name, length is j - i */
+
+            /* add it to the expression */
+            iff_setop(iff->expr, LYS_IFF_F, expr_size--);
+
+            /* now get the link to the feature definition */
+            f = lysp_feature_find(qname->mod, &c[i], j - i, 1);
+            if (!f) {
+                LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+                        "Invalid value \"%s\" of if-feature - unable to find feature \"%.*s\".", qname->str, j - i, &c[i]);
+                rc = LY_EVALID;
+                goto error;
+            }
+            iff->features[f_size] = f;
+            LY_ARRAY_INCREMENT(iff->features);
+            f_size--;
+        }
+    }
+    while (stack.index) {
+        op = iff_stack_pop(&stack);
+        iff_setop(iff->expr, op, expr_size--);
+    }
+
+    if (++expr_size || ++f_size) {
+        /* not all expected operators and operands found */
+        LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_SYNTAX_YANG,
+                "Invalid value \"%s\" of if-feature - processing error.", qname->str);
+        rc = LY_EINT;
+    } else {
+        rc = LY_SUCCESS;
+    }
+
+error:
+    /* cleanup */
+    iff_stack_clean(&stack);
+
+    return rc;
+}
+
+LY_ERR
+lys_eval_iffeatures(const struct ly_ctx *ctx, struct lysp_qname *iffeatures, ly_bool *enabled)
+{
+    LY_ERR ret;
+    struct lysc_iffeature iff = {0};
+
+    if (!iffeatures) {
+        *enabled = 1;
+        return LY_SUCCESS;
+    }
+
+    LY_CHECK_RET(lys_compile_iffeature(ctx, iffeatures, &iff));
+
+    ret = lysc_iffeature_value(&iff);
+    lysc_iffeature_free((struct ly_ctx *)ctx, &iff);
+    if (ret == LY_ENOT) {
+        *enabled = 0;
+    } else if (ret) {
+        return ret;
+    } else {
+        *enabled = 1;
+    }
+
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lys_enable_features(struct lysp_module *pmod, const char **features)
+{
+    uint32_t i;
+    struct lysp_feature *f;
+    LY_ERR ret;
+
+    if (!features) {
+        /* keep all features disabled */
+        return LY_SUCCESS;
+    }
+
+    if (!strcmp(features[0], "*")) {
+        /* enable all features */
+        f = NULL;
+        i = 0;
+        while ((f = lysp_feature_next(f, pmod, &i))) {
+            f->flags |= LYS_FENABLED;
+        }
+    } else {
+        /* enable selected features */
+        for (i = 0; features[i]; ++i) {
+            /* find the feature */
+            f = lysp_feature_find(pmod, features[i], strlen(features[i]), 0);
+            if (!f) {
+                LOGERR(pmod->mod->ctx, LY_ENOTFOUND, "Feature \"%s\" not found in module \"%s\".", features[i],
+                        pmod->mod->name);
+                return LY_ENOTFOUND;
+            }
+
+            /* enable feature */
+            f->flags |= LYS_FENABLED;
+        }
+    }
+
+check_iffeature:
+    /* check whether all enabled features have their if-features satisfied */
+    f = NULL;
+    i = 0;
+    while ((f = lysp_feature_next(f, pmod, &i))) {
+        if (!(f->flags & LYS_FENABLED) || !f->iffeatures) {
+            /* disabled feature or no if-features to check */
+            continue;
+        }
+
+        assert(f->iffeatures_c);
+        ret = lysc_iffeature_value(f->iffeatures_c);
+        if (ret == LY_ENOT) {
+            LOGWRN(pmod->mod->ctx, "Feature \"%s\" cannot be enabled because its \"if-feature\" is not satisfied.",
+                    f->name);
+
+            /* disable feature and re-evaluate all the feature if-features again */
+            f->flags &= ~LYS_FENABLED;
+            goto check_iffeature;
+        } else if (ret) {
+            return ret;
+        } /* else if-feature satisfied */
+    }
+
+    return LY_SUCCESS;
+}
+
+/**
+ * @brief Check circular dependency of features - feature MUST NOT reference itself (via their if-feature statement).
+ *
+ * The function works in the same way as lys_compile_identity_circular_check() with different structures and error messages.
+ *
+ * @param[in] ctx Compile context for logging.
+ * @param[in] feature The feature referenced in if-feature statement (its depfeatures list is being extended by the feature
+ *            being currently processed).
+ * @param[in] depfeatures The list of depending features of the feature being currently processed (not the one provided as @p feature)
+ * @return LY_SUCCESS if everything is ok.
+ * @return LY_EVALID if the feature references indirectly itself.
+ */
+static LY_ERR
+lys_compile_feature_circular_check(const struct ly_ctx *ctx, struct lysp_feature *feature, struct lysp_feature **depfeatures)
+{
+    LY_ERR ret = LY_SUCCESS;
+    LY_ARRAY_COUNT_TYPE u, v;
+    struct ly_set recursion = {0};
+    struct lysp_feature *drv;
+
+    if (!depfeatures) {
+        return LY_SUCCESS;
+    }
+
+    for (u = 0; u < LY_ARRAY_COUNT(depfeatures); ++u) {
+        if (feature == depfeatures[u]) {
+            LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.",
+                    feature->name);
+            ret = LY_EVALID;
+            goto cleanup;
+        }
+        ret = ly_set_add(&recursion, depfeatures[u], 0, NULL);
+        LY_CHECK_GOTO(ret, cleanup);
+    }
+
+    for (v = 0; v < recursion.count; ++v) {
+        drv = recursion.objs[v];
+        if (!drv->depfeatures) {
+            continue;
+        }
+        for (u = 0; u < LY_ARRAY_COUNT(drv->depfeatures); ++u) {
+            if (feature == drv->depfeatures[u]) {
+                LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Feature \"%s\" is indirectly referenced from itself.",
+                        feature->name);
+                ret = LY_EVALID;
+                goto cleanup;
+            }
+            ly_set_add(&recursion, drv->depfeatures[u], 0, NULL);
+            LY_CHECK_GOTO(ret, cleanup);
+        }
+    }
+
+cleanup:
+    ly_set_erase(&recursion, NULL);
+    return ret;
+}
+
+LY_ERR
+lys_compile_feature_iffeatures(struct lysp_module *pmod)
+{
+    LY_ARRAY_COUNT_TYPE u, v;
+    struct lysp_feature *f = NULL, **df;
+    uint32_t idx = 0;
+
+    while ((f = lysp_feature_next(f, pmod, &idx))) {
+        if (!f->iffeatures) {
+            continue;
+        }
+
+        /* compile if-features */
+        LY_ARRAY_CREATE_RET(pmod->mod->ctx, f->iffeatures_c, LY_ARRAY_COUNT(f->iffeatures), LY_EMEM);
+        LY_ARRAY_FOR(f->iffeatures, u) {
+            LY_ARRAY_INCREMENT(f->iffeatures_c);
+            LY_CHECK_RET(lys_compile_iffeature(pmod->mod->ctx, &(f->iffeatures)[u], &(f->iffeatures_c)[u]));
+        }
+        LY_ARRAY_FOR(f->iffeatures_c, u) {
+            LY_ARRAY_FOR(f->iffeatures_c[u].features, v) {
+                /* check for circular dependency - direct reference first,... */
+                if (f == f->iffeatures_c[u].features[v]) {
+                    LOGVAL(pmod->mod->ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Feature \"%s\" is referenced from itself.",
+                            f->name);
+                    return LY_EVALID;
+                }
+                /* ... and indirect circular reference */
+                LY_CHECK_RET(lys_compile_feature_circular_check(pmod->mod->ctx, f->iffeatures_c[u].features[v], f->depfeatures));
+
+                /* add itself into the dependants list */
+                LY_ARRAY_NEW_RET(pmod->mod->ctx, f->iffeatures_c[u].features[v]->depfeatures, df, LY_EMEM);
+                *df = f;
+            }
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+void
+lys_free_feature_iffeatures(struct lysp_module *pmod)
+{
+    struct lysp_feature *f = NULL;
+    uint32_t idx = 0;
+
+    while ((f = lysp_feature_next(f, pmod, &idx))) {
+        FREE_ARRAY(pmod->mod->ctx, f->iffeatures_c, lysc_iffeature_free);
+        f->iffeatures_c = NULL;
+        LY_ARRAY_FREE(f->depfeatures);
+        f->depfeatures = NULL;
+    }
+}
diff --git a/src/schema_features.h b/src/schema_features.h
new file mode 100644
index 0000000..e6da709
--- /dev/null
+++ b/src/schema_features.h
@@ -0,0 +1,61 @@
+/**
+ * @file schema_features.h
+ * @author Michal Vasko <mvasko@cesnet.cz>
+ * @brief Header for schema features.
+ *
+ * Copyright (c) 2015 - 2020 CESNET, z.s.p.o.
+ *
+ * This source code is licensed under BSD 3-Clause License (the "License").
+ * You may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://opensource.org/licenses/BSD-3-Clause
+ */
+
+#ifndef LY_SCHEMA_FEATURES_H_
+#define LY_SCHEMA_FEATURES_H_
+
+#include "log.h"
+
+struct lysp_module;
+struct lys_parser_ctx;
+struct lysp_qname;
+
+/**
+ * @brief Evaluate if-features array.
+ *
+ * @param[in] ctx libyang context.
+ * @param[in] iffeatures Sized array of if-features to evaluate.
+ * @param[out] enabled Whether if-features evaluated to true or false.
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_eval_iffeatures(const struct ly_ctx *ctx, struct lysp_qname *iffeatures, ly_bool *enabled);
+
+/**
+ * @brief Enable features in a parsed module with their consolidation and checking that they really
+ * can be enabled and have all their if-features true.
+ *
+ * @param[in] pmod Parsed module to modify.
+ * @param[in] features Array of features ended with NULL to enable. NULL for all features disabled, '*' for all enabled
+ * @return LY_SUCCESS on success.
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_enable_features(struct lysp_module *pmod, const char **features);
+
+/**
+ * @brief Compile if-features of features in the current module and all its submodules.
+ *
+ * @param[in] ctx Compile context.
+ * @return LY_ERR value.
+ */
+LY_ERR lys_compile_feature_iffeatures(struct lysp_module *pmod);
+
+/**
+ * @brief Free all auxiliary if-feature structures in a parsed module used for compilation.
+ *
+ * @param[in] pmod Module to update.
+ */
+void lys_free_feature_iffeatures(struct lysp_module *pmod);
+
+#endif /* LY_SCHEMA_FEATURES_H_ */
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 45312ec..03ca6f8 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -17,6 +17,7 @@
 #include "tree_schema.h"
 
 #include <assert.h>
+#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <stdint.h>
@@ -37,6 +38,7 @@
 #include "path.h"
 #include "schema_compile.h"
 #include "schema_compile_amend.h"
+#include "schema_features.h"
 #include "set.h"
 #include "tree.h"
 #include "tree_schema_internal.h"
@@ -264,13 +266,6 @@
         return NULL;
     }
 
-    if (!(options & LYS_GETNEXT_NOSTATECHECK)) {
-        /* check if the node is disabled by if-feature */
-        if (lysc_node_is_disabled(next, 0)) {
-            next = next->next;
-            goto repeat;
-        }
-    }
     return next;
 }
 
@@ -553,338 +548,6 @@
 }
 
 API LY_ERR
-lysc_feature_value(const struct lysc_feature *feature)
-{
-    LY_CHECK_ARG_RET(NULL, feature, LY_EINVAL);
-    return feature->flags & LYS_FENABLED ? LY_SUCCESS : LY_ENOT;
-}
-
-uint8_t
-lysc_iff_getop(uint8_t *list, size_t pos)
-{
-    uint8_t *item;
-    uint8_t mask = 3, result;
-
-    item = &list[pos / 4];
-    result = (*item) & (mask << 2 * (pos % 4));
-    return result >> 2 * (pos % 4);
-}
-
-static LY_ERR
-lysc_iffeature_value_(const struct lysc_iffeature *iff, size_t *index_e, size_t *index_f)
-{
-    uint8_t op;
-    LY_ERR a, b;
-
-    op = lysc_iff_getop(iff->expr, *index_e);
-    (*index_e)++;
-
-    switch (op) {
-    case LYS_IFF_F:
-        /* resolve feature */
-        return lysc_feature_value(iff->features[(*index_f)++]);
-    case LYS_IFF_NOT:
-        /* invert result */
-        return lysc_iffeature_value_(iff, index_e, index_f) == LY_SUCCESS ? LY_ENOT : LY_SUCCESS;
-    case LYS_IFF_AND:
-    case LYS_IFF_OR:
-        a = lysc_iffeature_value_(iff, index_e, index_f);
-        b = lysc_iffeature_value_(iff, index_e, index_f);
-        if (op == LYS_IFF_AND) {
-            if ((a == LY_SUCCESS) && (b == LY_SUCCESS)) {
-                return LY_SUCCESS;
-            } else {
-                return LY_ENOT;
-            }
-        } else { /* LYS_IFF_OR */
-            if ((a == LY_SUCCESS) || (b == LY_SUCCESS)) {
-                return LY_SUCCESS;
-            } else {
-                return LY_ENOT;
-            }
-        }
-    }
-
-    return 0;
-}
-
-API LY_ERR
-lysc_iffeature_value(const struct lysc_iffeature *iff)
-{
-    size_t index_e = 0, index_f = 0;
-
-    LY_CHECK_ARG_RET(NULL, iff, -1);
-
-    if (iff->expr) {
-        return lysc_iffeature_value_(iff, &index_e, &index_f);
-    }
-    return 0;
-}
-
-/**
- * @brief Enable/Disable the specified feature in the module.
- *
- * If the feature is already set to the desired value, LY_SUCCESS is returned.
- * By changing the feature, also all the feature which depends on it via their
- * if-feature statements are again evaluated (disabled if a if-feature statement
- * evaluates to false).
- *
- * @param[in] mod Module where to set (search for) the feature.
- * @param[in] name Name of the feature to set. Asterisk ('*') can be used to
- * set all the features in the module.
- * @param[in] value Desired value of the feature: 1 (enable) or 0 (disable).
- * @param[in] skip_checks Flag to skip checking of if-features and just set @p value of the feature.
- * @return LY_ERR value.
- */
-static LY_ERR
-lys_feature_change(const struct lys_module *mod, const char *name, ly_bool value, ly_bool skip_checks)
-{
-    LY_ERR ret = LY_SUCCESS;
-    ly_bool all = 0;
-    LY_ARRAY_COUNT_TYPE u, disabled_count;
-    uint32_t changed_count;
-    struct lysc_feature *f, **df;
-    struct lysc_iffeature *iff;
-    struct ly_set *changed;
-    struct ly_ctx *ctx = mod->ctx; /* shortcut */
-
-    if (!strcmp(name, "*")) {
-        /* enable all */
-        all = 1;
-    }
-
-    if (!mod->implemented) {
-        LOGERR(ctx, LY_EINVAL, "Module \"%s\" is not implemented so all its features are permanently disabled.", mod->name);
-        return LY_EINVAL;
-    }
-    if (!mod->features) {
-        if (all) {
-            /* no feature to enable */
-            return LY_SUCCESS;
-        }
-        LOGERR(ctx, LY_EINVAL, "Unable to switch feature since the module \"%s\" has no features.", mod->name);
-        return LY_ENOTFOUND;
-    }
-
-    LY_CHECK_RET(ly_set_new(&changed));
-    changed_count = 0;
-
-run:
-    for (disabled_count = u = 0; u < LY_ARRAY_COUNT(mod->features); ++u) {
-        f = &mod->features[u];
-        if (all || !strcmp(f->name, name)) {
-            if ((value && (f->flags & LYS_FENABLED)) || (!value && !(f->flags & LYS_FENABLED))) {
-                if (all) {
-                    /* skip already set features */
-                    continue;
-                } else {
-                    /* feature already set correctly */
-                    goto cleanup;
-                }
-            }
-
-            if (value) { /* enable */
-                if (!skip_checks) {
-                    /* check referenced features if they are enabled */
-                    LY_ARRAY_FOR(f->iffeatures, struct lysc_iffeature, iff) {
-                        if (lysc_iffeature_value(iff) == LY_ENOT) {
-                            if (all) {
-                                ++disabled_count;
-                                goto next;
-                            } else {
-                                LOGERR(ctx, LY_EDENIED, "Feature \"%s\" cannot be enabled since it is disabled by "
-                                        "its if-feature condition(s).", f->name);
-                                ret = LY_EDENIED;
-                                goto cleanup;
-                            }
-                        }
-                    }
-                }
-                /* enable the feature */
-                f->flags |= LYS_FENABLED;
-            } else { /* disable */
-                /* disable the feature */
-                f->flags &= ~LYS_FENABLED;
-            }
-
-            /* remember the changed feature */
-            ret = ly_set_add(changed, f, 1, NULL);
-            LY_CHECK_GOTO(ret, cleanup);
-
-            if (!all) {
-                /* stop in case changing a single feature */
-                break;
-            }
-        }
-next:
-        ;
-    }
-
-    if (!all && !changed->count) {
-        LOGERR(ctx, LY_EINVAL, "Feature \"%s\" not found in module \"%s\".", name, mod->name);
-        ret = LY_ENOTFOUND;
-        goto cleanup;
-    }
-
-    if (value && all && disabled_count) {
-        if (changed_count == changed->count) {
-            /* no change in last run -> not able to enable all ... */
-            /* ... print errors */
-            for (u = 0; disabled_count && u < LY_ARRAY_COUNT(mod->features); ++u) {
-                if (!(mod->features[u].flags & LYS_FENABLED)) {
-                    LOGERR(ctx, LY_EDENIED, "Feature \"%s\" cannot be enabled since it is disabled by its if-feature "
-                            "condition(s).", mod->features[u].name);
-                    --disabled_count;
-                }
-            }
-            /* ... restore the original state */
-            for (u = 0; u < changed->count; ++u) {
-                f = changed->objs[u];
-                /* re-disable the feature */
-                f->flags &= ~LYS_FENABLED;
-            }
-
-            ret = LY_EDENIED;
-            goto cleanup;
-        } else {
-            /* we did some change in last run, try it again */
-            changed_count = changed->count;
-            goto run;
-        }
-    }
-
-    /* reflect change(s) in the dependent features */
-    for (u = 0; !skip_checks && (u < changed->count); ++u) {
-        /* If a dependent feature is enabled, it can be now changed by the change (to false) of the value of
-         * its if-feature statements. The reverse logic, automatically enable feature when its feature is enabled
-         * is not done - by default, features are disabled and must be explicitely enabled. */
-        f = changed->objs[u];
-        LY_ARRAY_FOR(f->depfeatures, struct lysc_feature *, df) {
-            if (!((*df)->flags & LYS_FENABLED)) {
-                /* not enabled, nothing to do */
-                continue;
-            }
-            /* check the feature's if-features which could change by the previous change of our feature */
-            LY_ARRAY_FOR((*df)->iffeatures, struct lysc_iffeature, iff) {
-                if (lysc_iffeature_value(iff) == LY_ENOT) {
-                    /* the feature must be disabled now */
-                    (*df)->flags &= ~LYS_FENABLED;
-                    /* add the feature into the list of changed features */
-                    ret = ly_set_add(changed, *df, 1, NULL);
-                    LY_CHECK_GOTO(ret, cleanup);
-                    break;
-                }
-            }
-        }
-    }
-
-    /* success */
-    ++mod->ctx->module_set_id;
-
-cleanup:
-    ly_set_free(changed, NULL);
-    return ret;
-}
-
-API LY_ERR
-lys_feature_enable(const struct lys_module *module, const char *feature)
-{
-    LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
-    return lys_feature_change((struct lys_module *)module, feature, 1, 0);
-}
-
-API LY_ERR
-lys_feature_disable(const struct lys_module *module, const char *feature)
-{
-    LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
-    return lys_feature_change((struct lys_module *)module, feature, 0, 0);
-}
-
-API LY_ERR
-lys_feature_enable_force(const struct lys_module *module, const char *feature)
-{
-    LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
-    return lys_feature_change((struct lys_module *)module, feature, 1, 1);
-}
-
-API LY_ERR
-lys_feature_disable_force(const struct lys_module *module, const char *feature)
-{
-    LY_CHECK_ARG_RET(NULL, module, feature, LY_EINVAL);
-
-    return lys_feature_change((struct lys_module *)module, feature, 0, 1);
-}
-
-API LY_ERR
-lys_feature_value(const struct lys_module *module, const char *feature)
-{
-    struct lysc_feature *f = NULL;
-    LY_ARRAY_COUNT_TYPE u;
-
-    LY_CHECK_ARG_RET(NULL, module, module->compiled, feature, -1);
-
-    /* search for the specified feature */
-    LY_ARRAY_FOR(module->features, u) {
-        f = &module->features[u];
-        if (!strcmp(f->name, feature)) {
-            break;
-        }
-    }
-
-    /* feature definition not found */
-    if (!f) {
-        return LY_ENOTFOUND;
-    }
-
-    /* feature disabled */
-    if (!(f->flags & LYS_FENABLED)) {
-        return LY_ENOT;
-    }
-
-    /* check referenced features if they are enabled */
-    LY_ARRAY_FOR(f->iffeatures, u) {
-        if (lysc_iffeature_value(&f->iffeatures[u]) == LY_ENOT) {
-            /* if-feature disabled */
-            return LY_ENOT;
-        }
-    }
-
-    /* feature enabled */
-    return LY_SUCCESS;
-}
-
-API const struct lysc_node *
-lysc_node_is_disabled(const struct lysc_node *node, ly_bool recursive)
-{
-    LY_ARRAY_COUNT_TYPE u;
-
-    LY_CHECK_ARG_RET(NULL, node, NULL);
-
-    do {
-        if (node->iffeatures) {
-            /* check local if-features */
-            LY_ARRAY_FOR(node->iffeatures, u) {
-                if (lysc_iffeature_value(&node->iffeatures[u]) == LY_ENOT) {
-                    return node;
-                }
-            }
-        }
-
-        if (!recursive) {
-            return NULL;
-        }
-
-        /* go through schema-only parents */
-        node = node->parent;
-    } while (node && (node->nodetype & (LYS_CASE | LYS_CHOICE)));
-
-    return NULL;
-}
-
-API LY_ERR
 lysc_set_private(const struct lysc_node *node, void *priv, void **prev_priv_p)
 {
     struct lysc_action *act;
@@ -929,7 +592,7 @@
 }
 
 API LY_ERR
-lys_set_implemented(struct lys_module *mod)
+lys_set_implemented(struct lys_module *mod, const char **features)
 {
     LY_ERR ret = LY_SUCCESS, r;
     struct lys_module *m;
@@ -953,6 +616,9 @@
         return LY_EDENIED;
     }
 
+    /* enable features */
+    LY_CHECK_RET(lys_enable_features(mod->parsed, features));
+
     /* add the module into newly implemented module set */
     LY_CHECK_RET(ly_set_add(&mod->ctx->implementing, mod, 1, NULL));
 
@@ -1000,32 +666,32 @@
 }
 
 static LY_ERR
-lys_resolve_import_include(struct lys_parser_ctx *pctx, struct lysp_module *modp)
+lys_resolve_import_include(struct lys_parser_ctx *pctx, struct lysp_module *pmod)
 {
     struct lysp_import *imp;
     struct lysp_include *inc;
     LY_ARRAY_COUNT_TYPE u, v;
 
-    modp->parsing = 1;
-    LY_ARRAY_FOR(modp->imports, u) {
-        imp = &modp->imports[u];
+    pmod->parsing = 1;
+    LY_ARRAY_FOR(pmod->imports, u) {
+        imp = &pmod->imports[u];
         if (!imp->module) {
-            LY_CHECK_RET(lysp_load_module(PARSER_CTX(pctx), imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, &imp->module));
+            LY_CHECK_RET(lysp_load_module(PARSER_CTX(pctx), imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, NULL, &imp->module));
         }
         /* check for importing the same module twice */
         for (v = 0; v < u; ++v) {
-            if (imp->module == modp->imports[v].module) {
+            if (imp->module == pmod->imports[v].module) {
                 LOGWRN(PARSER_CTX(pctx), "Single revision of the module \"%s\" imported twice.", imp->name);
             }
         }
     }
-    LY_ARRAY_FOR(modp->includes, u) {
-        inc = &modp->includes[u];
+    LY_ARRAY_FOR(pmod->includes, u) {
+        inc = &pmod->includes[u];
         if (!inc->submodule) {
             LY_CHECK_RET(lysp_load_submodule(pctx, inc));
         }
     }
-    modp->parsing = 0;
+    pmod->parsing = 0;
 
     return LY_SUCCESS;
 }
@@ -1122,7 +788,7 @@
 LY_ERR
 lys_create_module(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, ly_bool implement,
         LY_ERR (*custom_check)(const struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_submodule *submod, void *data),
-        void *check_data, struct lys_module **module)
+        void *check_data, const char **features, struct lys_module **module)
 {
     struct lys_module *mod = NULL, *latest, *mod_dup;
     struct lysp_submodule *submod;
@@ -1134,7 +800,7 @@
     char *filename, *rev, *dot;
     size_t len;
 
-    LY_CHECK_ARG_RET(ctx, ctx, in, LY_EINVAL);
+    LY_CHECK_ARG_RET(ctx, ctx, in, !features || implement, LY_EINVAL);
     if (module) {
         *module = NULL;
     }
@@ -1190,38 +856,23 @@
         LY_CHECK_GOTO(ret = custom_check(ctx, mod->parsed, NULL, check_data), error);
     }
 
-    if (implement) {
-        /* mark the loaded module implemented */
-        if (ly_ctx_get_module_implemented(ctx, mod->name)) {
-            LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->name);
-            ret = LY_EDENIED;
-            goto error;
-        }
-    }
-
     /* check for duplicity in the context */
+    if (implement && ly_ctx_get_module_implemented(ctx, mod->name)) {
+        LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->name);
+        ret = LY_EDENIED;
+        goto error;
+    }
     mod_dup = (struct lys_module *)ly_ctx_get_module(ctx, mod->name, mod->revision);
     if (mod_dup) {
-        if (mod_dup->parsed) {
-            /* error */
-            if (mod->parsed->revs) {
-                LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
-                        mod->name, mod->parsed->revs[0].date);
-            } else {
-                LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
-                        mod->name);
-            }
-            ret = LY_EEXIST;
-            goto error;
+        if (mod->parsed->revs) {
+            LOGERR(ctx, LY_EEXIST, "Module \"%s\" of revision \"%s\" is already present in the context.",
+                    mod->name, mod->parsed->revs[0].date);
         } else {
-            /* add the parsed data to the currently compiled-only module in the context */
-            mod_dup->parsed = mod->parsed;
-            mod_dup->parsed->mod = mod_dup;
-            mod->parsed = NULL;
-            lys_module_free(mod, NULL);
-            mod = mod_dup;
-            goto finish_parsing;
+            LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
+                    mod->name);
         }
+        ret = LY_EEXIST;
+        goto error;
     }
 
     switch (in->type) {
@@ -1261,15 +912,8 @@
         ret = LY_EINT;
         goto error;
     }
-
     lys_parser_fill_filepath(ctx, in, &mod->filepath);
 
-    if (!implement) {
-        /* pre-compile features and identities of the module */
-        LY_CHECK_GOTO(ret = lys_feature_precompile(NULL, ctx, mod->parsed, mod->parsed->features, &mod->features), error);
-        LY_CHECK_GOTO(ret = lys_identity_precompile(NULL, ctx, mod->parsed, mod->parsed->identities, &mod->identities), error);
-    }
-
     if (latest) {
         latest->latest_revision = 0;
     }
@@ -1279,27 +923,31 @@
     LY_CHECK_GOTO(ret, error);
     ctx->module_set_id++;
 
-finish_parsing:
-    /* resolve imports and includes */
+    /* resolve includes and all imports */
     LY_CHECK_GOTO(ret = lys_resolve_import_include(pctx, mod->parsed), error_ctx);
 
+    /* check name collisions */
+    LY_CHECK_GOTO(ret = lysp_check_dup_typedefs(pctx, mod->parsed), error_ctx);
+    /* TODO groupings */
+    LY_CHECK_GOTO(ret = lysp_check_dup_features(pctx, mod->parsed), error_ctx);
+    LY_CHECK_GOTO(ret = lysp_check_dup_identities(pctx, mod->parsed), error_ctx);
+
+    /* compile features */
+    LY_CHECK_GOTO(ret = lys_compile_feature_iffeatures(mod->parsed), error_ctx);
+
     if (!implement) {
-        /* pre-compile features and identities of any submodules */
+        /* pre-compile identities of the module */
+        LY_CHECK_GOTO(ret = lys_identity_precompile(NULL, ctx, mod->parsed, mod->parsed->identities, &mod->identities), error);
+
+        /* pre-compile identities of any submodules */
         LY_ARRAY_FOR(mod->parsed->includes, u) {
             submod = mod->parsed->includes[u].submodule;
-            ret = lys_feature_precompile(NULL, ctx, (struct lysp_module *)submod, submod->features, &mod->features);
-            LY_CHECK_GOTO(ret, error);
             ret = lys_identity_precompile(NULL, ctx, (struct lysp_module *)submod, submod->identities, &mod->identities);
             LY_CHECK_GOTO(ret, error);
         }
-    }
-
-    /* check name collisions - typedefs and TODO groupings */
-    LY_CHECK_GOTO(ret = lysp_check_typedefs(pctx, mod->parsed), error_ctx);
-
-    if (implement) {
+    } else {
         /* implement (compile) */
-        LY_CHECK_GOTO(ret = lys_set_implemented(mod), error_ctx);
+        LY_CHECK_GOTO(ret = lys_set_implemented(mod, features), error_ctx);
     }
 
     if (format == LYS_IN_YANG) {
@@ -1329,7 +977,7 @@
 }
 
 API LY_ERR
-lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const struct lys_module **module)
+lys_parse(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, const char **features, const struct lys_module **module)
 {
     if (module) {
         *module = NULL;
@@ -1339,7 +987,7 @@
     /* remember input position */
     in->func_start = in->current;
 
-    return lys_create_module(ctx, in, format, 1, NULL, NULL, (struct lys_module **)module);
+    return lys_create_module(ctx, in, format, 1, NULL, NULL, features, (struct lys_module **)module);
 }
 
 API LY_ERR
@@ -1352,7 +1000,7 @@
 
     LY_CHECK_ERR_RET(ret = ly_in_new_memory(data, &in), LOGERR(ctx, ret, "Unable to create input handler."), ret);
 
-    ret = lys_parse(ctx, in, format, module);
+    ret = lys_parse(ctx, in, format, NULL, module);
     ly_in_free(in, 0);
 
     return ret;
@@ -1368,7 +1016,7 @@
 
     LY_CHECK_ERR_RET(ret = ly_in_new_fd(fd, &in), LOGERR(ctx, ret, "Unable to create input handler."), ret);
 
-    ret = lys_parse(ctx, in, format, module);
+    ret = lys_parse(ctx, in, format, NULL, module);
     ly_in_free(in, 0);
 
     return ret;
@@ -1385,7 +1033,7 @@
     LY_CHECK_ERR_RET(ret = ly_in_new_filepath(path, 0, &in),
             LOGERR(ctx, ret, "Unable to create input handler for filepath %s.", path), ret);
 
-    ret = lys_parse(ctx, in, format, module);
+    ret = lys_parse(ctx, in, format, NULL, module);
     ly_in_free(in, 0);
 
     return ret;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index fc19147..35dade9 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -118,40 +118,25 @@
 /**
  * @page howtoSchemaFeatures YANG Features
  *
- * YANG feature statement is an important part of the language which can significantly affect the meaning of the schemas. Despite
- * the features have similar effect as loading/removing schema from the context, manipulating with the feature value is not
- * limited to the context preparation period before working with data. YANG features, respectively their use in if-feature
- * statements, are evaluated as part of the [data validation process](@ref howtoDataValidation).
+ * YANG feature statement is an important part of the language which can significantly affect the meaning of the schemas.
+ * Modifying features may have similar effects as loading/removing schema from the context so it is limited to context
+ * preparation period before working with data. YANG features, respectively their use in if-feature
+ * statements, are evaluated as part of schema compilation so a feature-specific compiled schema tree is generated
+ * as a result.
  *
- * The main functions with *lys_feature_* prefix are used to change the value (true/false) of the feature and to get its current
- * value. Enabling/disabling all the features in a particular module can be done using '`*`' value instead of the feature name.
+ * To enable any features, they must currently be specified when implementing a new schema with ::lys_parse() or
+ * ::ly_ctx_load_module(). To later examine what the status of a feature is, check its ::LYS_FENABLED flag or
+ * search for it first with ::lys_feature_value(). Lastly, to evaluate compiled if-features, use ::lysc_iffeature_value().
  *
- * There are two options to reflect feature's if-feature statements when enabling/disabling the feature. The ::lys_feature_enable()
- * and ::lys_feature_disable() functions check their if-feature expressions (it is not possible to enable feature if it is not
- * allowed by its if-feature expressions) and also checks for and update other features those if-feature expressions use the
- * changed feature. On the contrary, ::lys_feature_enable_force() and ::lys_feature_disable_force() ignore all the if-feature
- * limitations.
- *
- * The ::lysc_feature_value() and ::lys_feature_value() functions differ only by their parameters. The ::lysc_iffeature_value()
- * is used to evaluate (possibly complex in YANG 1.1) logical expression from if-feature statement.
- *
- * The list of features of a particular YANG module is available in ::lys_module.features.
- *
- * To get know, if a specific schema node is currently disabled or enable, the ::lysc_node_is_disabled() function can be used.
+ * To iterate over all features of a particular YANG module, use ::lysp_feature_next().
  *
  * Note, that the feature's state can affect some of the output formats (e.g. Tree format).
  *
  * Functions List
  * --------------
- * - ::lys_feature_enable()
- * - ::lys_feature_enable_force()
- * - ::lys_feature_disable()
- * - ::lys_feature_disable_force()
  * - ::lys_feature_value()
- * - ::lysc_feature_value()
  * - ::lysc_iffeature_value()
- *
- * - ::lysc_node_is_disabled()
+ * - ::lysp_feature_next()
  */
 
 /**
@@ -368,7 +353,6 @@
     LYEXT_PAR_TYPE,      /**< ::lysc_type */
     LYEXT_PAR_TYPE_BIT,  /**< ::lysc_type_bitenum_item */
     LYEXT_PAR_TYPE_ENUM, /**< ::lysc_type_bitenum_item */
-    LYEXT_PAR_FEATURE,   /**< ::lysc_feature */
     LYEXT_PAR_MUST,      /**< ::lysc_must */
     LYEXT_PAR_PATTERN,   /**< ::lysc_pattern */
     LYEXT_PAR_LENGTH,    /**< ::lysc_range */
@@ -521,11 +505,23 @@
  */
 struct lysp_feature {
     const char *name;                /**< feature name (mandatory) */
-    struct lysp_qname *iffeatures;  /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysp_qname *iffeatures;   /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
+    struct lysc_iffeature *iffeatures_c;    /**< compiled if-features */
+    struct lysp_feature **depfeatures;  /**< list of pointers to other features depending on this one
+                                          ([sized array](@ref sizedarrays)) */
     const char *dsc;                 /**< description statement */
     const char *ref;                 /**< reference statement  */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values allowed */
+    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* values and
+                                          LYS_FENABLED are allowed */
+};
+
+/**
+ * @brief Compiled YANG if-feature-stmt
+ */
+struct lysc_iffeature {
+    uint8_t *expr;                   /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
+    struct lysp_feature **features;  /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
 };
 
 /**
@@ -788,6 +784,7 @@
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       6 LYS_MAND_TRUE    | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
  *         LYS_SET_PATH     | | | | | | | | | | | | | | | | | | | | | |x| |
+ *         LYS_FENABLED     | | | | | | | | | | | |x| | | | | | | | | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       7 LYS_MAND_FALSE   | |x|x| | |x| | | | | | | | | | | |x| |x| | | |
  *         LYS_ORDBY_USER   | | | |x|x| | | | | | | | | | | | | | | | | | |
@@ -850,7 +847,6 @@
  *         LYS_UNIQUE       | | |x| | | | | | | | | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *       9 LYS_KEY          | | |x| | | | | | | | | | | |
- *         LYS_FENABLED     | | | | | | | | | |x| | | | |
  *                          +-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  *      10 LYS_SET_DFLT     | | |x|x| | |x| | | | | | | |
  *         LYS_ISENUM       | | | | | | | | | | | | |x| |
@@ -892,10 +888,7 @@
 #define LYS_UNIQUE       0x80        /**< flag for leafs being part of a unique set, applicable only to ::lysc_node_leaf */
 #define LYS_KEY          0x100       /**< flag for leafs being a key of a list, applicable only to ::lysc_node_leaf */
 #define LYS_KEYLESS      0x200       /**< flag for list without any key, applicable only to ::lysc_node_list */
-#define LYS_FENABLED     0x100       /**< feature enabled flag, applicable only to ::lysc_feature.
-                                          Do not interpret presence of this flag as enabled feature! Also if-feature statements
-                                          are supposed to be taken into account, so use ::lys_feature_value() or
-                                          ::lysc_feature_value() to get know if a specific feature is really enabled. */
+#define LYS_FENABLED     0x20        /**< feature enabled flag, applicable only to ::lysp_feature. */
 #define LYS_ORDBY_SYSTEM 0x80        /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
                                           ::lysc_node_list/::lysp_node_list */
 #define LYS_ORDBY_USER   0x40        /**< ordered-by user lists, applicable only to ::lysc_node_leaflist/::lysp_node_leaflist and
@@ -1295,14 +1288,6 @@
 };
 
 /**
- * @brief Compiled YANG if-feature-stmt
- */
-struct lysc_iffeature {
-    uint8_t *expr;                   /**< 2bits array describing the if-feature expression in prefix format, see @ref ifftokens */
-    struct lysc_feature **features;  /**< array of pointers to the features used in expression ([sized array](@ref sizedarrays)) */
-};
-
-/**
  * @brief YANG when-stmt
  */
 struct lysc_when {
@@ -1326,26 +1311,10 @@
     struct lys_module *module;       /**< module structure */
     struct lysc_ident **derived;     /**< list of (pointers to the) derived identities ([sized array](@ref sizedarrays)) */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_ values are allowed */
 };
 
 /**
- * @brief YANG feature-stmt
- */
-struct lysc_feature {
-    const char *name;                /**< feature name (mandatory) */
-    const char *dsc;                 /**< description */
-    const char *ref;                 /**< reference */
-    struct lys_module *module;       /**< module structure */
-    struct lysc_feature **depfeatures;/**< list of pointers to other features depending on this one ([sized array](@ref sizedarrays)) */
-    struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
-    uint16_t flags;                  /**< [schema node flags](@ref snodeflags) - only LYS_STATUS_* and
-                                          #LYS_FENABLED values allowed */
-};
-
-/**
  * @defgroup ifftokens if-feature expression tokens
  * Tokens of if-feature expression used in ::lysc_iffeature.expr.
  *
@@ -1443,7 +1412,6 @@
     const char *dsc;             /**< description */
     const char *ref;             /**< reference */
     struct lysc_ext_instance *exts;    /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     union {
         int32_t value;           /**< integer value associated with the enumeration */
         uint32_t position;       /**< non-negative integer value associated with the bit */
@@ -1538,7 +1506,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
 
     struct lysc_action_inout input;  /**< RPC's/action's input */
     struct lysc_action_inout output; /**< RPC's/action's output */
@@ -1561,7 +1528,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /** private arbitrary user data, not used by libyang */
 };
@@ -1584,7 +1550,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 };
@@ -1604,7 +1569,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 
@@ -1629,7 +1593,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 
@@ -1652,7 +1615,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 
@@ -1677,7 +1639,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 
@@ -1703,7 +1664,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 
@@ -1733,7 +1693,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 
@@ -1762,7 +1721,6 @@
     const char *dsc;                 /**< description */
     const char *ref;                 /**< reference */
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
-    struct lysc_iffeature *iffeatures; /**< list of if-feature expressions ([sized array](@ref sizedarrays)) */
     struct lysc_when **when;         /**< list of pointers to when statements ([sized array](@ref sizedarrays)) */
     void *priv;                      /**< private arbitrary user data, not used by libyang */
 
@@ -1945,14 +1903,14 @@
 LY_ERR lysc_iffeature_value(const struct lysc_iffeature *iff);
 
 /**
- * @brief Get the current status of the provided feature.
+ * @brief Get the next feature in the module or submodules.
  *
- * @param[in] feature Compiled feature statement to examine.
- * @return LY_SUCCESS if feature is enabled,
- * @return LY_ENOT if feature is disabled,
- * @return LY_ERR in case of error (invalid argument)
+ * @param[in] last Last returned feature.
+ * @param[in] pmod Parsed module and submodoules whose features to iterate over.
+ * @param[in,out] idx Submodule index, set to 0 on first call.
+ * @return Next found feature, NULL if the last has already been returned.
  */
-LY_ERR lysc_feature_value(const struct lysc_feature *feature);
+struct lysp_feature *lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod, uint32_t *idx);
 
 /**
  * @defgroup findxpathoptions Atomize XPath options
@@ -2054,13 +2012,6 @@
     struct lysc_module *compiled;    /**< Compiled and fully validated YANG schema tree for data parsing.
                                           Available only for implemented modules. */
 
-    struct lysc_feature *features;   /**< List of compiled features of the module ([sized array](@ref sizedarrays)).
-                                          Features are outside the compiled tree since they are needed even the module is not
-                                          compiled. In such a case, the features are always disabled and cannot be enabled until
-                                          the module is implemented. The features are present in this form to allow their linkage
-                                          from if-feature statements of the compiled schemas and their proper use in case
-                                          the module became implemented in future (no matter if implicitly via augment/deviate
-                                          or explicitly via ::lys_set_implemented()). */
     struct lysc_ident *identities;   /**< List of compiled identities of the module ([sized array](@ref sizedarrays))
                                           Identities are outside the compiled tree to allow their linkage to the identities from
                                           the implemented modules. This avoids problems when the module became implemented in
@@ -2079,67 +2030,6 @@
 };
 
 /**
- * @brief Enable specified feature in the module
- *
- * By default, when the module is loaded by libyang parser, all features are disabled.
- *
- * If all features are being enabled, it must be possible respecting their if-feature conditions. For example,
- * enabling all features on the following feature set will fail since it is not possible to enable both features
- * (and it is unclear which of them should be enabled then). In this case the LY_EDENIED is returned and the feature
- * is untouched.
- *
- *     feature f1;
- *     feature f2 { if-feature 'not f1';}
- *
- * @param[in] module Module where the feature will be enabled.
- * @param[in] feature Name of the feature to enable. To enable all features at once, use asterisk (`*`) character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found,
- * @return LY_EDENIED if @p feature could not be enabled because it has some false if-feature statements.
- */
-LY_ERR lys_feature_enable(const struct lys_module *module, const char *feature);
-
-/**
- * @brief Disable specified feature in the module
- *
- * By default, when the module is loaded by libyang parser, all features are disabled.
- *
- * If disabling a feature causes some other features that depend on this feature to become disabled.
- *
- * @param[in] module Module where the feature will be disabled.
- * @param[in] feature Name of the feature to disable. To disable all features at once, use asterisk (`*`) character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found.
- */
-LY_ERR lys_feature_disable(const struct lys_module *module, const char *feature);
-
-/**
- * @brief Enable specified feature in the module disregarding its if-features.
- *
- * @param[in] module Module where the feature will be enabled.
- * @param[in] feature Name of the feature to enable. To enable all features at once, use asterisk character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found.
- */
-LY_ERR lys_feature_enable_force(const struct lys_module *module, const char *feature);
-
-/**
- * @brief Disable specified feature in the module disregarding dependant features.
- *
- * By default, when the module is loaded by libyang parser, all features are disabled.
- *
- * @param[in] module Module where the feature will be disabled.
- * @param[in] feature Name of the feature to disable. To disable all features at once, use asterisk character.
- * @return LY_SUCCESS on success,
- * @return LY_EINVAL if @p module is not implemented,
- * @return LY_ENOTFOUND if @p feature was not found.
- */
-LY_ERR lys_feature_disable_force(const struct lys_module *module, const char *feature);
-
-/**
  * @brief Get the current real status of the specified feature in the module.
  *
  * If the feature is enabled, but some of its if-features are false, the feature is considered
@@ -2186,10 +2076,8 @@
 #define LYS_GETNEXT_WITHCHOICE   0x01 /**< ::lys_getnext() option to allow returning #LYS_CHOICE nodes instead of looking into them */
 #define LYS_GETNEXT_NOCHOICE     0x02 /**< ::lys_getnext() option to ignore (kind of conditional) nodes within choice node */
 #define LYS_GETNEXT_WITHCASE     0x04 /**< ::lys_getnext() option to allow returning #LYS_CASE 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 */
-#define LYS_GETNEXT_OUTPUT       0x200 /**< ::lys_getnext() option to provide RPC's/action's output schema nodes instead of input schema nodes
+#define LYS_GETNEXT_INTONPCONT   0x08 /**< ::lys_getnext() option to look into non-presence container, instead of returning container itself */
+#define LYS_GETNEXT_OUTPUT       0x10 /**< ::lys_getnext() option to provide RPC's/action's output schema nodes instead of input schema nodes
                                             provided by default */
 /** @} sgetnextflags */
 
@@ -2213,22 +2101,13 @@
  *
  * @param[in] mod Module to make implemented. It is not an error
  * to provide already implemented module, it just does nothing.
+ * @param[in] features Optional array of features ended with NULL to be enabled if the module is being implemented.
+ * NULL for all features disabled and '*' for all enabled.
  * @return LY_SUCCESS on success.
  * @return LY_EDENIED in case the context contains some other revision of the same module which is already implemented.
+ * @return LY_ERR on other errors during module compilation.
  */
-LY_ERR lys_set_implemented(struct lys_module *mod);
-
-/**
- * @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,
- * @return pointer to the node with the unsatisfied (disabled) if-feature expression.
- */
-const struct lysc_node *lysc_node_is_disabled(const struct lysc_node *node, ly_bool recursive);
+LY_ERR lys_set_implemented(struct lys_module *mod, const char **features);
 
 /**
  * @brief Check type restrictions applicable to the particular leaf/leaf-list with the given string @p value.
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index e969a37..7597445 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -27,6 +27,7 @@
 
 void lysp_grp_free(struct ly_ctx *ctx, struct lysp_grp *grp);
 void lysc_extension_free(struct ly_ctx *ctx, struct lysc_ext **ext);
+static void lysc_node_free_(struct ly_ctx *ctx, struct lysc_node *node);
 
 static void
 lysp_stmt_free(struct ly_ctx *ctx, struct lysp_stmt *stmt)
@@ -105,6 +106,8 @@
 {
     FREE_STRING(ctx, feat->name);
     FREE_ARRAY(ctx, feat->iffeatures, lysp_qname_free);
+    FREE_ARRAY(ctx, feat->iffeatures_c, lysc_iffeature_free);
+    LY_ARRAY_FREE(feat->depfeatures);
     FREE_STRING(ctx, feat->dsc);
     FREE_STRING(ctx, feat->ref);
     FREE_ARRAY(ctx, feat->exts, lysp_ext_instance_free);
@@ -509,28 +512,16 @@
     FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
 }
 
-static void
+void
 lysc_ident_free(struct ly_ctx *ctx, struct lysc_ident *ident)
 {
     FREE_STRING(ctx, ident->name);
     FREE_STRING(ctx, ident->dsc);
     FREE_STRING(ctx, ident->ref);
-    FREE_ARRAY(ctx, ident->iffeatures, lysc_iffeature_free);
     LY_ARRAY_FREE(ident->derived);
     FREE_ARRAY(ctx, ident->exts, lysc_ext_instance_free);
 }
 
-void
-lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat)
-{
-    FREE_STRING(ctx, feat->name);
-    FREE_STRING(ctx, feat->dsc);
-    FREE_STRING(ctx, feat->ref);
-    FREE_ARRAY(ctx, feat->iffeatures, lysc_iffeature_free);
-    LY_ARRAY_FREE(feat->depfeatures);
-    FREE_ARRAY(ctx, feat->exts, lysc_ext_instance_free);
-}
-
 static void
 lysc_range_free(struct ly_ctx *ctx, struct lysc_range *range)
 {
@@ -564,7 +555,6 @@
     FREE_STRING(ctx, item->name);
     FREE_STRING(ctx, item->dsc);
     FREE_STRING(ctx, item->ref);
-    FREE_ARRAY(ctx, item->iffeatures, lysc_iffeature_free);
     FREE_ARRAY(ctx, item->exts, lysc_ext_instance_free);
 }
 
@@ -637,7 +627,7 @@
 
     FREE_ARRAY(ctx, inout->musts, lysc_must_free);
     LY_LIST_FOR_SAFE(inout->data, child_next, child) {
-        lysc_node_free(ctx, child);
+        lysc_node_free_(ctx, child);
     }
 }
 
@@ -647,7 +637,6 @@
     FREE_STRING(ctx, action->name);
     FREE_STRING(ctx, action->dsc);
     FREE_STRING(ctx, action->ref);
-    FREE_ARRAY(ctx, action->iffeatures, lysc_iffeature_free);
     FREE_ARRAY(ctx, action->exts, lysc_ext_instance_free);
     FREE_ARRAY(ctx, action->when, lysc_when_free);
     FREE_ARRAY(ctx, action->input_exts, lysc_ext_instance_free);
@@ -664,12 +653,11 @@
     FREE_STRING(ctx, notif->name);
     FREE_STRING(ctx, notif->dsc);
     FREE_STRING(ctx, notif->ref);
-    FREE_ARRAY(ctx, notif->iffeatures, lysc_iffeature_free);
     FREE_ARRAY(ctx, notif->exts, lysc_ext_instance_free);
     FREE_ARRAY(ctx, notif->when, lysc_when_free);
     FREE_ARRAY(ctx, notif->musts, lysc_must_free);
     LY_LIST_FOR_SAFE(notif->data, child_next, child) {
-        lysc_node_free(ctx, child);
+        lysc_node_free_(ctx, child);
     }
 }
 
@@ -679,7 +667,7 @@
     struct lysc_node *child, *child_next;
 
     LY_LIST_FOR_SAFE(node->child, child_next, child) {
-        lysc_node_free(ctx, child);
+        lysc_node_free_(ctx, child);
     }
     FREE_ARRAY(ctx, node->musts, lysc_must_free);
     FREE_ARRAY(ctx, node->actions, lysc_action_free);
@@ -726,7 +714,7 @@
     struct lysc_node *child, *child_next;
 
     LY_LIST_FOR_SAFE(node->child, child_next, child) {
-        lysc_node_free(ctx, child);
+        lysc_node_free_(ctx, child);
     }
     FREE_ARRAY(ctx, node->musts, lysc_must_free);
 
@@ -745,7 +733,7 @@
     struct lysc_node *child, *child_next;
 
     LY_LIST_FOR_SAFE((struct lysc_node *)node->cases, child_next, child) {
-        lysc_node_free(ctx, child);
+        lysc_node_free_(ctx, child);
     }
 }
 
@@ -755,7 +743,7 @@
     struct lysc_node *child, *child_next;
 
     LY_LIST_FOR_SAFE(node->child, child_next, child) {
-        lysc_node_free(ctx, child);
+        lysc_node_free_(ctx, child);
     }
 }
 
@@ -765,8 +753,8 @@
     FREE_ARRAY(ctx, node->musts, lysc_must_free);
 }
 
-void
-lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node)
+static void
+lysc_node_free_(struct ly_ctx *ctx, struct lysc_node *node)
 {
     /* common part */
     FREE_STRING(ctx, node->name);
@@ -802,11 +790,48 @@
     }
 
     FREE_ARRAY(ctx, node->when, lysc_when_free);
-    FREE_ARRAY(ctx, node->iffeatures, lysc_iffeature_free);
     FREE_ARRAY(ctx, node->exts, lysc_ext_instance_free);
     free(node);
 }
 
+void
+lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node, ly_bool unlink)
+{
+    struct lysc_node *iter, **child_p;
+
+    if (unlink) {
+        /* unlink from siblings */
+        if (node->prev->next) {
+            node->prev->next = node->next;
+        }
+        if (node->next) {
+            node->next->prev = node->prev;
+        } else {
+            /* unlinking the last node */
+            if (node->parent) {
+                iter = (struct lysc_node *)lysc_node_children(node->parent, node->flags & LYS_CONFIG_MASK);
+            } else {
+                iter = node->module->compiled->data;
+            }
+            /* update the "last" pointer from the first node */
+            iter->prev = node->prev;
+        }
+
+        /* unlink from parent */
+        if (node->parent) {
+            child_p = lysc_node_children_p(node->parent, node->flags & LYS_CONFIG_MASK);
+        } else {
+            child_p = &node->module->compiled->data;
+        }
+        if (child_p && (*child_p == node)) {
+            /* the node is the first child */
+            *child_p = node->next;
+        }
+    }
+
+    lysc_node_free_(ctx, node);
+}
+
 static void
 lysc_module_free_(struct lysc_module *module)
 {
@@ -817,7 +842,7 @@
     ctx = module->mod->ctx;
 
     LY_LIST_FOR_SAFE(module->data, node_next, node) {
-        lysc_node_free(ctx, node);
+        lysc_node_free_(ctx, node);
     }
     FREE_ARRAY(ctx, module->rpcs, lysc_action_free);
     FREE_ARRAY(ctx, module->notifs, lysc_notif_free);
@@ -845,7 +870,6 @@
     }
 
     lysc_module_free(module->compiled, private_destructor);
-    FREE_ARRAY(module->ctx, module->features, lysc_feature_free);
     FREE_ARRAY(module->ctx, module->identities, lysc_ident_free);
     lysp_module_free(module->parsed);
 
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index a5f6ef5..3ce3f3b 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -320,7 +320,7 @@
  * @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
  */
 static LY_ERR
-lysp_check_typedef(struct lys_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
+lysp_check_dup_typedef(struct lys_parser_ctx *ctx, struct lysp_node *node, const struct lysp_tpdf *tpdf,
         struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
 {
     struct lysp_node *parent;
@@ -485,7 +485,7 @@
 }
 
 LY_ERR
-lysp_check_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod)
+lysp_check_dup_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod)
 {
     struct hash_table *ids_global;
     struct hash_table *ids_scoped;
@@ -498,13 +498,13 @@
     ids_global = lyht_new(8, sizeof(char *), lysp_id_cmp, NULL, 1);
     ids_scoped = lyht_new(8, sizeof(char *), lysp_id_cmp, NULL, 1);
     LY_ARRAY_FOR(mod->typedefs, v) {
-        if (lysp_check_typedef(ctx, NULL, &mod->typedefs[v], ids_global, ids_scoped)) {
+        if (lysp_check_dup_typedef(ctx, NULL, &mod->typedefs[v], ids_global, ids_scoped)) {
             goto cleanup;
         }
     }
     LY_ARRAY_FOR(mod->includes, v) {
         LY_ARRAY_FOR(mod->includes[v].submodule->typedefs, u) {
-            if (lysp_check_typedef(ctx, NULL, &mod->includes[v].submodule->typedefs[u], ids_global, ids_scoped)) {
+            if (lysp_check_dup_typedef(ctx, NULL, &mod->includes[v].submodule->typedefs[u], ids_global, ids_scoped)) {
                 goto cleanup;
             }
         }
@@ -512,7 +512,7 @@
     for (i = 0; i < ctx->tpdfs_nodes.count; ++i) {
         typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[i]);
         LY_ARRAY_FOR(typedefs, u) {
-            if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[i], &typedefs[u], ids_global, ids_scoped)) {
+            if (lysp_check_dup_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[i], &typedefs[u], ids_global, ids_scoped)) {
                 goto cleanup;
             }
         }
@@ -526,6 +526,108 @@
     return ret;
 }
 
+static ly_bool
+ly_ptrequal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
+{
+    void *ptr1 = *((void **)val1_p), *ptr2 = *((void **)val2_p);
+
+    return ptr1 == ptr2 ? 1 : 0;
+}
+
+LY_ERR
+lysp_check_dup_features(struct lys_parser_ctx *ctx, struct lysp_module *mod)
+{
+    LY_ARRAY_COUNT_TYPE u;
+    struct hash_table *ht;
+    struct lysp_feature *f;
+    uint32_t hash;
+    LY_ERR ret = LY_SUCCESS, r;
+
+    ht = lyht_new(1, sizeof(void *), ly_ptrequal_cb, NULL, 1);
+    LY_CHECK_RET(!ht, LY_EMEM);
+
+    /* add all module features into a hash table */
+    LY_ARRAY_FOR(mod->features, struct lysp_feature, f) {
+        hash = dict_hash(f->name, strlen(f->name));
+        r = lyht_insert(ht, &f->name, hash, NULL);
+        if (r == LY_EEXIST) {
+            LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, f->name, "feature");
+            ret = LY_EVALID;
+            goto cleanup;
+        } else if (r) {
+            ret = r;
+            goto cleanup;
+        }
+    }
+
+    /* add all submodule features into a hash table */
+    LY_ARRAY_FOR(mod->includes, u) {
+        LY_ARRAY_FOR(mod->includes[u].submodule->features, struct lysp_feature, f) {
+            hash = dict_hash(f->name, strlen(f->name));
+            r = lyht_insert(ht, &f->name, hash, NULL);
+            if (r == LY_EEXIST) {
+                LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, f->name, "feature");
+                ret = LY_EVALID;
+                goto cleanup;
+            } else if (r) {
+                ret = r;
+                goto cleanup;
+            }
+        }
+    }
+
+cleanup:
+    lyht_free(ht);
+    return ret;
+}
+
+LY_ERR
+lysp_check_dup_identities(struct lys_parser_ctx *ctx, struct lysp_module *mod)
+{
+    LY_ARRAY_COUNT_TYPE u;
+    struct hash_table *ht;
+    struct lysp_ident *i;
+    uint32_t hash;
+    LY_ERR ret = LY_SUCCESS, r;
+
+    ht = lyht_new(1, sizeof(void *), ly_ptrequal_cb, NULL, 1);
+    LY_CHECK_RET(!ht, LY_EMEM);
+
+    /* add all module identities into a hash table */
+    LY_ARRAY_FOR(mod->identities, struct lysp_ident, i) {
+        hash = dict_hash(i->name, strlen(i->name));
+        r = lyht_insert(ht, &i->name, hash, NULL);
+        if (r == LY_EEXIST) {
+            LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, i->name, "identity");
+            ret = LY_EVALID;
+            goto cleanup;
+        } else if (r) {
+            ret = r;
+            goto cleanup;
+        }
+    }
+
+    /* add all submodule identities into a hash table */
+    LY_ARRAY_FOR(mod->includes, u) {
+        LY_ARRAY_FOR(mod->includes[u].submodule->identities, struct lysp_ident, i) {
+            hash = dict_hash(i->name, strlen(i->name));
+            r = lyht_insert(ht, &i->name, hash, NULL);
+            if (r == LY_EEXIST) {
+                LOGVAL_PARSER(ctx, LY_VCODE_DUPIDENT, i->name, "identity");
+                ret = LY_EVALID;
+                goto cleanup;
+            } else if (r) {
+                ret = r;
+                goto cleanup;
+            }
+        }
+    }
+
+cleanup:
+    lyht_free(ht);
+    return ret;
+}
+
 struct lysp_load_module_check_data {
     const char *name;
     const char *revision;
@@ -608,7 +710,7 @@
 }
 
 LY_ERR
-lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement,
+lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, const char **features, ly_bool implement,
         struct lys_parser_ctx *main_ctx, const char *main_name, ly_bool required, void **result)
 {
     struct ly_in *in;
@@ -641,7 +743,7 @@
         ret = lys_parse_submodule(ctx, in, format, main_ctx, lysp_load_module_check, &check_data,
                 (struct lysp_submodule **)&mod);
     } else {
-        ret = lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data,
+        ret = lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data, features,
                 (struct lys_module **)&mod);
 
     }
@@ -659,7 +761,7 @@
 
 LY_ERR
 lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement, ly_bool require_parsed,
-        struct lys_module **mod)
+        const char **features, struct lys_module **mod)
 {
     const char *module_data = NULL;
     LYS_INFORMAT format = LYS_IN_UNKNOWN;
@@ -718,7 +820,7 @@
                     LY_CHECK_RET(ly_in_new_memory(module_data, &in));
                     check_data.name = name;
                     check_data.revision = revision;
-                    lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data, mod);
+                    lys_create_module(ctx, in, format, implement, lysp_load_module_check, &check_data, features, mod);
                     ly_in_free(in, 0);
                     if (module_data_free) {
                         module_data_free((void *)module_data, ctx->imp_clb_data);
@@ -732,7 +834,7 @@
 search_file:
             if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
                 /* module was not received from the callback or there is no callback set */
-                lys_module_localfile(ctx, name, revision, implement, NULL, NULL, m ? 0 : 1, (void **)mod);
+                lys_module_localfile(ctx, name, revision, features, implement, NULL, NULL, m ? 0 : 1, (void **)mod);
             }
             if (!(*mod) && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
                 goto search_clb;
@@ -852,8 +954,8 @@
 search_file:
         if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
             /* submodule was not received from the callback or there is no callback set */
-            lys_module_localfile(ctx, inc->name, inc->rev[0] ? inc->rev : NULL, 0, pctx, pctx->parsed_mod->mod->name, 1,
-                    (void **)&submod);
+            lys_module_localfile(ctx, inc->name, inc->rev[0] ? inc->rev : NULL, NULL, 0, pctx,
+                    pctx->parsed_mod->mod->name, 1, (void **)&submod);
         }
         if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
             goto search_clb;
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 4d45f7e..fbab73c 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -211,7 +211,25 @@
  * @param[in] mod Module where the type is being defined.
  * @return LY_ERR value.
  */
-LY_ERR lysp_check_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod);
+LY_ERR lysp_check_dup_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Check names of features in the parsed module and submodules to detect collisions.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_features(struct lys_parser_ctx *ctx, struct lysp_module *mod);
+
+/**
+ * @brief Check names of identities in the parsed module and submodules to detect collisions.
+ *
+ * @param[in] ctx Parser context.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_identities(struct lys_parser_ctx *ctx, struct lysp_module *mod);
 
 /**
  * @brief Finalize some of the structures in case they are stored in sized array,
@@ -267,10 +285,12 @@
  * of the latest revision can not be made implemented.
  * @param[in] require_parsed Flag to require parsed module structure in case the module is already in the context,
  * but only the compiled structure is available.
+ * @param[in] features All the features to enable if implementing the module.
  * @param[out] mod Parsed module structure.
  * @return LY_ERR value.
  */
-LY_ERR lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement, ly_bool require_parsed, struct lys_module **mod);
+LY_ERR lysp_load_module(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement,
+        ly_bool require_parsed, const char **features, struct lys_module **mod);
 
 /**
  * @brief Parse included submodule into the simply parsed YANG module.
@@ -436,11 +456,12 @@
  * @param[in] implement Flag if the schema is supposed to be marked as implemented and compiled.
  * @param[in] custom_check Callback to check the parsed schema before it is accepted.
  * @param[in] check_data Caller's data to pass to the custom_check callback.
+ * @param[in] features Array of features to enable ended with NULL. NULL for all features disabled and '*' for all enabled.
  * @param[out] module Created module.
  * @return LY_ERR value.
  */
 LY_ERR lys_create_module(struct ly_ctx *ctx, struct ly_in *in, LYS_INFORMAT format, ly_bool implement,
-        lys_custom_check custom_check, void *check_data, struct lys_module **module);
+        lys_custom_check custom_check, void *check_data, const char **features, struct lys_module **module);
 
 /**
  * @brief Parse submodule.
@@ -479,6 +500,7 @@
  * @param[in] ctx libyang context where to work.
  * @param[in] name Name of the (sub)module to load.
  * @param[in] revision Optional revision of the (sub)module to load, if NULL the newest revision is being loaded.
+ * @param[in] features Array of enabled features ended with NULL.
  * @param[in] implement Flag if the (sub)module is supposed to be marked as implemented.
  * @param[in] main_ctx Parser context of the main module in case of loading submodule.
  * @param[in] main_name Main module name in case of loading submodule.
@@ -488,8 +510,8 @@
  * If it is a module, it is already in the context!
  * @return LY_ERR value, in case of LY_SUCCESS, the \arg result is always provided.
  */
-LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, ly_bool implement,
-        struct lys_parser_ctx *main_ctx, const char *main_name, ly_bool required, void **result);
+LY_ERR lys_module_localfile(struct ly_ctx *ctx, const char *name, const char *revision, const char **features,
+        ly_bool implement, struct lys_parser_ctx *main_ctx, const char *main_name, ly_bool required, void **result);
 
 /**
  * @brief Get the @ref ifftokens from the given position in the 2bits array
@@ -559,6 +581,14 @@
 void lysc_iffeature_free(struct ly_ctx *ctx, struct lysc_iffeature *iff);
 
 /**
+ * @brief Free the compiled identity structure.
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] ident Compiled identity structure to be cleaned.
+ * Since the structure is typically part of the sized array, the structure itself is not freed.
+ */
+void lysc_ident_free(struct ly_ctx *ctx, struct lysc_ident *ident);
+
+/**
  * @brief Free the compiled must structure.
  * @param[in] ctx libyang context where the string data resides in a dictionary.
  * @param[in,out] must Compiled must structure to be cleaned.
@@ -601,9 +631,10 @@
 /**
  * @brief Free the compiled node structure.
  * @param[in] ctx libyang context where the string data resides in a dictionary.
- * @param[in,out] node Compiled node structure to be freed.
+ * @param[in] node Compiled node structure to be freed.
+ * @param[in] unlink Whether to first unlink the node before freeing.
  */
-void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node);
+void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node, ly_bool unlink);
 
 /**
  * @brief Free the compiled container node structure.
diff --git a/src/validation.c b/src/validation.c
index 36e8de6..6b495e2 100644
--- a/src/validation.c
+++ b/src/validation.c
@@ -952,7 +952,6 @@
         LYD_VALIDATE_OP op)
 {
     struct lyd_node *next = NULL, *node;
-    const struct lysc_node *snode;
 
     /* validate all restrictions of nodes themselves */
     LY_LIST_FOR_SAFE(first, next, node) {
@@ -983,12 +982,6 @@
         /* obsolete data */
         lyd_validate_obsolete(node);
 
-        /* node's schema if-features */
-        if ((snode = lysc_node_is_disabled(node->schema, 1))) {
-            LOGVAL(LYD_CTX(node), LY_VLOG_LYD, node, LY_VCODE_NOIFF, snode->name);
-            return LY_EVALID;
-        }
-
         /* node's musts */
         LY_CHECK_RET(lyd_validate_must(node, op));
 
@@ -1265,11 +1258,6 @@
 
     /* perform final validation of the operation/notification */
     lyd_validate_obsolete(op_node);
-    if (lysc_node_is_disabled(op_node->schema, 1)) {
-        LOGVAL(LYD_CTX(op_tree), LY_VLOG_LYD, op_node, LY_VCODE_NOIFF, op_node->schema->name);
-        ret = LY_EVALID;
-        goto cleanup;
-    }
     LY_CHECK_GOTO(ret = lyd_validate_must(op_node, op), cleanup);
 
     /* final validation of all the descendants */
diff --git a/src/xpath.c b/src/xpath.c
index 4d3c5fc..acfeaf4 100644
--- a/src/xpath.c
+++ b/src/xpath.c
@@ -5663,7 +5663,7 @@
     }
 
     /* getnext opts */
-    getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+    getnext_opts = 0;
     if (options & LYXP_SCNODE_OUTPUT) {
         getnext_opts |= LYS_GETNEXT_OUTPUT;
     }
@@ -6250,7 +6250,7 @@
     }
 
     /* getnext opts */
-    getnext_opts = LYS_GETNEXT_NOSTATECHECK;
+    getnext_opts = 0;
     if (options & LYXP_SCNODE_OUTPUT) {
         getnext_opts |= LYS_GETNEXT_OUTPUT;
     }
diff --git a/tests/utests/data/test_diff.c b/tests/utests/data/test_diff.c
index c1a1cf2..f29e80d 100644
--- a/tests/utests/data/test_diff.c
+++ b/tests/utests/data/test_diff.c
@@ -227,7 +227,7 @@
     (*state) = st = calloc(1, sizeof *st);
     assert_non_null(st);
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &st->ctx));
-    assert_non_null(ly_ctx_load_module(st->ctx, "ietf-netconf-acm", "2018-02-14"));
+    assert_non_null(ly_ctx_load_module(st->ctx, "ietf-netconf-acm", "2018-02-14", NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(st->ctx, schema, LYS_IN_YANG, NULL));
 
     return 0;
diff --git a/tests/utests/data/test_lyb.c b/tests/utests/data/test_lyb.c
index 588ad22..91117f0 100644
--- a/tests/utests/data/test_lyb.c
+++ b/tests/utests/data/test_lyb.c
@@ -280,8 +280,8 @@
     "</interfaces>";
 
 
-    assert_non_null(ly_ctx_load_module(st->ctx, "ietf-ip", NULL));
-    assert_non_null(ly_ctx_load_module(st->ctx, "iana-if-type", NULL));
+    assert_non_null(ly_ctx_load_module(st->ctx, "ietf-ip", NULL, NULL));
+    assert_non_null(ly_ctx_load_module(st->ctx, "iana-if-type", NULL, NULL));
 
     assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(st->ctx, data_xml, LYD_XML, LYD_PARSE_ONLY, 0, &st->dt1));
     assert_ptr_not_equal(st->dt1, NULL);
@@ -328,7 +328,7 @@
     "</cont>";
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(st->ctx, origin_yang, LYS_IN_YANG, NULL));
-    lys_set_implemented(ly_ctx_get_module_latest(st->ctx, "ietf-origin"));
+    lys_set_implemented(ly_ctx_get_module_latest(st->ctx, "ietf-origin"), NULL);
 
     assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(st->ctx, data_xml, LYD_XML, LYD_PARSE_ONLY, 0, &st->dt1));
     assert_ptr_not_equal(st->dt1, NULL);
diff --git a/tests/utests/data/test_parser_json.c b/tests/utests/data/test_parser_json.c
index 0c9e0a2..c22eb66 100644
--- a/tests/utests/data/test_parser_json.c
+++ b/tests/utests/data/test_parser_json.c
@@ -77,15 +77,15 @@
             "leaf foo3 { type uint32; }"
             "notification n2;}";
     const struct lys_module *mod;
+    const char *feats[] = {"writable-running", NULL};
 
 #if ENABLE_LOGGER_CHECKING
     ly_set_log_clb(logger, 1);
 #endif
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
-    assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01"));
-    assert_non_null((mod = ly_ctx_load_module(ctx, "ietf-netconf", "2011-06-01")));
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "writable-running"));
+    assert_non_null((mod = ly_ctx_load_module(ctx, "ietf-netconf", "2011-06-01", feats)));
+    assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01", NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL));
 
     return 0;
diff --git a/tests/utests/data/test_parser_xml.c b/tests/utests/data/test_parser_xml.c
index 183e22f..baf4608 100644
--- a/tests/utests/data/test_parser_xml.c
+++ b/tests/utests/data/test_parser_xml.c
@@ -75,15 +75,15 @@
             "leaf foo3 { type uint32; }"
             "notification n2;}";
     const struct lys_module *mod;
+    const char *feats[] = {"writable-running", NULL};
 
 #if ENABLE_LOGGER_CHECKING
     ly_set_log_clb(logger, 1);
 #endif
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
-    assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01"));
-    assert_non_null((mod = ly_ctx_load_module(ctx, "ietf-netconf", "2011-06-01")));
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "writable-running"));
+    assert_non_null((mod = ly_ctx_load_module(ctx, "ietf-netconf", "2011-06-01", feats)));
+    assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01", NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL));
 
     return 0;
diff --git a/tests/utests/data/test_printer_xml.c b/tests/utests/data/test_printer_xml.c
index b2bcd75..75cddd2 100644
--- a/tests/utests/data/test_printer_xml.c
+++ b/tests/utests/data/test_printer_xml.c
@@ -131,7 +131,7 @@
 #endif
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &s->ctx));
-    assert_non_null(ly_ctx_load_module(s->ctx, "ietf-netconf-with-defaults", "2011-06-01"));
+    assert_non_null(ly_ctx_load_module(s->ctx, "ietf-netconf-with-defaults", "2011-06-01", NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(s->ctx, schema_a, LYS_IN_YANG, NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(s->ctx, schema_b, LYS_IN_YANG, NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(s->ctx, schema_c, LYS_IN_YANG, NULL));
diff --git a/tests/utests/data/test_types.c b/tests/utests/data/test_types.c
index 27e2338..e17118d 100644
--- a/tests/utests/data/test_types.c
+++ b/tests/utests/data/test_types.c
@@ -184,8 +184,7 @@
     assert_string_equal("15", leaf->value.canonical);
     assert_int_equal(15, leaf->value.int8);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal(leaf->value.canonical, value.canonical);
     assert_int_equal(15, value.int8);
     value.realtype->plugin->free(s->ctx, &value);
@@ -225,8 +224,7 @@
     assert_string_equal("150", leaf->value.canonical);
     assert_int_equal(150, leaf->value.uint8);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal(leaf->value.canonical, value.canonical);
     assert_int_equal(150, value.uint8);
     value.realtype->plugin->free(s->ctx, &value);
@@ -266,8 +264,7 @@
     assert_string_equal("8.0", leaf->value.canonical);
     assert_int_equal(80, leaf->value.dec64);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal(leaf->value.canonical, value.canonical);
     assert_int_equal(80, value.dec64);
     value.realtype->plugin->free(s->ctx, &value);
@@ -374,8 +371,7 @@
     assert_string_equal("zero", leaf->value.bits_items[0]->name);
     assert_string_equal("two", leaf->value.bits_items[1]->name);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal(leaf->value.canonical, value.canonical);
     assert_int_equal(2, LY_ARRAY_COUNT(value.bits_items));
     assert_string_equal("zero", value.bits_items[0]->name);
@@ -389,29 +385,17 @@
     assert_string_equal("bits", tree->schema->name);
     leaf = (struct lyd_node_term*)tree;
     assert_string_equal("zero two", leaf->value.canonical);
+
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_string_equal(leaf->value.canonical, value.canonical);
+    assert_int_equal(2, LY_ARRAY_COUNT(value.bits_items));
+    assert_string_equal("zero", value.bits_items[0]->name);
+    value.realtype->plugin->free(s->ctx, &value);
     lyd_free_all(tree);
 
     /* disabled feature */
     TEST_DATA("<bits xmlns=\"urn:tests:types\"> \t one \n\t </bits>", LY_EVALID, "Bit \"one\" is disabled by its 1. if-feature condition. /types:bits");
 
-    /* enable that feature */
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(ly_ctx_get_module(s->ctx, "types", NULL), "f"));
-    TEST_DATA("<bits xmlns=\"urn:tests:types\"> \t one \n\t </bits>", LY_SUCCESS, "");
-    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
-    assert_string_equal("bits", tree->schema->name);
-    leaf = (struct lyd_node_term*)tree;
-    assert_string_equal("one", leaf->value.canonical);
-    assert_int_equal(1, LY_ARRAY_COUNT(leaf->value.bits_items));
-    assert_string_equal("one", leaf->value.bits_items[0]->name);
-
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
-    assert_string_equal(leaf->value.canonical, value.canonical);
-    assert_int_equal(1, LY_ARRAY_COUNT(value.bits_items));
-    assert_string_equal("one", value.bits_items[0]->name);
-    value.realtype->plugin->free(s->ctx, &value);
-    lyd_free_all(tree);
-
     /* multiple instances of the bit */
     TEST_DATA("<bits xmlns=\"urn:tests:types\">one zero one</bits>", LY_EVALID, "Bit \"one\" used multiple times. /types:bits");
 
@@ -441,8 +425,7 @@
     assert_string_equal("white", leaf->value.canonical);
     assert_string_equal("white", leaf->value.enum_item->name);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal(leaf->value.canonical, value.canonical);
     assert_string_equal("white", value.enum_item->name);
     value.realtype->plugin->free(s->ctx, &value);
@@ -452,15 +435,6 @@
     TEST_DATA("<enums xmlns=\"urn:tests:types\">yellow</enums>", LY_EVALID,
               "Enumeration \"yellow\" is disabled by its 1. if-feature condition. /types:enums");
 
-    /* enable that feature */
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(ly_ctx_get_module(s->ctx, "types", NULL), "f"));
-    TEST_DATA("<enums xmlns=\"urn:tests:types\">yellow</enums>", LY_SUCCESS, "");
-    assert_int_equal(LYS_LEAF, tree->schema->nodetype);
-    assert_string_equal("enums", tree->schema->name);
-    leaf = (struct lyd_node_term*)tree;
-    assert_string_equal("yellow", leaf->value.canonical);
-    lyd_free_all(tree);
-
     /* leading/trailing whitespaces are not valid */
     TEST_DATA("<enums xmlns=\"urn:tests:types\"> white</enums>", LY_EVALID, "Invalid enumeration value \" white\". /types:enums");
     TEST_DATA("<enums xmlns=\"urn:tests:types\">white\n</enums>", LY_EVALID, "Invalid enumeration value \"white\n\". /types:enums");
@@ -495,8 +469,7 @@
     leaf = (struct lyd_node_term*)tree;
     assert_string_equal("TQ==", leaf->value.canonical);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal(leaf->value.canonical, value.canonical);
     value.realtype->plugin->free(s->ctx, &value);
     memset(&value, 0, sizeof value);
@@ -663,8 +636,7 @@
     assert_string_equal("gigabit-ethernet", leaf->value.ident->name);
     test_printed_value(&leaf->value, "t:gigabit-ethernet", LY_PREF_SCHEMA, s->mod_types->parsed);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal("types:gigabit-ethernet", value.canonical);
     assert_string_equal("gigabit-ethernet", value.ident->name);
     value.realtype->plugin->free(s->ctx, &value);
@@ -729,8 +701,7 @@
     assert_string_equal("leaftarget", leaf->value.target[1].node->name);
     assert_null(leaf->value.target[1].predicates);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal("/types:cont/leaftarget", value.canonical);
     assert_true(LY_ARRAY_COUNT(leaf->value.target) == LY_ARRAY_COUNT(value.target));
     assert_true(leaf->value.target[0].node == value.target[0].node);
@@ -776,8 +747,7 @@
     test_printed_value(&leaf->value, "/t:list_inst[t:id=\"/t:leaflisttarget[.='b']\"]/t:value", LY_PREF_SCHEMA, s->mod_types->parsed);
     test_printed_value(&leaf->value, "/types:list_inst[id=\"/types:leaflisttarget[.='b']\"]/value", LY_PREF_JSON, NULL);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal("/types:list_inst[id=\"/types:leaflisttarget[.='b']\"]/value", value.canonical);
     assert_true(LY_ARRAY_COUNT(leaf->value.target) == LY_ARRAY_COUNT(value.target));
     assert_true(leaf->value.target[0].node == value.target[0].node);
@@ -1115,8 +1085,7 @@
 
     test_printed_value(&leaf->value, "12", LY_PREF_SCHEMA, NULL);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal("12", value.canonical);
     assert_non_null(value.subvalue->prefix_data);
     assert_int_equal(((struct ly_set *)leaf->value.subvalue->prefix_data)->count, 0);
@@ -1153,8 +1122,7 @@
     test_printed_value(&leaf->value, "d:fast-ethernet", LY_PREF_SCHEMA, s->mod_defs->parsed);
     test_printed_value(&leaf->value.subvalue->value, "d:fast-ethernet", LY_PREF_SCHEMA, s->mod_defs->parsed);
 
-    value.realtype = leaf->value.realtype;
-    assert_int_equal(LY_SUCCESS, value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
+    assert_int_equal(LY_SUCCESS, leaf->value.realtype->plugin->duplicate(s->ctx, &leaf->value, &value));
     assert_string_equal("defs:fast-ethernet", value.canonical);
     assert_string_equal("defs:fast-ethernet", value.subvalue->value.canonical);
     assert_non_null(value.subvalue->prefix_data);
diff --git a/tests/utests/data/test_validation.c b/tests/utests/data/test_validation.c
index 072c375..66299f2 100644
--- a/tests/utests/data/test_validation.c
+++ b/tests/utests/data/test_validation.c
@@ -412,13 +412,15 @@
                 "type string;"
             "}"
         "}";
+    struct ly_in *in;
+    const char *feats[] = {"feat1", NULL};
 
 #if ENABLE_LOGGER_CHECKING
     ly_set_log_clb(logger, 1);
 #endif
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
-    assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01"));
+    assert_non_null(ly_ctx_load_module(ctx, "ietf-netconf-with-defaults", "2011-06-01", NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_a, LYS_IN_YANG, NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_c, LYS_IN_YANG, NULL));
@@ -428,7 +430,9 @@
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_g, LYS_IN_YANG, NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_h, LYS_IN_YANG, NULL));
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_i, LYS_IN_YANG, NULL));
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_j, LYS_IN_YANG, NULL));
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(schema_j, &in));
+    assert_int_equal(LY_SUCCESS, lys_parse(ctx, in, LYS_IN_YANG, feats, NULL));
+    ly_in_free(in, 0);
 
     return 0;
 }
@@ -1225,112 +1229,6 @@
 }
 
 static void
-test_iffeature(void **state)
-{
-    *state = test_iffeature;
-
-    const char *data;
-    struct lyd_node *tree;
-    const struct lys_module *mod = ly_ctx_get_module_latest(ctx, "g");
-
-    /* get empty data */
-    tree = NULL;
-    assert_int_equal(lyd_validate_module(&tree, mod, 0, NULL), LY_SUCCESS);
-    assert_null(tree);
-
-    /* disabled by f1 */
-    data =
-    "<cont xmlns=\"urn:tests:g\">"
-        "<d>51</d>"
-    "</cont>";
-    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
-    assert_null(tree);
-    logbuf_assert("Data are disabled by \"cont\" schema node if-feature. /g:cont");
-
-    /* enable f1 */
-    assert_int_equal(lys_feature_enable(mod, "f1"), LY_SUCCESS);
-
-    /* get data with default container */
-    assert_int_equal(lyd_validate_module(&tree, mod, 0, NULL), LY_SUCCESS);
-    assert_non_null(tree);
-    lyd_free_siblings(tree);
-
-    /* disabled by f2 */
-    data =
-    "<cont xmlns=\"urn:tests:g\">"
-        "<cont2>"
-            "<e>val</e>"
-        "</cont2>"
-    "</cont>";
-    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
-    assert_null(tree);
-    logbuf_assert("Data are disabled by \"cont2\" schema node if-feature. /g:cont/cont2");
-
-    data =
-    "<cont xmlns=\"urn:tests:g\">"
-        "<a>val</a>"
-    "</cont>";
-    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
-    assert_null(tree);
-    logbuf_assert("Data are disabled by \"choic\" schema node if-feature. /g:cont/a");
-
-    /* enable f3 */
-    assert_int_equal(lys_feature_enable(mod, "f3"), LY_SUCCESS);
-
-    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
-    assert_non_null(tree);
-    lyd_free_siblings(tree);
-
-    /* disabled by f2 */
-    data =
-    "<cont xmlns=\"urn:tests:g\">"
-        "<l>val</l>"
-    "</cont>";
-    assert_int_equal(LY_EVALID, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
-    assert_null(tree);
-    logbuf_assert("Data are disabled by \"b\" schema node if-feature. /g:cont/l");
-
-    /* enable f2 */
-    assert_int_equal(lys_feature_enable(mod, "f2"), LY_SUCCESS);
-
-    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree));
-    assert_non_null(tree);
-    lyd_free_siblings(tree);
-
-    /* try separate validation */
-    assert_int_equal(lys_feature_disable(mod, "f1"), LY_SUCCESS);
-    assert_int_equal(lys_feature_disable(mod, "f2"), LY_SUCCESS);
-    assert_int_equal(lys_feature_disable(mod, "f3"), LY_SUCCESS);
-
-    data =
-    "<cont xmlns=\"urn:tests:g\">"
-        "<l>val</l>"
-        "<d>51</d>"
-        "<cont2>"
-            "<e>val</e>"
-        "</cont2>"
-    "</cont>";
-    assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_ONLY, 0, &tree));
-    assert_non_null(tree);
-
-    assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
-    logbuf_assert("Data are disabled by \"cont\" schema node if-feature. /g:cont");
-
-    assert_int_equal(lys_feature_enable(mod, "f1"), LY_SUCCESS);
-
-    assert_int_equal(LY_EVALID, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
-    logbuf_assert("Data are disabled by \"b\" schema node if-feature. /g:cont/l");
-
-    assert_int_equal(lys_feature_enable(mod, "f2"), LY_SUCCESS);
-
-    assert_int_equal(LY_SUCCESS, lyd_validate_all(&tree, NULL, LYD_VALIDATE_PRESENT, NULL));
-
-    lyd_free_siblings(tree);
-
-    *state = NULL;
-}
-
-static void
 test_state(void **state)
 {
     *state = test_state;
@@ -1400,7 +1298,6 @@
     const char *data;
     struct ly_in *in;
     struct lyd_node *tree, *op_tree;
-    const struct lys_module *mod;
 
     data =
     "<cont xmlns=\"urn:tests:j\">"
@@ -1429,14 +1326,6 @@
     assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_TRUSTED, 0, &tree));
     assert_non_null(tree);
 
-    /* disabled if-feature */
-    assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
-    logbuf_assert("Data are disabled by \"act\" schema node if-feature. /j:cont/l1[k='val1']/act");
-
-    mod = ly_ctx_get_module_latest(ctx, "j");
-    assert_non_null(mod);
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "feat1"));
-
     /* input must false */
     assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
     logbuf_assert("Must condition \"../../lf1 = 'true'\" not satisfied. /j:cont/l1[k='val1']/act");
@@ -1453,7 +1342,6 @@
     /* success */
     assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_RPC, NULL));
 
-    lys_feature_disable(mod, "feat1");
     lyd_free_tree(op_tree);
     lyd_free_siblings(tree);
 
@@ -1468,7 +1356,6 @@
     const char *data;
     struct ly_in *in;
     struct lyd_node *tree, *op_tree, *request;
-    const struct lys_module *mod;
 
     data =
     "<cont xmlns=\"urn:tests:j\">"
@@ -1504,14 +1391,6 @@
     assert_int_equal(LY_SUCCESS, lyd_parse_data_mem(ctx, data, LYD_XML, LYD_PARSE_ONLY | LYD_PARSE_TRUSTED, 0, &tree));
     assert_non_null(tree);
 
-    /* disabled if-feature */
-    assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
-    logbuf_assert("Data are disabled by \"act\" schema node if-feature. /j:cont/l1[k='val1']/act");
-
-    mod = ly_ctx_get_module_latest(ctx, "j");
-    assert_non_null(mod);
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "feat1"));
-
     /* input must false */
     assert_int_equal(LY_EVALID, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
     logbuf_assert("Must condition \"../../lf1 = 'true2'\" not satisfied. /j:cont/l1[k='val1']/act");
@@ -1528,7 +1407,6 @@
     /* success */
     assert_int_equal(LY_SUCCESS, lyd_validate_op(op_tree, tree, LYD_VALIDATE_OP_REPLY, NULL));
 
-    lys_feature_disable(mod, "feat1");
     lyd_free_tree(op_tree);
     lyd_free_siblings(tree);
 
@@ -1545,7 +1423,6 @@
         cmocka_unit_test_teardown(test_unique_nested, teardown_s),
         cmocka_unit_test_teardown(test_dup, teardown_s),
         cmocka_unit_test_teardown(test_defaults, teardown_s),
-        cmocka_unit_test_teardown(test_iffeature, teardown_s),
         cmocka_unit_test_teardown(test_state, teardown_s),
         cmocka_unit_test_teardown(test_must, teardown_s),
         cmocka_unit_test_teardown(test_action, teardown_s),
diff --git a/tests/utests/extensions/test_metadata.c b/tests/utests/extensions/test_metadata.c
index 523bcdc..4f1ac74 100644
--- a/tests/utests/extensions/test_metadata.c
+++ b/tests/utests/extensions/test_metadata.c
@@ -109,6 +109,7 @@
     const struct lys_module *mod;
     struct lysc_ext_instance *e;
     struct lyext_metadata *ant;
+    struct ly_in *in;
 
     const char *data = "module a {yang-version 1.1; namespace urn:tests:extensions:metadata:a; prefix a;"
             "import ietf-yang-metadata {prefix md;}"
@@ -121,7 +122,10 @@
             "  type uint8;"
             "  units meters;"
             "}}";
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(s->ctx, data, LYS_IN_YANG, &mod));
+    const char *feats[] = {"f", NULL};
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lys_parse(s->ctx, in, LYS_IN_YANG, feats, &mod));
+    ly_in_free(in, 0);
     assert_int_equal(1, LY_ARRAY_COUNT(mod->compiled->exts));
     e = &mod->compiled->exts[0];
     assert_non_null(ant = (struct lyext_metadata*)e->data);
diff --git a/tests/utests/extensions/test_nacm.c b/tests/utests/extensions/test_nacm.c
index 86b2b80..04b4407 100644
--- a/tests/utests/extensions/test_nacm.c
+++ b/tests/utests/extensions/test_nacm.c
@@ -66,7 +66,7 @@
 #endif
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &s->ctx));
-    assert_non_null(ly_ctx_load_module(s->ctx, "ietf-netconf-acm", "2018-02-14"));
+    assert_non_null(ly_ctx_load_module(s->ctx, "ietf-netconf-acm", "2018-02-14", NULL));
 
     *state = s;
 
diff --git a/tests/utests/schema/test_printer_yang.c b/tests/utests/schema/test_printer_yang.c
index 52528eb..b59e6b2 100644
--- a/tests/utests/schema/test_printer_yang.c
+++ b/tests/utests/schema/test_printer_yang.c
@@ -171,17 +171,7 @@
     compiled = "module b {\n"
             "  namespace \"urn:test:b\";\n"
             "  prefix b;\n\n"
-            "  revision 2019-04-16;\n\n"
-            "  feature f1 {\n"
-            "    status current;\n"
-            "    description\n"
-            "      \"text\";\n"
-            "    reference\n"
-            "      \"text\";\n"
-            "  }\n\n"
-            "  feature f2 {\n"
-            "    if-feature \"not f1\";\n"
-            "  }\n"
+            "  revision 2019-04-16;\n"
             "}\n";
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, orig, LYS_IN_YANG, &mod));
     assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YANG, 0, 0));
@@ -193,7 +183,7 @@
     assert_string_equal(printed, compiled);
     ly_out_reset(out);
 
-    orig = compiled = "module c {\n"
+    orig = "module c {\n"
             "  yang-version 1.1;\n"
             "  namespace \"urn:test:c\";\n"
             "  prefix c;\n\n"
@@ -210,12 +200,16 @@
             "    status obsolete;\n"
             "  }\n"
             "}\n";
+    compiled = "module c {\n"
+            "  namespace \"urn:test:c\";\n"
+            "  prefix c;\n"
+            "}\n";
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, orig, LYS_IN_YANG, &mod));
     assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YANG, 0, 0));
     assert_int_equal(strlen(orig), ly_out_printed(out));
     assert_string_equal(printed, orig);
     ly_out_reset(out);
-    assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YANG, 0, 0));
+    assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YANG_COMPILED, 0, 0));
     assert_int_equal(strlen(compiled), ly_out_printed(out));
     assert_string_equal(printed, compiled);
 
diff --git a/tests/utests/schema/test_schema_common.c b/tests/utests/schema/test_schema_common.c
index a43d6fc..dc50517 100644
--- a/tests/utests/schema/test_schema_common.c
+++ b/tests/utests/schema/test_schema_common.c
@@ -124,23 +124,15 @@
     assert_string_equal("h-output", node->name);
     assert_null(node = lys_getnext(node, (const struct lysc_node*)rpc, mod->compiled, LYS_GETNEXT_OUTPUT));
 
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module b {namespace urn:b;prefix b; feature f;"
-                                        "leaf a {type string; if-feature f;}"
-                                        "leaf b {type string;}}", LYS_IN_YANG, &mod));
-    assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
-    assert_string_equal("b", node->name);
-    assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, LYS_GETNEXT_NOSTATECHECK));
-    assert_string_equal("a", node->name);
-
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module c {namespace urn:c;prefix c; rpc c;}", LYS_IN_YANG, &mod));
     assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
     assert_string_equal("c", node->name);
-    assert_null(node = lys_getnext(node, NULL, mod->compiled, LYS_GETNEXT_NOSTATECHECK));
+    assert_null(node = lys_getnext(node, NULL, mod->compiled, 0));
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module d {namespace urn:d;prefix d; notification d;}", LYS_IN_YANG, &mod));
     assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
     assert_string_equal("d", node->name);
-    assert_null(node = lys_getnext(node, NULL, mod->compiled, LYS_GETNEXT_NOSTATECHECK));
+    assert_null(node = lys_getnext(node, NULL, mod->compiled, 0));
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module e {namespace urn:e;prefix e; container c {container cc;} leaf a {type string;}}", LYS_IN_YANG, &mod));
     assert_non_null(node = lys_getnext(NULL, NULL, mod->compiled, 0));
diff --git a/tests/utests/schema/test_schema_stmts.c b/tests/utests/schema/test_schema_stmts.c
index 11512ac..55400b4 100644
--- a/tests/utests/schema/test_schema_stmts.c
+++ b/tests/utests/schema/test_schema_stmts.c
@@ -116,11 +116,11 @@
     assert_int_equal(1, LY_ARRAY_COUNT(mod->identities[1].derived));
     assert_ptr_equal(mod->identities[1].derived[0], &mod->identities[0]);
 
-    TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "identity i1;identity i1;", "Duplicate identifier \"i1\" of identity statement. /inv:{identity='i1'}");
+    TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "identity i1;identity i1;", "Duplicate identifier \"i1\" of identity statement. Line number 1.");
 
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} identity i1;}");
     TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "include inv_sub;identity i1;",
-                    "Duplicate identifier \"i1\" of identity statement. /inv:{identity='i1'}");
+                    "Duplicate identifier \"i1\" of identity statement. Line number 1.");
     TEST_SCHEMA_ERR(ctx, 0, 0,"inv", "identity i1 {base i2;}", "Unable to find base (i2) of identity \"i1\". /inv:{identity='i1'}");
     TEST_SCHEMA_ERR(ctx, 0, 0,"inv", "identity i1 {base i1;}", "Identity \"i1\" is derived from itself. /inv:{identity='i1'}");
     TEST_SCHEMA_ERR(ctx, 0, 0,"inv", "identity i1 {base i2;}identity i2 {base i3;}identity i3 {base i1;}",
@@ -160,7 +160,7 @@
 
     struct ly_ctx *ctx;
     const struct lys_module *mod;
-    const struct lysc_feature *f, *f1;
+    const struct lysp_feature *f;
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, 0, &ctx));
 
@@ -223,144 +223,51 @@
                    "feature f7 {if-feature \"(f2 and f3) or (not f1)\";}\n"
                    "feature f8 {if-feature \"f1 or f2 or f3 or orfeature or andfeature\";}\n"
                    "feature f9 {if-feature \"not not f1\";}", mod);
-    assert_non_null(mod->features);
-    assert_int_equal(9, LY_ARRAY_COUNT(mod->features));
+    assert_non_null(mod->parsed->features);
+    assert_int_equal(9, LY_ARRAY_COUNT(mod->parsed->features));
 
     /* all features are disabled by default */
-    LY_ARRAY_FOR(mod->features, struct lysc_feature, f) {
-        assert_int_equal(LY_ENOT, lysc_feature_value(f));
+    LY_ARRAY_FOR(mod->parsed->features, struct lysp_feature, f) {
+        assert_false(f->flags & LYS_FENABLED);
     }
-    /* enable f1 */
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f1"));
-    f1 = &mod->features[0];
-    assert_int_equal(LY_SUCCESS, lysc_feature_value(f1));
-
-    /* enable orfeature */
-    f = &mod->features[3];
-    assert_int_equal(LY_ENOT, lysc_feature_value(f));
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "orfeature"));
-    assert_int_equal(LY_SUCCESS, lysc_feature_value(f));
-
-    /* enable andfeature - no possible since f2 is disabled */
-    f = &mod->features[4];
-    assert_int_equal(LY_ENOT, lysc_feature_value(f));
-    assert_int_equal(LY_EDENIED, lys_feature_enable(mod, "andfeature"));
-    logbuf_assert("Feature \"andfeature\" cannot be enabled since it is disabled by its if-feature condition(s).");
-    assert_int_equal(LY_ENOT, lysc_feature_value(f));
-
-    /* first enable f2, so f5 can be enabled then */
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f2"));
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "andfeature"));
-    assert_int_equal(LY_SUCCESS, lysc_feature_value(f));
-
-    /* f1 is enabled, so f6 cannot be enabled */
-    f = &mod->features[5];
-    assert_int_equal(LY_ENOT, lysc_feature_value(f));
-    assert_int_equal(LY_EDENIED, lys_feature_enable(mod, "f6"));
-    logbuf_assert("Feature \"f6\" cannot be enabled since it is disabled by its if-feature condition(s).");
-    assert_int_equal(LY_ENOT, lysc_feature_value(f));
-
-    /* so disable f1 - andfeature will became also disabled */
-    assert_int_equal(LY_SUCCESS, lysc_feature_value(f1));
-    assert_int_equal(LY_SUCCESS, lys_feature_disable(mod, "f1"));
-    assert_int_equal(LY_ENOT, lysc_feature_value(f1));
-    assert_int_equal(LY_ENOT, lysc_feature_value(&mod->features[4]));
-    /* while orfeature is stille enabled */
-    assert_int_equal(LY_SUCCESS, lysc_feature_value(&mod->features[3]));
-    /* and finally f6 can be enabled */
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f6"));
-    assert_int_equal(LY_SUCCESS, lysc_feature_value(&mod->features[5]));
-
-    /* complex evaluation of f7: f1 and f3 are disabled, while f2 is enabled */
-    assert_int_equal(LY_SUCCESS, lysc_iffeature_value(&mod->features[6].iffeatures[0]));
-    /* long evaluation of f8 to need to reallocate internal stack for operators */
-    assert_int_equal(LY_SUCCESS, lysc_iffeature_value(&mod->features[7].iffeatures[0]));
-
-    /* double negation of disabled f1 -> disabled */
-    assert_int_equal(LY_ENOT, lysc_iffeature_value(&mod->features[8].iffeatures[0]));
-
-    /* disable all features */
-    assert_int_equal(LY_SUCCESS, lys_feature_disable(mod, "*"));
-    LY_ARRAY_FOR(mod->features, struct lysc_feature, f) {
-        assert_int_equal(LY_ENOT, lys_feature_value(mod, f->name));
-    }
-    /* re-setting already set feature */
-    assert_int_equal(LY_SUCCESS, lys_feature_disable(mod, "f1"));
-    assert_int_equal(LY_ENOT, lys_feature_value(mod, "f1"));
-
-    /* enabling feature that cannot be enabled due to its if-features */
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f1"));
-    assert_int_equal(LY_EDENIED, lys_feature_enable(mod, "andfeature"));
-    logbuf_assert("Feature \"andfeature\" cannot be enabled since it is disabled by its if-feature condition(s).");
-    assert_int_equal(LY_EDENIED, lys_feature_enable(mod, "*"));
-    logbuf_assert("Feature \"f6\" cannot be enabled since it is disabled by its if-feature condition(s).");
-    /* test if not changed */
-    assert_int_equal(LY_SUCCESS, lys_feature_value(mod, "f1"));
-    assert_int_equal(LY_ENOT, lys_feature_value(mod, "f2"));
-
-    TEST_SCHEMA_OK(ctx, 0, 0, "b", "feature f1 {if-feature f2;}feature f2;", mod);
-    assert_non_null(mod->features);
-    assert_int_equal(2, LY_ARRAY_COUNT(mod->features));
-    assert_non_null(mod->features[0].iffeatures);
-    assert_int_equal(1, LY_ARRAY_COUNT(mod->features[0].iffeatures));
-    assert_non_null(mod->features[0].iffeatures[0].features);
-    assert_int_equal(1, LY_ARRAY_COUNT(mod->features[0].iffeatures[0].features));
-    assert_ptr_equal(&mod->features[1], mod->features[0].iffeatures[0].features[0]);
-    assert_non_null(mod->features);
-    assert_int_equal(2, LY_ARRAY_COUNT(mod->features));
-    assert_non_null(mod->features[1].depfeatures);
-    assert_int_equal(1, LY_ARRAY_COUNT(mod->features[1].depfeatures));
-    assert_ptr_equal(&mod->features[0], mod->features[1].depfeatures[0]);
-
-    /* invalid reference */
-    assert_int_equal(LY_ENOTFOUND, lys_feature_enable(mod, "xxx"));
-    logbuf_assert("Feature \"xxx\" not found in module \"b\".");
 
     /* some invalid expressions */
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f{if-feature f1;}",
-                    "Invalid value \"f1\" of if-feature - unable to find feature \"f1\". /inv:{feature='f'}");
+                    "Invalid value \"f1\" of if-feature - unable to find feature \"f1\".");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f2{if-feature 'f and';}",
-                    "Invalid value \"f and\" of if-feature - unexpected end of expression. /inv:{feature='f2'}");
+                    "Invalid value \"f and\" of if-feature - unexpected end of expression.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f{if-feature 'or';}",
-                    "Invalid value \"or\" of if-feature - unexpected end of expression. /inv:{feature='f'}");
+                    "Invalid value \"or\" of if-feature - unexpected end of expression.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f2{if-feature '(f1';}",
-                    "Invalid value \"(f1\" of if-feature - non-matching opening and closing parentheses. /inv:{feature='f2'}");
+                    "Invalid value \"(f1\" of if-feature - non-matching opening and closing parentheses.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f2{if-feature 'f1)';}",
-                    "Invalid value \"f1)\" of if-feature - non-matching opening and closing parentheses. /inv:{feature='f2'}");
+                    "Invalid value \"f1)\" of if-feature - non-matching opening and closing parentheses.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f2{if-feature ---;}",
-                    "Invalid value \"---\" of if-feature - unable to find feature \"---\". /inv:{feature='f2'}");
+                    "Invalid value \"---\" of if-feature - unable to find feature \"---\".");
     TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "feature f1; feature f2{if-feature 'not f1';}",
-                    "Invalid value \"not f1\" of if-feature - YANG 1.1 expression in YANG 1.0 module. /inv:{feature='f2'}");
+                    "Invalid value \"not f1\" of if-feature - YANG 1.1 expression in YANG 1.0 module.");
     TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "feature f1; feature f1;",
-                    "Duplicate identifier \"f1\" of feature statement. /inv:{feature='f1'}");
+                    "Duplicate identifier \"f1\" of feature statement. Line number 1.");
 
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule inv_sub {belongs-to inv {prefix inv;} feature f1;}");
     TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "include inv_sub;feature f1;",
-                    "Duplicate identifier \"f1\" of feature statement. /inv:{feature='f1'}");
+                    "Duplicate identifier \"f1\" of feature statement. Line number 1.");
     TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "feature f1 {if-feature f2;} feature f2 {if-feature f1;}",
-                    "Feature \"f1\" is indirectly referenced from itself. /inv:{feature='f2'}");
+                    "Feature \"f1\" is indirectly referenced from itself.");
     TEST_SCHEMA_ERR(ctx, 0, 0, "inv", "feature f1 {if-feature f1;}",
-                    "Feature \"f1\" is referenced from itself. /inv:{feature='f1'}");
+                    "Feature \"f1\" is referenced from itself.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f {if-feature ();}",
-                    "Invalid value \"()\" of if-feature - number of features in expression does not match the required number of operands for the operations. /inv:{feature='f'}");
+                    "Invalid value \"()\" of if-feature - number of features in expression does not match the required number of operands for the operations.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f {if-feature 'f1(';}",
-                    "Invalid value \"f1(\" of if-feature - non-matching opening and closing parentheses. /inv:{feature='f'}");
+                    "Invalid value \"f1(\" of if-feature - non-matching opening and closing parentheses.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f {if-feature 'and f1';}",
-                    "Invalid value \"and f1\" of if-feature - missing feature/expression before \"and\" operation. /inv:{feature='f'}");
+                    "Invalid value \"and f1\" of if-feature - missing feature/expression before \"and\" operation.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f {if-feature 'f1 not ';}",
-                    "Invalid value \"f1 not \" of if-feature - unexpected end of expression. /inv:{feature='f'}");
+                    "Invalid value \"f1 not \" of if-feature - unexpected end of expression.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f {if-feature 'f1 not not ';}",
-                    "Invalid value \"f1 not not \" of if-feature - unexpected end of expression. /inv:{feature='f'}");
+                    "Invalid value \"f1 not not \" of if-feature - unexpected end of expression.");
     TEST_SCHEMA_ERR(ctx, 1, 0, "inv", "feature f1; feature f2; feature f {if-feature 'or f1 f2';}",
-                    "Invalid value \"or f1 f2\" of if-feature - missing feature/expression before \"or\" operation. /inv:{feature='f'}");
-
-    /* import reference */
-    assert_non_null(mod = ly_ctx_get_module(ctx, "a", NULL));
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f1"));
-    TEST_SCHEMA_OK(ctx, 0, 0, "c", "import a {prefix a;} feature f1; feature f2{if-feature 'a:f1';}", mod);
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f2"));
-    assert_int_equal(LY_ENOT, lys_feature_value(mod, "f1"));
-    assert_int_equal(LY_SUCCESS, lys_feature_value(mod, "f2"));
+                    "Invalid value \"or f1 f2\" of if-feature - missing feature/expression before \"or\" operation.");
 
     /*
      * printing
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index c1ea424..091ae9b 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -28,7 +28,6 @@
 #include "schema_compile.h"
 #include "xpath.h"
 
-void lysc_feature_free(struct ly_ctx *ctx, struct lysc_feature *feat);
 void yang_parser_ctx_free(struct lys_yang_parser_ctx *ctx);
 
 #define BUFSIZE 1024
@@ -104,7 +103,7 @@
     struct ly_in *in;
     struct ly_ctx *ctx = NULL;
     struct lys_module *mod = NULL;
-    struct lysc_feature *f;
+    struct lysp_feature *f;
     struct lysc_iffeature *iff;
 
     str = "module test {namespace urn:test; prefix t;"
@@ -114,7 +113,7 @@
     assert_int_equal(LY_EINVAL, lys_compile(NULL, 0));
     logbuf_assert("Invalid argument mod (lys_compile()).");
     assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 0, NULL, NULL, &mod));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 0, NULL, NULL, NULL, &mod));
     ly_in_free(in, 0);
     assert_int_equal(0, mod->implemented);
     assert_int_equal(LY_SUCCESS, lys_compile(mod, 0));
@@ -126,28 +125,28 @@
     assert_string_equal("urn:test", mod->ns);
     assert_string_equal("t", mod->prefix);
     /* features */
-    assert_non_null(mod->features);
-    assert_int_equal(2, LY_ARRAY_COUNT(mod->features));
-    f = &mod->features[1];
+    assert_non_null(mod->parsed->features);
+    assert_int_equal(2, LY_ARRAY_COUNT(mod->parsed->features));
+    f = &mod->parsed->features[1];
     assert_non_null(f->iffeatures);
     assert_int_equal(1, LY_ARRAY_COUNT(f->iffeatures));
-    iff = &f->iffeatures[0];
+    iff = &f->iffeatures_c[0];
     assert_non_null(iff->expr);
     assert_non_null(iff->features);
     assert_int_equal(1, LY_ARRAY_COUNT(iff->features));
-    assert_ptr_equal(&mod->features[0], iff->features[0]);
+    assert_ptr_equal(&mod->parsed->features[0], iff->features[0]);
 
     /* submodules cannot be compiled directly */
     str = "submodule test {belongs-to xxx {prefix x;}}";
     assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
-    assert_int_equal(LY_EINVAL, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL));
+    assert_int_equal(LY_EINVAL, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL, NULL));
     ly_in_free(in, 0);
     logbuf_assert("Input data contains submodule which cannot be parsed directly without its main module.");
 
     /* data definition name collision in top level */
     str = "module aa {namespace urn:aa;prefix aa; leaf a {type string;} container a{presence x;}}";
     assert_int_equal(LY_SUCCESS, ly_in_new_memory(str, &in));
-    assert_int_equal(LY_EEXIST, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, &mod));
+    assert_int_equal(LY_EEXIST, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod));
     logbuf_assert("Duplicate identifier \"a\" of data definition/RPC/action/notification statement. /aa:a");
     ly_in_free(in, 0);
 
@@ -399,12 +398,18 @@
     const struct lys_module *mod;
     struct lysc_node_list *list;
     struct lysc_node *child;
+    struct ly_in *in;
+    const char *data =
+        "module a {namespace urn:a;prefix a;feature f;"
+            "list l1 {key \"x y\"; ordered-by user; leaf y{type string;if-feature f;} leaf x {type string; when 1;}}"
+            "list l2 {config false;leaf value {type string;}}}";
+    const char *feats[] = {"f", NULL};
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
 
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module a {namespace urn:a;prefix a;feature f;"
-                                        "list l1 {key \"x y\"; ordered-by user; leaf y{type string;if-feature f;} leaf x {type string; when 1;}}"
-                                        "list l2 {config false;leaf value {type string;}}}", LYS_IN_YANG, &mod));
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lys_parse(ctx, in, LYS_IN_YANG, feats, &mod));
+    ly_in_free(in, 0);
     list = (struct lysc_node_list*)mod->compiled->data;
     assert_non_null(list);
     assert_non_null(list->child);
@@ -484,7 +489,7 @@
 
     assert_int_equal(LY_EVALID, lys_parse_mem(ctx, "module cc {yang-version 1.1;namespace urn:cc;prefix cc;feature f;"
                               "list l {key x; leaf x {type string; if-feature f;}}}", LYS_IN_YANG, NULL));
-    logbuf_assert("List's key must not have any \"if-feature\" statement. /cc:l/x");
+    logbuf_assert("Key \"x\" is disabled by its if-features. /cc:l/x");
 
     assert_int_equal(LY_EVALID, lys_parse_mem(ctx, "module dd {namespace urn:dd;prefix dd;"
                               "list l {key x; leaf x {type string; config false;}}}", LYS_IN_YANG, NULL));
@@ -1203,20 +1208,17 @@
     assert_non_null(type);
     assert_int_equal(LY_TYPE_ENUM, type->basetype);
     assert_non_null(((struct lysc_type_enum*)type)->enums);
-    assert_int_equal(6, LY_ARRAY_COUNT(((struct lysc_type_enum*)type)->enums));
-    assert_non_null(((struct lysc_type_enum*)type)->enums[2].iffeatures);
+    assert_int_equal(5, LY_ARRAY_COUNT(((struct lysc_type_enum*)type)->enums));
     assert_string_equal("automin", ((struct lysc_type_enum*)type)->enums[0].name);
     assert_int_equal(0, ((struct lysc_type_enum*)type)->enums[0].value);
     assert_string_equal("min", ((struct lysc_type_enum*)type)->enums[1].name);
     assert_int_equal(-2147483648, ((struct lysc_type_enum*)type)->enums[1].value);
-    assert_string_equal("one", ((struct lysc_type_enum*)type)->enums[2].name);
-    assert_int_equal(1, ((struct lysc_type_enum*)type)->enums[2].value);
-    assert_string_equal("two", ((struct lysc_type_enum*)type)->enums[3].name);
-    assert_int_equal(2, ((struct lysc_type_enum*)type)->enums[3].value);
-    assert_string_equal("seven", ((struct lysc_type_enum*)type)->enums[4].name);
-    assert_int_equal(7, ((struct lysc_type_enum*)type)->enums[4].value);
-    assert_string_equal("eight", ((struct lysc_type_enum*)type)->enums[5].name);
-    assert_int_equal(8, ((struct lysc_type_enum*)type)->enums[5].value);
+    assert_string_equal("two", ((struct lysc_type_enum*)type)->enums[2].name);
+    assert_int_equal(2, ((struct lysc_type_enum*)type)->enums[2].value);
+    assert_string_equal("seven", ((struct lysc_type_enum*)type)->enums[3].name);
+    assert_int_equal(7, ((struct lysc_type_enum*)type)->enums[3].value);
+    assert_string_equal("eight", ((struct lysc_type_enum*)type)->enums[4].name);
+    assert_int_equal(8, ((struct lysc_type_enum*)type)->enums[4].value);
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module b {yang-version 1.1; namespace urn:b;prefix b;feature f; typedef mytype {type enumeration {"
                                         "enum 11; enum min {value -2147483648;}enum x$&;"
@@ -1307,18 +1309,15 @@
     assert_non_null(type);
     assert_int_equal(LY_TYPE_BITS, type->basetype);
     assert_non_null(((struct lysc_type_bits*)type)->bits);
-    assert_int_equal(5, LY_ARRAY_COUNT(((struct lysc_type_bits*)type)->bits));
-    assert_non_null(((struct lysc_type_bits*)type)->bits[1].iffeatures);
+    assert_int_equal(4, LY_ARRAY_COUNT(((struct lysc_type_bits*)type)->bits));
     assert_string_equal("automin", ((struct lysc_type_bits*)type)->bits[0].name);
     assert_int_equal(0, ((struct lysc_type_bits*)type)->bits[0].position);
-    assert_string_equal("one", ((struct lysc_type_bits*)type)->bits[1].name);
-    assert_int_equal(1, ((struct lysc_type_bits*)type)->bits[1].position);
-    assert_string_equal("two", ((struct lysc_type_bits*)type)->bits[2].name);
-    assert_int_equal(2, ((struct lysc_type_bits*)type)->bits[2].position);
-    assert_string_equal("seven", ((struct lysc_type_bits*)type)->bits[3].name);
-    assert_int_equal(7, ((struct lysc_type_bits*)type)->bits[3].position);
-    assert_string_equal("eight", ((struct lysc_type_bits*)type)->bits[4].name);
-    assert_int_equal(8, ((struct lysc_type_bits*)type)->bits[4].position);
+    assert_string_equal("two", ((struct lysc_type_bits*)type)->bits[1].name);
+    assert_int_equal(2, ((struct lysc_type_bits*)type)->bits[1].position);
+    assert_string_equal("seven", ((struct lysc_type_bits*)type)->bits[2].name);
+    assert_int_equal(7, ((struct lysc_type_bits*)type)->bits[2].position);
+    assert_string_equal("eight", ((struct lysc_type_bits*)type)->bits[3].name);
+    assert_int_equal(8, ((struct lysc_type_bits*)type)->bits[3].position);
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module b {yang-version 1.1;namespace urn:b;prefix b;feature f; typedef mytype {type bits {"
                                         "bit automin; bit one;bit two; bit seven {position 7;}bit eight;}} leaf l { type mytype {bit eight;bit seven;bit automin;}}}",
@@ -1698,7 +1697,7 @@
     assert_int_equal(1, ((struct lysc_type_leafref* )type)->require_instance);
 
     /* conditional leafrefs */
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module e {yang-version 1.1;namespace urn:e;prefix e;feature f1; feature f2;"
+    /*assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module e {yang-version 1.1;namespace urn:e;prefix e;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, &mod));
     type = ((struct lysc_node_leaf*)mod->compiled->data)->type;
@@ -1708,7 +1707,7 @@
     assert_string_equal("/target", ((struct lysc_type_leafref* )type)->path->expr);
     assert_int_equal(0, LY_ARRAY_COUNT(((struct lysc_type_leafref*)type)->prefixes));
     assert_non_null(((struct lysc_type_leafref*)type)->realtype);
-    assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref*)type)->realtype->basetype);
+    assert_int_equal(LY_TYPE_BOOL, ((struct lysc_type_leafref*)type)->realtype->basetype);*/
 
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module f {namespace urn:f;prefix f;"
                                         "list interface{key name;leaf name{type string;}list address {key ip;leaf ip {type string;}}}"
@@ -1812,8 +1811,7 @@
     logbuf_assert("Missing path substatement for leafref type mytype. /ii:ref1");
     assert_int_equal(LY_EVALID, 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, &mod));
-    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. /jj:ref");
+    logbuf_assert("Not found node \"target\" in path. /jj:ref");
     assert_int_equal(LY_EVALID, lys_parse_mem(ctx, "module kk {namespace urn:kk;prefix kk;"
                                         "leaf ref {type leafref {path /target;}}leaf target {type string;config false;}}", LYS_IN_YANG, &mod));
     logbuf_assert("Invalid leafref path \"/target\" - target is supposed to represent configuration data (as the leafref does), but it does not. /kk:ref");
@@ -2242,22 +2240,7 @@
     assert_non_null((child = child->next));
     assert_string_equal("x", child->name);
     assert_ptr_equal(mod, child->module);
-    assert_non_null((child = child->next));
-    assert_string_equal("y", child->name);
-    assert_non_null(child->iffeatures);
-    assert_int_equal(1, LY_ARRAY_COUNT(child->iffeatures));
-    assert_int_equal(1, LY_ARRAY_COUNT(child->iffeatures[0].features));
-    assert_string_equal("f", child->iffeatures[0].features[0]->name);
-    assert_int_equal(LY_EINVAL, lys_feature_enable(mod->parsed->imports[0].module, "f"));
-    logbuf_assert("Module \"grp\" is not implemented so all its features are permanently disabled.");
-    assert_int_equal(LY_ENOT, lysc_iffeature_value(&child->iffeatures[0]));
-
-    /* make the imported module implemented and enable the feature */
-    assert_non_null(mod = ly_ctx_get_module(ctx, "grp", NULL));
-    assert_int_equal(LY_SUCCESS, lys_set_implemented((struct lys_module *)mod));
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "f"));
-    assert_string_equal("f", child->iffeatures[0].features[0]->name);
-    assert_int_equal(LY_SUCCESS, lysc_iffeature_value(&child->iffeatures[0]));
+    assert_null((child = child->next));
 
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule bsub {belongs-to b {prefix b;} grouping grp {leaf b {when 1; type string;} leaf c {type string;}}}");
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module b {namespace urn:b;prefix b;include bsub;uses grp {when 2;}}", LYS_IN_YANG, &mod));
@@ -2415,25 +2398,33 @@
     struct lysc_node_leaf *leaf;
     struct lysc_node_leaflist *llist;
     uint8_t dynamic;
+    struct ly_in *in;
+    const char *data, *feats1[] = {"f", NULL}, *feats2[] = {"fa", NULL};
 
     assert_int_equal(LY_SUCCESS, ly_ctx_new(NULL, LY_CTX_DISABLE_SEARCHDIRS, &ctx));
 
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module grp {yang-version 1.1;namespace urn:grp;prefix g; feature f;typedef mytype {type string; default cheers!;}"
+    data = "module grp {yang-version 1.1;namespace urn:grp;prefix g; feature f;typedef mytype {type string; default cheers!;}"
                                        "grouping grp {container c {leaf l {type mytype; default goodbye;}"
                                        "leaf-list ll {type mytype; default goodbye; max-elements 6;}"
                                        "choice ch {default ca; leaf ca {type int8;}leaf cb{type uint8;}}"
                                        "leaf x {type mytype; mandatory true; must 1;}"
                                        "anydata a {mandatory false; if-feature f; description original; reference original;}"
-                                       "container c {config false; leaf l {type string;}}}}}", LYS_IN_YANG, NULL));
+                                       "container c {config false; leaf l {type string;}}}}}";
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(data, &in));
+    assert_int_equal(LY_SUCCESS, lys_parse(ctx, in, LYS_IN_YANG, feats1, NULL));
 
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, "module a {yang-version 1.1;namespace urn:a;prefix a;import grp {prefix g;}feature fa;"
+    data = "module a {yang-version 1.1;namespace urn:a;prefix a;import grp {prefix g;}feature fa;"
                                         "uses g:grp {refine c/l {default hello; config false;}"
                                         "refine c/ll {default hello;default world;}"
                                         "refine c/ch {default cb;config true; if-feature fa;}"
                                         "refine c/x {mandatory false; must ../ll;description refined; reference refined;}"
-                                        "refine c/a {mandatory true; must 1; if-feature fa;description refined; reference refined;}"
+                                        "refine c/a {mandatory true; must 1; description refined; reference refined;}"
                                         "refine c/ll {max-elements 5;}"
-                                        "refine c/c {config true;presence indispensable;}}}", LYS_IN_YANG, &mod));
+                                        "refine c/c {config true;presence indispensable;}}}";
+    ly_in_memory(in, data);
+    assert_int_equal(LY_SUCCESS, lys_parse(ctx, in, LYS_IN_YANG, feats2, &mod));
+    ly_in_free(in, 0);
+
     assert_non_null((parent = mod->compiled->data));
     assert_int_equal(LYS_CONTAINER, parent->nodetype);
     assert_string_equal("c", parent->name);
@@ -2458,8 +2449,6 @@
     assert_string_equal("cb", ((struct lysc_node_choice*)child)->dflt->name);
     assert_true(LYS_SET_DFLT & ((struct lysc_node_choice*)child)->dflt->flags);
     assert_false(LYS_SET_DFLT & ((struct lysc_node_choice*)child)->cases[0].flags);
-    assert_non_null(child->iffeatures);
-    assert_int_equal(1, LY_ARRAY_COUNT(child->iffeatures));
     assert_non_null(leaf = (struct lysc_node_leaf*)child->next);
     assert_int_equal(LYS_LEAF, leaf->nodetype);
     assert_string_equal("x", leaf->name);
@@ -2476,8 +2465,6 @@
     assert_true(LYS_MAND_TRUE & child->flags);
     assert_non_null(((struct lysc_node_anydata*)child)->musts);
     assert_int_equal(1, LY_ARRAY_COUNT(((struct lysc_node_anydata*)child)->musts));
-    assert_non_null(child->iffeatures);
-    assert_int_equal(2, LY_ARRAY_COUNT(child->iffeatures));
     assert_string_equal("refined", child->dsc);
     assert_string_equal("refined", child->ref);
     assert_non_null(child = child->next);
@@ -3201,7 +3188,7 @@
     assert_int_equal(LY_EVALID, lys_parse_mem(ctx, "module jj5 {namespace urn:jj5;prefix jj5; container top {leaf x {type string; config true;}}"
                               "deviation /top {deviate add {config false;}}}", LYS_IN_YANG, &mod));
     /*logbuf_assert("Invalid deviation of config - configuration node cannot be child of any state data node. /jj5:{deviation='/top'}");*/
-    logbuf_assert("Compilation of a deviated and/or refined node failed. /jj5:top/x");
+    logbuf_assert("Compilation of a deviated and/or refined node failed. /jj5:top");
     assert_int_equal(LY_EVALID, lys_parse_mem(ctx, "module jj6 {namespace urn:jj6;prefix jj6; leaf x {config false; type string;}"
                               "deviation /x {deviate add {config true;}}}", LYS_IN_YANG, &mod));
     logbuf_assert("Invalid deviation adding \"config\" property which already exists (with value \"config false\"). /jj6:{deviation='/x'}");
diff --git a/tests/utests/test_context.c b/tests/utests/test_context.c
index 35c9664..6526abc 100644
--- a/tests/utests/test_context.c
+++ b/tests/utests/test_context.c
@@ -286,7 +286,7 @@
     assert_int_equal(ctx->module_set_id, ly_ctx_get_module_set_id(ctx));
 
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module x {namespace urn:x;prefix x;}", &in));
-    assert_int_equal(LY_EINVAL, lys_create_module(ctx, in, 4, 1, NULL, NULL, &mod1));
+    assert_int_equal(LY_EINVAL, lys_create_module(ctx, in, 4, 1, NULL, NULL, NULL, &mod1));
     ly_in_free(in, 0);
     logbuf_assert("Invalid schema input format.");
 
@@ -304,22 +304,22 @@
     /* name collision of module and submodule */
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-30;}");
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;include y;}", &in));
-    assert_int_equal(LY_EVALID, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, &mod1));
+    assert_int_equal(LY_EVALID, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod1));
     ly_in_free(in, 0);
     logbuf_assert("Name collision between module and submodule of name \"y\". Line number 1.");
 
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y;revision 2018-10-30; }", &in));
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, &mod1));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod1));
     ly_in_free(in, 0);
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module y {namespace urn:y;prefix y;}", &in));
-    assert_int_equal(LY_EVALID, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, &mod1));
+    assert_int_equal(LY_EVALID, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod1));
     ly_in_free(in, 0);
     logbuf_assert("Name collision between module and submodule of name \"y\". Line number 1.");
 
     store = 1;
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule y {belongs-to b {prefix b;}}");
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module b {namespace urn:b;prefix b;include y;}", &in));
-    assert_int_equal(LY_EVALID, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, &mod1));
+    assert_int_equal(LY_EVALID, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod1));
     ly_in_free(in, 0);
     logbuf_assert("Name collision between submodules of name \"y\". Line number 1.");
     store = -1;
@@ -328,13 +328,13 @@
     ly_ctx_reset_latests(ctx);
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "submodule y {belongs-to a {prefix a;} revision 2018-10-31;}");
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module a {namespace urn:a;prefix a;include y; revision 2018-10-31;}", &in));
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 0, NULL, NULL, &mod2));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 0, NULL, NULL, NULL, &mod2));
     ly_in_free(in, 0);
     assert_string_equal("2018-10-31", mod2->parsed->includes[0].submodule->revs[0].date);
 
     /* reloading module in case only the compiled module resists in the context */
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module w {namespace urn:w;prefix w;revision 2018-10-24;}", &in));
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 0, NULL, NULL, &mod1));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 0, NULL, NULL, NULL, &mod1));
     ly_in_free(in, 0);
     mod1->implemented = 1;
     assert_int_equal(LY_SUCCESS, lys_compile(mod1, 0));
@@ -356,7 +356,7 @@
 
     assert_int_equal(LY_SUCCESS, ly_in_new_memory("module z {namespace urn:z;prefix z;import w {prefix w;revision-date 2018-10-24;}}", &in));
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, "module w {namespace urn:w;prefix w;revision 2018-10-24;}");
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, &mod2));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod2));
     ly_in_free(in, 0);
     assert_non_null(mod2);
     assert_non_null(mod1->parsed);
@@ -461,16 +461,16 @@
     assert_non_null(ly_ctx_get_module_ns(ctx, "urn:ietf:params:xml:ns:yang:ietf-datastores", "2018-02-14"));
 
     /* select module by revision */
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in1, LYS_IN_YANG, 1, NULL, NULL, &mod));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in1, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod));
     /* invalid attempts - implementing module of the same name and inserting the same module */
-    assert_int_equal(LY_EDENIED, lys_create_module(ctx, in2, LYS_IN_YANG, 1, NULL, NULL, NULL));
+    assert_int_equal(LY_EDENIED, lys_create_module(ctx, in2, LYS_IN_YANG, 1, NULL, NULL, NULL, NULL));
     logbuf_assert("Module \"a\" is already implemented in the context.");
     ly_in_reset(in1);
-    assert_int_equal(LY_EEXIST, lys_create_module(ctx, in1, LYS_IN_YANG, 0, NULL, NULL, NULL));
+    assert_int_equal(LY_EEXIST, lys_create_module(ctx, in1, LYS_IN_YANG, 0, NULL, NULL, NULL, NULL));
     logbuf_assert("Module \"a\" of revision \"2018-10-23\" is already present in the context.");
     /* insert the second module only as imported, not implemented */
     ly_in_reset(in2);
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in2, LYS_IN_YANG, 0, NULL, NULL, &mod2));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in2, LYS_IN_YANG, 0, NULL, NULL, NULL, &mod2));
     assert_non_null(mod2);
     assert_ptr_not_equal(mod, mod2);
     mod = ly_ctx_get_module_latest(ctx, "a");
@@ -478,14 +478,14 @@
     mod2 = ly_ctx_get_module_latest_ns(ctx, mod->ns);
     assert_ptr_equal(mod, mod2);
     /* work with module with no revision */
-    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in0, LYS_IN_YANG, 0, NULL, NULL, &mod));
+    assert_int_equal(LY_SUCCESS, lys_create_module(ctx, in0, LYS_IN_YANG, 0, NULL, NULL, NULL, &mod));
     assert_ptr_equal(mod, ly_ctx_get_module(ctx, "a", NULL));
     assert_ptr_not_equal(mod, ly_ctx_get_module_latest(ctx, "a"));
 
     str1 = "submodule b {belongs-to a {prefix a;}}";
     ly_in_free(in1, 0);
     assert_int_equal(LY_SUCCESS, ly_in_new_memory(str1, &in1));
-    assert_int_equal(LY_EINVAL, lys_create_module(ctx, in1, LYS_IN_YANG, 1, NULL, NULL, &mod));
+    assert_int_equal(LY_EINVAL, lys_create_module(ctx, in1, LYS_IN_YANG, 1, NULL, NULL, NULL, &mod));
     logbuf_assert("Input data contains submodule which cannot be parsed directly without its main module.");
 
     while ((mod = (struct lys_module *)ly_ctx_get_module_iter(ctx, &index))) {
diff --git a/tests/utests/test_yanglib.c b/tests/utests/test_yanglib.c
index 8f72540..ae810a5 100644
--- a/tests/utests/test_yanglib.c
+++ b/tests/utests/test_yanglib.c
@@ -21,6 +21,7 @@
 #include <cmocka.h>
 
 #include "context.h"
+#include "in.h"
 #include "log.h"
 #include "set.h"
 #include "tests/config.h"
@@ -137,6 +138,8 @@
         "}"
     "}";
     const struct lys_module *mod;
+    const char *feats[] = {"feat1", NULL};
+    struct ly_in *in;
 
 #if ENABLE_LOGGER_CHECKING
     ly_set_log_clb(logger, 1);
@@ -145,8 +148,9 @@
     assert_int_equal(LY_SUCCESS, ly_ctx_new(TESTS_DIR_MODULES_YANG, 0, &ctx));
     ly_ctx_set_module_imp_clb(ctx, test_imp_clb, NULL);
 
-    assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_a, LYS_IN_YANG, &mod));
-    assert_int_equal(LY_SUCCESS, lys_feature_enable(mod, "feat1"));
+    assert_int_equal(LY_SUCCESS, ly_in_new_memory(schema_a, &in));
+    assert_int_equal(LY_SUCCESS, lys_parse(ctx, in, LYS_IN_YANG, feats, &mod));
+    ly_in_free(in, 0);
     assert_int_equal(LY_SUCCESS, lys_parse_mem(ctx, schema_b, LYS_IN_YANG, NULL));
 
     return 0;
diff --git a/tools/lint/commands.c b/tools/lint/commands.c
index 20217fb..8c00587 100644
--- a/tools/lint/commands.c
+++ b/tools/lint/commands.c
@@ -304,7 +304,7 @@
     name = strndup(arg_ptr, name_len);
 
     while (name) {
-        model = ly_ctx_load_module(ctx, name, NULL);
+        model = ly_ctx_load_module(ctx, name, NULL, NULL);
         free(name);
         if (!model) {
             /* libyang printed the error messages */
@@ -1254,35 +1254,35 @@
 
     if (!task) {
         size_t len, max_len = 0;
-        LY_ARRAY_COUNT_TYPE u;
-        struct lysc_feature *features;
+        uint32_t idx = 0;
+        const struct lysp_feature *f = NULL;
 
         printf("%s features:\n", module->name);
 
-        features = module->features;
-
         /* get the max len */
-        LY_ARRAY_FOR(features, u) {
-            len = strlen(features[u].name);
+        while ((f = lysp_feature_next(f, module->parsed, &idx))) {
+            len = strlen(f->name);
             if (len > max_len) {
                 max_len = len;
             }
         }
 
-        LY_ARRAY_FOR(features, u) {
-            printf("\t%-*s (%s)\n", (int)max_len, features[u].name, (features[u].flags & LYS_FENABLED) ? "on" : "off");
+        idx = 0;
+        f = NULL;
+        while ((f = lysp_feature_next(f, module->parsed, &idx))) {
+            printf("\t%-*s (%s)\n", (int)max_len, f->name, (f->flags & LYS_FENABLED) ? "on" : "off");
         }
-        if (!u) {
+        if (!max_len) {
             printf("\t(none)\n");
         }
     } else {
         feat_names = strtok(feat_names, ",");
         while (feat_names) {
-            if (((task == 1) && lys_feature_enable(module, feat_names))
+            /*if (((task == 1) && lys_feature_enable(module, feat_names))
                     || ((task == 2) && lys_feature_disable(module, feat_names))) {
                 fprintf(stderr, "Feature \"%s\" not found.\n", feat_names);
                 ret = 1;
-            }
+            }*/
             feat_names = strtok(NULL, ",");
         }
     }
diff --git a/tools/lint/main_ni.c b/tools/lint/main_ni.c
index 3d30a47..70f700a 100644
--- a/tools/lint/main_ni.c
+++ b/tools/lint/main_ni.c
@@ -767,9 +767,9 @@
                     if (verbose >= 2) {
                         fprintf(stdout, "Enabling feature %s in module %s.\n", ptr, mod->name);
                     }
-                    if (lys_feature_enable(mod, ptr)) {
+                    /*if (lys_feature_enable(mod, ptr)) {
                         fprintf(stderr, "Feature %s not defined in module %s.\n", ptr, mod->name);
-                    }
+                    }*/
                 }
                 free(featlist);
                 break;
@@ -779,7 +779,7 @@
             if (verbose >= 2) {
                 fprintf(stdout, "Enabling all features in module %s.\n", mod->name);
             }
-            lys_feature_enable(mod, "*");
+            //lys_feature_enable(mod, "*");
         }
     }
     if (!out && (outformat_s || data)) {