schema compile BUGFIX if-feature in identity
Disabled identities by if-feature(s) are now also present in the lysc
tree. Checking whether the identity is not disabled by if-feature is
performed dynamically using ::lys_identity_iffeature_value(). Also the
compiled YANG printer now prints all identities, but disabled ones are
marked by comment.
diff --git a/src/plugins_types/identityref.c b/src/plugins_types/identityref.c
index 5837aaa..5a49d46 100644
--- a/src/plugins_types/identityref.c
+++ b/src/plugins_types/identityref.c
@@ -131,7 +131,7 @@
/**
* @brief Check that an identityref is derived from the type base.
*
- * @param[in] ident Identityref.
+ * @param[in] ident Derived identity to which identityref points.
* @param[in] type Identityref type.
* @param[in] value String value for logging.
* @param[in] value_len Length of @p value.
@@ -183,6 +183,44 @@
return LY_SUCCESS;
}
+/**
+ * @brief Check if @p ident is not disabled.
+ *
+ * Identity is disabled if it is located in an unimplemented model or
+ * it can be disabled by if-feature. Calling this function may invoke
+ * the implementation of another module.
+ *
+ * @param[in] ident Derived identity to which identityref points.
+ * @param[in] value Value of identityref.
+ * @param[in] value_len Length (number of bytes) of the given @p value.
+ * @param[in] options [Type plugin store options](@ref plugintypestoreopts).
+ * @param[in,out] unres Global unres structure for newly implemented modules.
+ * @param[out] err Error information on error.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+identityref_check_ident(const struct lysc_ident *ident, const char *value,
+ size_t value_len, uint32_t options, struct lys_glob_unres *unres, struct ly_err_item **err)
+{
+ LY_ERR ret = LY_SUCCESS;
+
+ if (!ident->module->implemented) {
+ if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
+ ret = lyplg_type_make_implemented(ident->module, NULL, unres);
+ } else {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
+ (int)value_len, (char *)value, ident->module->name);
+ }
+ } else if (lys_identity_iffeature_value(ident) == LY_ENOT) {
+ ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
+ "Invalid identityref \"%.*s\" value - identity is disabled by if-feature.",
+ (int)value_len, value);
+ }
+
+ return ret;
+}
+
API LY_ERR
lyplg_type_store_identityref(const struct ly_ctx *ctx, const struct lysc_type *type, const void *value, size_t value_len,
uint32_t options, LY_VALUE_FORMAT format, void *prefix_data, uint32_t hints, const struct lysc_node *ctx_node,
@@ -205,18 +243,9 @@
ret = identityref_str2ident(value, value_len, format, prefix_data, ctx, ctx_node, &ident, err);
LY_CHECK_GOTO(ret, cleanup);
- /* handle identity in a non-implemented module */
- if (!ident->module->implemented) {
- if (options & LYPLG_TYPE_STORE_IMPLEMENT) {
- ret = lyplg_type_make_implemented(ident->module, NULL, unres);
- LY_CHECK_GOTO(ret, cleanup);
- } else {
- ret = ly_err_new(err, LY_EVALID, LYVE_DATA, NULL, NULL,
- "Invalid identityref \"%.*s\" value - identity found in non-implemented module \"%s\".",
- (int)value_len, (char *)value, ident->module->name);
- goto cleanup;
- }
- }
+ /* check if the identity is enabled */
+ ret = identityref_check_ident(ident, value, value_len, options, unres, err);
+ LY_CHECK_GOTO(ret, cleanup);
/* check that the identity is derived form all the bases */
ret = identityref_check_base(ident, type_ident, value, value_len, err);
diff --git a/src/printer_yang.c b/src/printer_yang.c
index b46c957..8510faa 100644
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -551,8 +551,12 @@
yprc_extension_instances(ctx, LY_STMT_IDENTITY, 0, ident->exts, &flag, 0);
+ ypr_open(ctx->out, &flag);
+ if (lys_identity_iffeature_value(ident) == LY_ENOT) {
+ ly_print_(ctx->out, "%*s/* identity \"%s\" is disabled by if-feature(s) */\n", INDENT, ident->name);
+ }
+
LY_ARRAY_FOR(ident->derived, u) {
- ypr_open(ctx->out, &flag);
if (ctx->module != ident->derived[u]->module) {
ly_print_(ctx->out, "%*sderived %s:%s;\n", INDENT, ident->derived[u]->module->prefix, ident->derived[u]->name);
} else {
diff --git a/src/schema_compile.c b/src/schema_compile.c
index 1d6872e..8be266d 100644
--- a/src/schema_compile.c
+++ b/src/schema_compile.c
@@ -271,7 +271,6 @@
struct lysc_ctx context = {0};
struct lysc_ident *ident;
LY_ERR ret = LY_SUCCESS;
- ly_bool enabled;
assert(ctx_sc || ctx);
@@ -290,12 +289,6 @@
lysc_update_path(ctx_sc, NULL, "{identity}");
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);
/* add new compiled identity */
@@ -371,14 +364,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, ly_bool *enabled)
+ struct lysc_ident *ident, struct lysc_ident ***bases)
{
LY_ARRAY_COUNT_TYPE u, v;
const char *s, *name;
const struct lys_module *mod;
struct lysc_ident **idref;
- assert((ident && enabled) || bases);
+ assert(ident || bases);
if ((LY_ARRAY_COUNT(bases_p) > 1) && (ctx->pmod->version < LYS_VERSION_1_1)) {
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
@@ -428,18 +421,7 @@
break;
}
}
- if (!idref || !(*idref)) {
- if (ident || (ctx->compile_opts & LYS_COMPILE_DISABLED)) {
- /* 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)) {
- if (ident) {
- *enabled = 0;
- }
- return LY_SUCCESS;
- }
- }
- }
+ if (!idref) {
if (ident) {
LOGVAL(ctx->ctx, LYVE_SYNTAX_YANG,
"Unable to find base (%s) of identity \"%s\".", bases_p[u], ident->name);
@@ -451,9 +433,6 @@
}
}
- if (ident) {
- *enabled = 1;
- }
return LY_SUCCESS;
}
@@ -461,21 +440,18 @@
* @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,out] idents Array of referencing identities to which the backlinks are supposed to be set. Any
- * identities with disabled bases are removed.
+ * @param[in,out] idents Array of referencing identities to which the backlinks are supposed to be set.
* @return LY_ERR value - LY_SUCCESS or LY_EVALID.
*/
static LY_ERR
lys_compile_identities_derived(struct lysc_ctx *ctx, struct lysp_ident *idents_p, struct lysc_ident **idents)
{
LY_ARRAY_COUNT_TYPE u, v;
- ly_bool enabled;
lysc_update_path(ctx, NULL, "{identity}");
-restart:
for (u = 0, v = 0; u < LY_ARRAY_COUNT(*idents); ++u) {
- /* find matching parsed identity, the disabled ones are missing in the compiled array */
+ /* find matching parsed identity */
while (v < LY_ARRAY_COUNT(idents_p)) {
if (idents_p[v].name == (*idents)[u].name) {
break;
@@ -489,32 +465,8 @@
}
lysc_update_path(ctx, NULL, (*idents)[u].name);
- LY_CHECK_RET(lys_compile_identity_bases(ctx, ctx->pmod, idents_p[v].bases, &(*idents)[u], NULL, &enabled));
+ LY_CHECK_RET(lys_compile_identity_bases(ctx, ctx->pmod, idents_p[v].bases, &(*idents)[u], NULL));
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);
- }
-
- /* revert compilation of all the previous identities */
- for (v = 0; v < u; ++v) {
- LY_ARRAY_FREE((*idents)[v].derived);
- (*idents)[v].derived = NULL;
- }
-
- /* free the whole array if there are no identites left */
- if (!LY_ARRAY_COUNT(*idents)) {
- LY_ARRAY_FREE(*idents);
- *idents = NULL;
- }
-
- /* restart the whole process without this identity */
- goto restart;
- }
}
lysc_update_path(ctx, NULL, NULL);
diff --git a/src/schema_compile.h b/src/schema_compile.h
index 5d68677..1df79e1 100644
--- a/src/schema_compile.h
+++ b/src/schema_compile.h
@@ -186,11 +186,10 @@
* @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, ly_bool *enabled);
+ struct lysc_ident *ident, struct lysc_ident ***bases);
/**
* @brief Perform a complet compilation of identites in a module and all its submodules.
diff --git a/src/schema_compile_node.c b/src/schema_compile_node.c
index 6529a0e..0fe504d 100644
--- a/src/schema_compile_node.c
+++ b/src/schema_compile_node.c
@@ -1636,7 +1636,7 @@
}
return LY_EVALID;
}
- LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases, NULL));
+ LY_CHECK_RET(lys_compile_identity_bases(ctx, type_p->pmod, type_p->bases, NULL, &idref->bases));
}
if (!base && !type_p->flags) {
diff --git a/src/schema_features.c b/src/schema_features.c
index e54fc7b..29e6478 100644
--- a/src/schema_features.c
+++ b/src/schema_features.c
@@ -97,6 +97,31 @@
return LY_ENOT;
}
+API LY_ERR
+lys_identity_iffeature_value(const struct lysc_ident *ident)
+{
+ LY_ARRAY_COUNT_TYPE u;
+ ly_bool enabled;
+ const struct lysp_ident *idents_p;
+
+ assert(ident);
+
+ idents_p = ident->module->parsed->identities;
+ LY_ARRAY_FOR(idents_p, u) {
+ if (idents_p[u].name == ident->name) {
+ break;
+ }
+ }
+ assert(u != LY_ARRAY_COUNT(idents_p));
+
+ LY_CHECK_RET(lys_eval_iffeatures(ident->module->ctx, idents_p[u].iffeatures, &enabled));
+ if (!enabled) {
+ return LY_ENOT;
+ }
+
+ return LY_SUCCESS;
+}
+
API struct lysp_feature *
lysp_feature_next(const struct lysp_feature *last, const struct lysp_module *pmod, uint32_t *idx)
{
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 792b285..be95005 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -2154,6 +2154,19 @@
LY_ERR lysc_iffeature_value(const struct lysc_iffeature *iff);
/**
+ * @brief Get how the if-feature statement is evaluated for certain identity.
+ *
+ * The function can be called even if the identity does not contain
+ * if-features, in which case ::LY_SUCCESS is returned.
+ *
+ * @param[in] ident Compiled identity statement to evaluate.
+ * @return LY_SUCCESS if the statement evaluates to true,
+ * @return LY_ENOT if it evaluates to false,
+ * @return LY_ERR on error.
+ */
+LY_ERR lys_identity_iffeature_value(const struct lysc_ident *ident);
+
+/**
* @brief Get the next feature in the module or submodules.
*
* @param[in] last Last returned feature.
diff --git a/tests/utests/schema/test_printer_yang.c b/tests/utests/schema/test_printer_yang.c
index 293404c..d40df7f 100644
--- a/tests/utests/schema/test_printer_yang.c
+++ b/tests/utests/schema/test_printer_yang.c
@@ -134,6 +134,19 @@
compiled = "module c {\n"
" namespace \"urn:test:c\";\n"
" prefix c;\n"
+ "\n"
+ " identity i1 {\n"
+ " /* identity \"i1\" is disabled by if-feature(s) */\n"
+ " derived i2;\n"
+ " description\n"
+ " \"text\";\n"
+ " reference\n"
+ " \"text32\";\n"
+ " }\n"
+ "\n"
+ " identity i2 {\n"
+ " status obsolete;\n"
+ " }\n"
"}\n";
UTEST_ADD_MODULE(orig, LYS_IN_YANG, NULL, &mod);
assert_int_equal(LY_SUCCESS, lys_print_module(out, mod, LYS_OUT_YANG, 0, 0));
diff --git a/tests/utests/schema/test_tree_schema_compile.c b/tests/utests/schema/test_tree_schema_compile.c
index cb71297..b89b058 100644
--- a/tests/utests/schema/test_tree_schema_compile.c
+++ b/tests/utests/schema/test_tree_schema_compile.c
@@ -1476,6 +1476,7 @@
test_identity(void **state)
{
char *str;
+ const char *feats[2] = {NULL, NULL};
struct lyd_node *tree;
const char *data;
@@ -1505,6 +1506,7 @@
assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>";
CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "Schema location /b:lf, line number 1.");
assert_non_null(ly_ctx_get_module(UTEST_LYCTX, "a", NULL));
assert_false(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id3</lf>";
@@ -1532,11 +1534,11 @@
assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>";
CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
- lyd_free_tree(tree);
+ CHECK_LOG("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "Schema location /b:lf, line number 1.");
assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:c\">ids:id3</lf>";
CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
- lyd_free_tree(tree);
+ CHECK_LOG("Invalid identityref \"ids:id3\" value - identity found in non-implemented module \"c\".", "Schema location /b:lf, line number 1.");
RESET_CTX(UTEST_LYCTX);
/* Unimplemented module expand base identity located in implemented module. */
@@ -1560,7 +1562,7 @@
assert_true(contains_derived_identity(UTEST_LYCTX, "b", NULL, "baseid", "id1"));
data = "<lf xmlns=\"urn:b\" xmlns:ids=\"urn:a\">ids:id1</lf>";
CHECK_PARSE_LYD_PARAM(data, LYD_XML, LYD_PARSE_STRICT, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
- lyd_free_tree(tree);
+ CHECK_LOG("Invalid identityref \"ids:id1\" value - identity found in non-implemented module \"a\".", "Schema location /b:lf, line number 1.");
RESET_CTX(UTEST_LYCTX);
/* Transitivity of derived identity through unimplemented module. */
@@ -1676,6 +1678,75 @@
assert_false(contains_derived_identity(UTEST_LYCTX, "a", "2015-05-08", "baseid", "baseref"));
RESET_CTX(UTEST_LYCTX);
+ /* Identity testing with if-features. */
+
+ /* The if-feature has no effect if the module is imported. */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
+ "feature f;"
+ "identity baseid { if-feature \"f\";}"
+ "}";
+ ly_ctx_set_module_imp_clb(UTEST_LYCTX, test_imp_clb, str);
+ str = "module b {namespace urn:b; prefix b; import a { prefix a;}"
+ "identity id1 { base a:baseid;}"
+ "leaf lf { type identityref { base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\">id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Even if the identity in the implemented module is disabled,
+ * it can be used as a base.
+ */
+ str = "module a {yang-version 1.1; namespace urn:a; prefix a;"
+ "feature f;"
+ "identity baseid { if-feature \"f\";}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ str = "module b {namespace urn:b; prefix b; import a { prefix a;}"
+ "identity id1 { base a:baseid;}"
+ "leaf lf { type identityref { base a:baseid;}}"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\">id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ RESET_CTX(UTEST_LYCTX);
+
+ /* Identity derivation cannot be instantiated if it is disabled.
+ * Conversely, if the identity is enabled, it can be instantiated.
+ */
+ str = "module a {namespace urn:a; prefix a;"
+ "identity baseid;"
+ "}";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, NULL, NULL);
+ str = "module b {yang-version 1.1; namespace urn:b; prefix b; import a { prefix a;}"
+ "feature f2;"
+ "feature f3;"
+ "identity id1 { base a:baseid;}"
+ "identity id2 { if-feature \"f2\"; base a:baseid;}"
+ "identity id3 { if-feature \"f3\"; base a:baseid;}"
+ "leaf lf { type identityref { base a:baseid;}}"
+ "}";
+ feats[0] = "f2";
+ UTEST_ADD_MODULE(str, LYS_IN_YANG, feats, NULL);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id1"));
+ data = "<lf xmlns=\"urn:b\">id1</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id2"));
+ data = "<lf xmlns=\"urn:b\">id2</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_SUCCESS, tree);
+ lyd_free_tree(tree);
+ assert_true(contains_derived_identity(UTEST_LYCTX, "a", NULL, "baseid", "id3"));
+ data = "<lf xmlns=\"urn:b\">id3</lf>";
+ CHECK_PARSE_LYD_PARAM(data, LYD_XML, 0, LYD_VALIDATE_PRESENT, LY_EVALID, tree);
+ CHECK_LOG_CTX("Invalid identityref \"id3\" value - identity is disabled by if-feature.", "Schema location /b:lf, line number 1.");
+ RESET_CTX(UTEST_LYCTX);
+
#undef RESET_CTX
}