parser FEATURE added collision check for grouping
Collision checking of names is similar to typedef checking.
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))
{