schema compile FEATURE validate non-instantiated groupings
diff --git a/src/tree_schema.h b/src/tree_schema.h
index a7c5017..b2d619d 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -545,6 +545,7 @@
* LYS_DOUBLEQUOTED | | | | | | | | | | | | | | | | | | | | | | |x|
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* 11 LYS_SET_MAX | | | |x|x| | | | | | | | | | | | |x| |x| | | |
+ * LYS_USED_GRP | | | | | | | | | | | | | | | |x| | | | | | | |
* ---------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*/
@@ -629,6 +630,8 @@
#define LYS_YINELEM_TRUE 0x80 /**< yin-element true for extension's argument */
#define LYS_YINELEM_FALSE 0x100 /**< yin-element false for extension's argument */
#define LYS_YINELEM_MASK 0x180 /**< mask for yin-element value */
+#define LYS_USED_GRP 0x400 /**< internal flag for validating not-instantiated groupings
+ (resp. do not validate again the instantiated groupings). */
#define LYS_SET_VALUE 0x200 /**< value attribute is set */
#define LYS_SET_MIN 0x200 /**< min attribute is set */
#define LYS_SET_MAX 0x400 /**< max attribute is set */
diff --git a/src/tree_schema_compile.c b/src/tree_schema_compile.c
index 52a102b..1f57db9 100644
--- a/src/tree_schema_compile.c
+++ b/src/tree_schema_compile.c
@@ -4443,6 +4443,10 @@
"Grouping \"%s\" references itself through a uses statement.", grp->name);
return LY_EVALID;
}
+ if (!(ctx->options & LYSC_OPT_GROUPING)) {
+ /* remember that the grouping is instantiated to avoid its standalone validation */
+ grp->flags |= LYS_USED_GRP;
+ }
/* switch context's mod_def */
mod_old = ctx->mod_def;
@@ -4753,6 +4757,45 @@
return ret;
}
+/**
+ * @brief Validate groupings that were defined but not directly used in the schema itself.
+ *
+ * The grouping does not need to be compiled (and it is compiled here, but the result is forgotten immediately),
+ * but to have the complete result of the schema validity, even such groupings are supposed to be checked.
+ */
+static LY_ERR
+lys_compile_grouping(struct lysc_ctx *ctx, struct lysp_node *node_p, struct lysp_grp *grp)
+{
+ LY_ERR ret;
+ struct lysp_node_uses fake_uses = {
+ .parent = node_p,
+ .nodetype = LYS_USES,
+ .flags = 0, .next = NULL,
+ .name = grp->name,
+ .dsc = NULL, .ref = NULL, .when = NULL, .iffeatures = NULL, .exts = NULL,
+ .refines = NULL, .augments = NULL
+ };
+ struct lysc_node_container fake_container = {
+ .nodetype = LYS_CONTAINER,
+ .flags = node_p ? (node_p->flags & LYS_FLAGS_COMPILED_MASK) : 0,
+ .module = ctx->mod,
+ .sp = NULL, .parent = NULL, .next = NULL,
+ .prev = (struct lysc_node*)&fake_container,
+ .name = "fake",
+ .dsc = NULL, .ref = NULL, .exts = NULL, .iffeatures = NULL, .when = NULL,
+ .child = NULL, .musts = NULL, .actions = NULL, .notifs = NULL
+ };
+
+ if (grp->parent) {
+ LOGWRN(ctx->ctx, "Locally scoped grouping \"%s\" not used.", grp->name);
+ }
+ ret = lys_compile_uses(ctx, &fake_uses, (struct lysc_node*)&fake_container);
+
+ /* cleanup */
+ lysc_node_container_free(ctx->ctx, &fake_container);
+
+ return ret;
+}
/**
* @brief Compile parsed schema node information.
@@ -5785,6 +5828,7 @@
struct lysp_module *sp;
struct lysp_node *node_p;
struct lysp_augment **augments = NULL;
+ struct lysp_grp *grps;
struct lys_module *m;
unsigned int u, v;
LY_ERR ret = LY_SUCCESS;
@@ -5910,6 +5954,23 @@
}
}
+ /* validate non-instantiated groupings from the parsed schema,
+ * without it we would accept even the schemas with invalid grouping specification */
+ ctx.options |= LYSC_OPT_GROUPING;
+ LY_ARRAY_FOR(sp->groupings, u) {
+ if (!(sp->groupings[u].flags & LYS_USED_GRP)) {
+ LY_CHECK_GOTO((ret = lys_compile_grouping(&ctx, node_p, &sp->groupings[u])) != LY_SUCCESS, error);
+ }
+ }
+ LY_LIST_FOR(sp->data, node_p) {
+ grps = (struct lysp_grp*)lysp_node_groupings(node_p);
+ LY_ARRAY_FOR(grps, u) {
+ if (!(grps[u].flags & LYS_USED_GRP)) {
+ LY_CHECK_GOTO((ret = lys_compile_grouping(&ctx, node_p, &grps[u])) != LY_SUCCESS, error);
+ }
+ }
+ }
+
ly_set_erase(&ctx.unres, NULL);
ly_set_erase(&ctx.groupings, NULL);
ly_set_erase(&ctx.tpdf_chain, NULL);
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index f66c70f..c3948a0 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -666,7 +666,7 @@
}
}
-static void
+void
lysc_node_container_free(struct ly_ctx *ctx, struct lysc_node_container *node)
{
struct lysc_node *child, *child_next;
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 4bafa70..6194649 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -164,6 +164,10 @@
#define LYSC_OPT_FREE_SP 0x04 /**< Free the input printable schema */
#define LYSC_OPT_INTERNAL 0x08 /**< Internal compilation caused by dependency */
#define LYSC_OPT_NOTIFICATION 0x10 /**< Internal option when compiling schema tree of Notification */
+
+#define LYSC_OPT_GROUPING 0x20 /** 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 */
/** @} scflags */
/**
@@ -622,6 +626,17 @@
void lysc_node_free(struct ly_ctx *ctx, struct lysc_node *node);
/**
+ * @brief Free the compiled container node structure.
+ *
+ * Only the container-specific members are freed, for generic node free function,
+ * use lysc_node_free().
+ *
+ * @param[in] ctx libyang context where the string data resides in a dictionary.
+ * @param[in,out] node Compiled container node structure to be freed.
+ */
+void lysc_node_container_free(struct ly_ctx *ctx, struct lysc_node_container *node);
+
+/**
* @brief Free the compiled schema structure.
* @param[in,out] module Compiled schema module structure to free.
* @param[in] private_destructor Function to remove private data from the compiled schema tree.