parser FEATURE added collision check for grouping
Collision checking of names is similar to typedef checking.
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 8147a7f..b62c54a 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -2977,7 +2977,7 @@
grp->nodetype = LYS_GROUPING;
grp->parent = parent;
- YANG_READ_SUBSTMT_FOR(ctx, kw, word, word_len, ret, return LY_SUCCESS, return ret) {
+ YANG_READ_SUBSTMT_FOR(ctx, kw, word, word_len, ret, goto checks, return ret) {
switch (kw) {
case LY_STMT_DESCRIPTION:
LY_CHECK_RET(parse_text_field(ctx, LY_STMT_DESCRIPTION, 0, &grp->dsc, Y_STR_ARG, &grp->exts));
@@ -3036,6 +3036,13 @@
return LY_EVALID;
}
}
+ LY_CHECK_RET(ret);
+checks:
+ /* store data for collision check */
+ if (parent) {
+ assert(ctx->main_ctx);
+ LY_CHECK_RET(ly_set_add(&ctx->main_ctx->grps_nodes, parent, 0, NULL));
+ }
return ret;
}
diff --git a/src/parser_yin.c b/src/parser_yin.c
index c2d963a..63c7661 100644
--- a/src/parser_yin.c
+++ b/src/parser_yin.c
@@ -2327,6 +2327,12 @@
ret = yin_parse_content(ctx, subelems, subelems_size, LY_STMT_GROUPING, NULL, &grp->exts);
subelems_deallocator(subelems_size, subelems);
+ /* store data for collision check */
+ if (!ret && grp->parent) {
+ assert(ctx->main_ctx);
+ LY_CHECK_RET(ly_set_add(&ctx->main_ctx->grps_nodes, grp->parent, 0, NULL));
+ }
+
return ret;
}
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 4603fb8..3c95a90 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -1569,7 +1569,7 @@
/* check name collisions */
LY_CHECK_GOTO(ret = lysp_check_dup_typedefs(pctx, mod->parsed), cleanup);
- /* TODO groupings */
+ LY_CHECK_GOTO(ret = lysp_check_dup_groupings(pctx, mod->parsed), cleanup);
LY_CHECK_GOTO(ret = lysp_check_dup_features(pctx, mod->parsed), cleanup);
LY_CHECK_GOTO(ret = lysp_check_dup_identities(pctx, mod->parsed), cleanup);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 3b510aa..0658a33 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -137,6 +137,22 @@
return NULL;
}
+static const struct lysp_node_grp *
+lysp_grouping_match(const char *name, struct lysp_node *node)
+{
+ const struct lysp_node_grp *groupings, *grp_iter;
+
+ groupings = lysp_node_groupings(node);
+ LY_LIST_FOR(groupings, grp_iter) {
+ if (!strcmp(name, grp_iter->name)) {
+ /* match */
+ return grp_iter;
+ }
+ }
+
+ return NULL;
+}
+
static LY_DATA_TYPE
lysp_type_str2builtin(const char *name, size_t len)
{
@@ -465,6 +481,104 @@
return ret;
}
+/**
+ * @brief Check name of a new grouping to avoid name collisions.
+ *
+ * @param[in] ctx Parser context, module where the grouping is being defined is taken from here.
+ * @param[in] node Schema node where the grouping is being defined, NULL in case of a top-level grouping.
+ * @param[in] grp Grouping definition to check.
+ * @param[in,out] grps_global Initialized hash table to store temporary data between calls. When the module's
+ * groupings are checked, caller is supposed to free the table.
+ * @return LY_EVALID in case of collision, LY_SUCCESS otherwise.
+ */
+static LY_ERR
+lysp_check_dup_grouping(struct lys_parser_ctx *ctx, struct lysp_node *node, const struct lysp_node_grp *grp,
+ struct hash_table *grps_global)
+{
+ struct lysp_node *parent;
+ uint32_t hash;
+ size_t name_len;
+ const char *name;
+ const struct lysp_node_grp *groupings, *grp_iter;
+
+ assert(ctx);
+ assert(grp);
+
+ name = grp->name;
+ name_len = strlen(name);
+
+ /* check locally scoped groupings (avoid name shadowing) */
+ if (node) {
+ groupings = lysp_node_groupings(node);
+ LY_LIST_FOR(groupings, grp_iter) {
+ if (grp_iter == grp) {
+ break;
+ }
+ if (!strcmp(name, grp_iter->name)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of grouping statement - name collision with sibling grouping.", name);
+ return LY_EVALID;
+ }
+ }
+ /* search grouping in parent's nodes */
+ for (parent = node->parent; parent; parent = parent->parent) {
+ if (lysp_grouping_match(name, parent)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of grouping statement - name collision with another scoped grouping.", name);
+ return LY_EVALID;
+ }
+ }
+ }
+
+ /* check collision with the top-level groupings */
+ if (node) {
+ hash = dict_hash(name, name_len);
+ if (!lyht_find(grps_global, &name, hash, NULL)) {
+ LOGVAL_PARSER(ctx, LYVE_SYNTAX_YANG,
+ "Duplicate identifier \"%s\" of grouping statement - scoped grouping collide with a top-level grouping.", name);
+ return LY_EVALID;
+ }
+ } else {
+ LY_CHECK_RET(lysp_check_dup_ht_insert(ctx, grps_global, name, "grouping",
+ "name collision with another top-level grouping"));
+ }
+
+ return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_check_dup_groupings(struct lys_parser_ctx *ctx, struct lysp_module *mod)
+{
+ struct hash_table *ids_global;
+ const struct lysp_node_grp *groupings, *grp_iter;
+ LY_ARRAY_COUNT_TYPE u;
+ uint32_t i;
+ LY_ERR ret = LY_SUCCESS;
+
+ ids_global = lyht_new(LYHT_MIN_SIZE, sizeof(char *), lysp_id_cmp, NULL, 1);
+ LY_LIST_FOR(mod->groupings, grp_iter) {
+ ret = lysp_check_dup_grouping(ctx, NULL, grp_iter, ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ LY_ARRAY_FOR(mod->includes, u) {
+ LY_LIST_FOR(mod->includes[u].submodule->groupings, grp_iter) {
+ ret = lysp_check_dup_grouping(ctx, NULL, grp_iter, ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+ for (i = 0; i < ctx->grps_nodes.count; ++i) {
+ groupings = lysp_node_groupings((struct lysp_node *)ctx->grps_nodes.objs[i]);
+ LY_LIST_FOR(groupings, grp_iter) {
+ ret = lysp_check_dup_grouping(ctx, (struct lysp_node *)ctx->grps_nodes.objs[i], grp_iter, ids_global);
+ LY_CHECK_GOTO(ret, cleanup);
+ }
+ }
+
+cleanup:
+ lyht_free(ids_global);
+ return ret;
+}
+
static ly_bool
ly_ptrequal_cb(void *val1_p, void *val2_p, ly_bool UNUSED(mod), void *UNUSED(cb_data))
{
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 351290f..e2d0f6b 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -147,7 +147,7 @@
struct ly_set tpdfs_nodes; /**< Set of nodes that contain typedef(s). Invalid in case of
submodule, use ::lys_parser_ctx.main_ctx instead. */
struct ly_set grps_nodes; /**< Set of nodes that contain grouping(s). Invalid in case of
- submodule, use ::lys_parser_ctx.main_ctx instead. TODO implement. */
+ submodule, use ::lys_parser_ctx.main_ctx instead. */
struct lysp_module *parsed_mod; /**< (sub)module being parsed */
struct lys_parser_ctx *main_ctx; /**< This pointer must not be NULL. If this context deals with the submodule,
then should be set to the context of the module to which it belongs,
@@ -162,7 +162,7 @@
struct ly_set tpdfs_nodes; /**< Set of nodes that contain typedef(s). Invalid in case of
submodule, use ::lys_parser_ctx.main_ctx instead. */
struct ly_set grps_nodes; /**< Set of nodes that contain grouping(s). Invalid in case of
- submodule, use ::lys_parser_ctx.main_ctx instead. TODO implement. */
+ submodule, use ::lys_parser_ctx.main_ctx instead. */
struct lysp_module *parsed_mod; /**< (sub)module being parsed */
struct lys_parser_ctx *main_ctx; /**< This pointer must not be NULL. If this context deals with the submodule,
then should be set to the context of the module to which it belongs,
@@ -185,7 +185,7 @@
struct ly_set tpdfs_nodes; /**< Set of nodes that contain typedef(s). Invalid in case of
submodule, use ::lys_parser_ctx.main_ctx instead. */
struct ly_set grps_nodes; /**< Set of nodes that contain grouping(s). Invalid in case of
- submodule, use ::lys_parser_ctx.main_ctx instead. TODO implement. */
+ submodule, use ::lys_parser_ctx.main_ctx instead. */
struct lysp_module *parsed_mod; /**< (sub)module being parsed */
struct lys_parser_ctx *main_ctx; /**< This pointer must not be NULL. If this context deals with the submodule,
then should be set to the context of the module to which it belongs,
@@ -257,6 +257,15 @@
LY_ERR lysp_check_dup_typedefs(struct lys_parser_ctx *ctx, struct lysp_module *mod);
/**
+ * @brief Check names of groupings in the parsed module to detect collisions.
+ *
+ * @param[in] ctx Parser context for logging and to maintain grps_nodes.
+ * @param[in] mod Module where the type is being defined.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_dup_groupings(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.