schema parsers CHANGE check typedef collisions
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 47819a1..2f3d9db 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -1220,7 +1220,7 @@
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     /* check value */
-    if (lysp_check_date(ctx->ctx, word, word_len, "revision-date")) {
+    if (lysp_check_date(ctx, word, word_len, "revision-date")) {
         free(buf);
         return LY_EVALID;
     }
@@ -1375,7 +1375,7 @@
     LY_CHECK_RET(get_argument(ctx, data, Y_STR_ARG, &word, &buf, &word_len));
 
     /* check value */
-    if (lysp_check_date(ctx->ctx, word, word_len, "revision")) {
+    if (lysp_check_date(ctx, word, word_len, "revision")) {
         return LY_EVALID;
     }
 
@@ -2682,7 +2682,7 @@
  * @return LY_ERR values.
  */
 static LY_ERR
-parse_typedef(struct ly_parser_ctx *ctx, const char **data, struct lysp_tpdf **typedefs)
+parse_typedef(struct ly_parser_ctx *ctx, struct lysp_node *parent, const char **data, struct lysp_tpdf **typedefs)
 {
     LY_ERR ret = LY_SUCCESS;
     char *buf, *word;
@@ -2726,13 +2726,18 @@
         }
     }
     LY_CHECK_RET(ret);
-
+checks:
     /* mandatory substatements */
     if (!tpdf->type.name) {
         LOGVAL_YANG(ctx, LY_VCODE_MISSTMT, "type", "typedef");
         return LY_EVALID;
     }
 
+    /* store data for collision check */
+    if (parent) {
+        ly_set_add(&ctx->tpdfs_nodes, parent, 0);
+    }
+
     return ret;
 }
 
@@ -2792,7 +2797,7 @@
             LY_CHECK_RET(parse_uses(ctx, data, (struct lysp_node*)inout, &inout->data));
             break;
         case YANG_TYPEDEF:
-            LY_CHECK_RET(parse_typedef(ctx, data, &inout->typedefs));
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)inout, data, &inout->typedefs));
             break;
         case YANG_MUST:
             LY_CHECK_RET(parse_restrs(ctx, data, kw, &inout->musts));
@@ -2860,7 +2865,7 @@
             break;
 
         case YANG_TYPEDEF:
-            LY_CHECK_RET(parse_typedef(ctx, data, &act->typedefs));
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)act, data, &act->typedefs));
             break;
         case YANG_GROUPING:
             LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)act, &act->groupings));
@@ -2944,7 +2949,7 @@
             LY_CHECK_RET(parse_restrs(ctx, data, kw, &notif->musts));
             break;
         case YANG_TYPEDEF:
-            LY_CHECK_RET(parse_typedef(ctx, data, &notif->typedefs));
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)notif, data, &notif->typedefs));
             break;
         case YANG_GROUPING:
             LY_CHECK_RET(parse_grouping(ctx, data, (struct lysp_node*)notif, &notif->groupings));
@@ -3022,7 +3027,7 @@
             break;
 
         case YANG_TYPEDEF:
-            LY_CHECK_RET(parse_typedef(ctx, data, &grp->typedefs));
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)grp, data, &grp->typedefs));
             break;
         case YANG_ACTION:
             LY_CHECK_RET(parse_action(ctx, data, (struct lysp_node*)grp, &grp->actions));
@@ -3477,7 +3482,7 @@
             break;
 
         case YANG_TYPEDEF:
-            LY_CHECK_RET(parse_typedef(ctx, data, &cont->typedefs));
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)cont, data, &cont->typedefs));
             break;
         case YANG_MUST:
             LY_CHECK_RET(parse_restrs(ctx, data, kw, &cont->musts));
@@ -3600,7 +3605,7 @@
             break;
 
         case YANG_TYPEDEF:
-            LY_CHECK_RET(parse_typedef(ctx, data, &list->typedefs));
+            LY_CHECK_RET(parse_typedef(ctx, (struct lysp_node*)list, data, &list->typedefs));
             break;
         case YANG_MUST:
             LY_CHECK_RET(parse_restrs(ctx, data, kw, &list->musts));
@@ -4327,7 +4332,7 @@
             LY_CHECK_RET(parse_action(ctx, data, NULL, &mod->rpcs));
             break;
         case YANG_TYPEDEF:
-            LY_CHECK_RET(parse_typedef(ctx, data, &mod->typedefs));
+            LY_CHECK_RET(parse_typedef(ctx, NULL, data, &mod->typedefs));
             break;
         case YANG_CUSTOM:
             LY_CHECK_RET(parse_ext(ctx, data, word, word_len, LYEXT_SUBSTMT_SELF, 0, &mod->exts));
@@ -4369,56 +4374,56 @@
 }
 
 LY_ERR
-yang_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p)
+yang_parse(struct ly_parser_ctx *context, const char *data, struct lysp_module **mod_p)
 {
     LY_ERR ret = LY_SUCCESS;
     char *word, *buf;
     size_t word_len;
     enum yang_keyword kw;
     struct lysp_module *mod = NULL;
-    struct ly_parser_ctx context = {0};
-
-    context.ctx = ctx;
-    context.line = 1;
 
     /* "module"/"submodule" */
-    ret = get_keyword(&context, &data, &kw, &word, &word_len);
-    LY_CHECK_GOTO(ret, error);
+    ret = get_keyword(context, &data, &kw, &word, &word_len);
+    LY_CHECK_GOTO(ret, cleanup);
 
     if ((kw != YANG_MODULE) && (kw != YANG_SUBMODULE)) {
-        LOGVAL_YANG(&context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
+        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid keyword \"%s\", expected \"module\" or \"submodule\".",
                ly_stmt2str(kw));
-        goto error;
+        goto cleanup;
     }
 
     mod = calloc(1, sizeof *mod);
-    LY_CHECK_ERR_GOTO(!mod, LOGMEM(ctx), error);
+    LY_CHECK_ERR_GOTO(!mod, LOGMEM(context->ctx), cleanup);
     if (kw == YANG_SUBMODULE) {
         mod->submodule = 1;
     }
-    mod->ctx = ctx;
-    context.mod = mod;
+    mod->parsing = 1;
+    mod->ctx = context->ctx;
+    context->mod = mod;
 
     /* substatements */
-    ret = parse_sub_module(&context, &data, mod);
-    LY_CHECK_GOTO(ret, error);
+    ret = parse_sub_module(context, &data, mod);
+    LY_CHECK_GOTO(ret, cleanup);
 
     /* read some trailing spaces or new lines */
-    ret = get_argument(&context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
-    LY_CHECK_GOTO(ret, error);
+    ret = get_argument(context, &data, Y_MAYBE_STR_ARG, &word, &buf, &word_len);
+    LY_CHECK_GOTO(ret, cleanup);
 
     if (word) {
-        LOGVAL_YANG(&context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
+        LOGVAL_YANG(context, LYVE_SYNTAX, "Invalid character sequence \"%.*s\", expected end-of-file.",
                word_len, word);
         free(buf);
-        goto error;
+        goto cleanup;
     }
     assert(!buf);
 
+    mod->parsing = 0;
     *mod_p = mod;
-    return ret;
 
-error:
-    lysp_module_free(mod);
+cleanup:
+    if (ret) {
+        lysp_module_free(mod);
+    }
+
     return ret;
 }
diff --git a/src/tree_schema.c b/src/tree_schema.c
index bd849de..e82ff02 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -97,7 +97,7 @@
 static void
 lysp_include_free(struct ly_ctx *ctx, struct lysp_include *include, int dict)
 {
-    if (include->submodule && !(--include->submodule->refcount)) {
+    if (include->submodule) {
         lysp_module_free(include->submodule);
     }
     dict = 1; /* includes not present in compiled tree, so the data are not reused there in anyway */
@@ -661,7 +661,7 @@
     for (i = 0; i < len; ++i) {
         if (name[i] == ':') {
             /* we have a prefixed feature */
-            mod = lysc_module_find_prefix(mod, name, i)->compiled;
+            mod = lysc_module_find_prefix(mod, name, i);
             LY_CHECK_RET(!mod, NULL);
 
             name = &name[i + 1];
@@ -1222,7 +1222,7 @@
             if (s) {
                 /* prefixed identity */
                 name = &s[1];
-                mod = lysc_module_find_prefix(ctx->mod->compiled, idents_p[i].bases[u], s - idents_p[i].bases[u])->compiled;
+                mod = lysc_module_find_prefix(ctx->mod->compiled, idents_p[i].bases[u], s - idents_p[i].bases[u]);
             } else {
                 name = idents_p[i].bases[u];
                 mod = ctx->mod->compiled;
@@ -1515,9 +1515,13 @@
     struct lysp_include *inc;
     LY_ERR ret = LY_EINVAL;
     unsigned int u, i;
+    struct ly_parser_ctx context = {0};
 
     LY_CHECK_ARG_RET(ctx, ctx, data, NULL);
 
+    context.ctx = ctx;
+    context.line = 1;
+
     mod = calloc(1, sizeof *mod);
     LY_CHECK_ERR_RET(!mod, LOGMEM(ctx), NULL);
 
@@ -1528,7 +1532,7 @@
         */
         break;
     case LYS_IN_YANG:
-        ret = yang_parse(ctx, data, &mod->parsed);
+        ret = yang_parse(&context, data, &mod->parsed);
         break;
     default:
         LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
@@ -1543,14 +1547,13 @@
         /* mark the loaded module implemented */
         if (ly_ctx_get_module_implemented(ctx, mod->parsed->name)) {
             LOGERR(ctx, LY_EDENIED, "Module \"%s\" is already implemented in the context.", mod->parsed->name);
-            lys_module_free(mod, NULL);
-            return NULL;
+            goto error;
         }
         mod->parsed->implemented = 1;
     }
 
     if (custom_check) {
-        LY_CHECK_ERR_RET(custom_check(ctx, mod->parsed, check_data), lys_module_free(mod, NULL), NULL);
+        LY_CHECK_GOTO(custom_check(ctx, mod->parsed, check_data), error);
     }
 
     if (mod->parsed->submodule) { /* submodule */
@@ -1585,8 +1588,7 @@
                     LOGERR(ctx, LY_EEXIST, "Module \"%s\" with no revision is already present in the context.",
                            mod->parsed->name);
                 }
-                lys_module_free(mod, NULL);
-                return NULL;
+                goto error;
             } else {
                 /* add the parsed data to the currently compiled-only module in the context */
                 mod_dup->parsed = mod->parsed;
@@ -1631,37 +1633,41 @@
         ly_set_add(&ctx->list, mod, LY_SET_OPT_USEASLIST);
 
 finish_parsing:
-        /* resolve imports and includes */
+        /* resolve imports */
         mod->parsed->parsing = 1;
         LY_ARRAY_FOR(mod->parsed->imports, u) {
             imp = &mod->parsed->imports[u];
             if (!imp->module && lysp_load_module(ctx, imp->name, imp->rev[0] ? imp->rev : NULL, 0, 0, &imp->module)) {
-                ly_set_rm(&ctx->list, mod, NULL);
-                lys_module_free(mod, NULL);
-                return NULL;
+                goto error_ctx;
             }
             /* check for importing the same module twice */
             for (i = 0; i < u; ++i) {
                 if (imp->module == mod->parsed->imports[i].module) {
                     LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Single revision of the module \"%s\" referred twice.", imp->name);
-                    ly_set_rm(&ctx->list, mod, NULL);
-                    lys_module_free(mod, NULL);
-                    return NULL;
+                    goto error_ctx;
                 }
             }
         }
         LY_ARRAY_FOR(mod->parsed->includes, u) {
             inc = &mod->parsed->includes[u];
             if (!inc->submodule && lysp_load_submodule(ctx, mod->parsed, inc)) {
-                ly_set_rm(&ctx->list, mod, NULL);
-                lys_module_free(mod, NULL);
-                return NULL;
+                goto error_ctx;
             }
         }
         mod->parsed->parsing = 0;
+
+        /* check name collisions - typedefs and groupings */
+        LY_CHECK_GOTO(lysp_check_typedefs(&context), error_ctx);
     }
 
     return mod;
+
+error_ctx:
+    ly_set_rm(&ctx->list, mod, NULL);
+error:
+    lys_module_free(mod, NULL);
+    ly_set_erase(&context.tpdfs_nodes, NULL);
+    return NULL;
 }
 
 API struct lys_module *
diff --git a/src/tree_schema.h b/src/tree_schema.h
index a181cd8..d6aa6c0 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -785,7 +785,6 @@
                                           2 - searchdirs were searched and this is the latest available revision */
     uint8_t parsing:1;               /**< flag for circular check */
     uint8_t version;                 /**< yang-version (LYS_VERSION values) */
-    uint16_t refcount;               /**< 0 in modules, number of includes of a submodules */
 };
 
 /**
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 284cdee..f7904d3 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -50,14 +50,14 @@
 }
 
 LY_ERR
-lysp_check_date(struct ly_ctx *ctx, const char *date, int date_len, const char *stmt)
+lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt)
 {
     int i;
     struct tm tm, tm_;
     char *r;
 
-    LY_CHECK_ARG_RET(ctx, date, LY_EINVAL);
-    LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx, date_len), LY_EINVAL);
+    LY_CHECK_ARG_RET(ctx ? ctx->ctx : NULL, date, LY_EINVAL);
+    LY_CHECK_ERR_RET(date_len != LY_REV_SIZE - 1, LOGARG(ctx ? ctx->ctx : NULL, date_len), LY_EINVAL);
 
     /* check format */
     for (i = 0; i < date_len; i++) {
@@ -88,7 +88,11 @@
 
 error:
     if (stmt) {
-        LOGVAL(ctx, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
+        if (ctx) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LY_VCODE_INVAL, date_len, date, stmt);
+        } else {
+            LOGVAL(NULL, LY_VLOG_NONE, NULL, LY_VCODE_INVAL, date_len, date, stmt);
+        }
     }
     return LY_EINVAL;
 }
@@ -113,6 +117,262 @@
     }
 }
 
+static const struct lysp_tpdf *
+lysp_type_match(const char *name, struct lysp_node *node)
+{
+    struct lysp_tpdf **typedefs;
+    unsigned int u;
+
+    typedefs = lysp_node_typedefs(node);
+    if (typedefs && *typedefs) {
+        LY_ARRAY_FOR(*typedefs, u) {
+            if (!strcmp(name, (*typedefs)[u].name)) {
+                /* match */
+                return &(*typedefs)[u];
+            }
+        }
+    }
+
+    return NULL;
+}
+
+LY_ERR
+lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
+               const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module)
+{
+    const char *str, *name;
+    struct lysp_tpdf *typedefs;
+    unsigned int u, v;
+
+    assert(id);
+    assert(start_module);
+    assert(tpdf);
+    assert(node);
+    assert(module);
+
+    str = strchr(id, ':');
+    if (str) {
+        *module = lysp_module_find_prefix(start_module, id, str - id);
+        name = str + 1;
+    } else {
+        *module = start_module;
+        name = id;
+    }
+    LY_CHECK_RET(!(*module), LY_ENOTFOUND);
+
+    if (start_node && *module == start_module) {
+        /* search typedefs in parent's nodes */
+        *node = start_node;
+        while (*node) {
+            *tpdf = lysp_type_match(name, *node);
+            if (*tpdf) {
+                /* match */
+                return LY_SUCCESS;
+            }
+            *node = (*node)->parent;
+        }
+    }
+
+    /* search in top-level typedefs */
+    if ((*module)->typedefs) {
+        LY_ARRAY_FOR((*module)->typedefs, u) {
+            if (!strcmp(name, (*module)->typedefs[u].name)) {
+                /* match */
+                *tpdf = &(*module)->typedefs[u];
+                return LY_SUCCESS;
+            }
+        }
+    }
+
+    /* search in submodules' typedefs */
+    LY_ARRAY_FOR((*module)->includes, u) {
+        typedefs = (*module)->includes[u].submodule->typedefs;
+        if (typedefs) {
+            LY_ARRAY_FOR(typedefs, v) {
+                if (!strcmp(name, typedefs[v].name)) {
+                    /* match */
+                    *tpdf = &typedefs[v];
+                    return LY_SUCCESS;
+                }
+            }
+        }
+    }
+
+    return LY_ENOTFOUND;
+}
+
+/*
+ * @brief Check name of a new type to avoid name collisions.
+ *
+ * @param[in] ctx Parser context, module where the type is being defined is taken from here.
+ * @param[in] node Schema node where the type is being defined, NULL in case of a top-level typedef.
+ * @param[in] tpdf Typedef definition to check.
+ * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
+ *            typedefs are checked, caller is supposed to free the table.
+ * @param[in,out] tpdfs_global Initialized hash table to store temporary data between calls. When the module's
+ *            typedefs are checked, caller is supposed to free the table.
+ * @return LY_EEXIST in case of collision, LY_SUCCESS otherwise.
+ */
+static LY_ERR
+lysp_check_typedef(struct ly_parser_ctx *ctx, struct lysp_node *node, struct lysp_tpdf *tpdf,
+                   struct hash_table *tpdfs_global, struct hash_table *tpdfs_scoped)
+{
+    struct lysp_node *parent;
+    uint32_t hash;
+    size_t name_len;
+    const char *name;
+    unsigned int u;
+    struct lysp_tpdf **typedefs;
+
+    assert(ctx);
+    assert(tpdf);
+
+    name = tpdf->name;
+    name_len = strlen(name);
+
+    if (name_len >= 4) {
+        /* otherwise it does not match any built-in type,
+         * check collision with the built-in types */
+        if (name[0] == 'b') {
+            if (name[1] == 'i') {
+                if ((name_len == 6 && !strcmp(&name[2], "nary")) || (name_len == 4 && !strcmp(&name[2], "ts"))) {
+collision:
+                    LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                           "Invalid name \"%s\" of typedef - name collision with a built-in type.", name);
+                    return LY_EEXIST;
+                }
+            } else if (name_len == 7 && !strcmp(&name[1], "oolean")) {
+                goto collision;
+            }
+        } else if (name[0] == 'd') {
+            if (name_len == 9 && !strcmp(&name[1], "ecimal64")) {
+                goto collision;
+            }
+        } else if (name[0] == 'e') {
+            if ((name_len == 5 && !strcmp(&name[1], "mpty")) || (name_len == 11 && !strcmp(&name[1], "numeration"))) {
+                goto collision;
+            }
+        } else if (name[0] == 'i') {
+            if (name[1] == 'n') {
+                if ((name_len == 4 && !strcmp(&name[2], "t8")) ||
+                        (name_len == 5 && (!strcmp(&name[2], "t16") || !strcmp(&name[2], "t32") || !strcmp(&name[2], "t64"))) ||
+                        (name_len == 19 && !strcmp(&name[2], "stance-identifier"))) {
+                    goto collision;
+                }
+            } else if (name_len == 11 && !strcmp(&name[1], "dentityref")) {
+                goto collision;
+            }
+        } else if (name[0] == 'l') {
+            if (name_len == 7 && !strcmp(&name[1], "eafref")) {
+                goto collision;
+            }
+        } else if (name[0] == 's') {
+            if (name_len == 6 && !strcmp(&name[1], "tring")) {
+                goto collision;
+            }
+        } else if (name[0] == 'u') {
+            if (name[1] == 'n') {
+                if (name_len == 5 && !strcmp(&name[2], "ion")) {
+                    goto collision;
+                }
+            } else if (name[1] == 'i' && name[2] == 'n' && name[3] == 't' &&
+                    ((name_len == 5 && !strcmp(&name[4], "8")) ||
+                     (name_len == 6 && (!strcmp(&name[4], "16") || !strcmp(&name[4], "32") || !strcmp(&name[4], "64"))))) {
+                goto collision;
+            }
+        }
+    }
+
+    /* check locally scoped typedefs (avoid name shadowing) */
+    if (node) {
+        typedefs = lysp_node_typedefs(node);
+        if (typedefs && *typedefs) {
+            LY_ARRAY_FOR(*typedefs, u) {
+                if (typedefs[u] == tpdf) {
+                    break;
+                }
+                if (!strcmp(name, (*typedefs)[u].name)) {
+                    LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                           "Invalid name \"%s\" of typedef - name collision with sibling type.", name);
+                    return LY_EEXIST;
+                }
+            }
+        }
+        /* search typedefs in parent's nodes */
+        for (parent = node->parent; parent; parent = node->parent) {
+            if (lysp_type_match(name, parent)) {
+                LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                       "Invalid name \"%s\" of typedef - name collision with another scoped type.", name);
+                return LY_EEXIST;
+            }
+        }
+    }
+
+    /* check collision with the top-level typedefs */
+    hash = dict_hash(name, name_len);
+    if (node) {
+        lyht_insert(tpdfs_scoped, &name, hash, NULL);
+        if (!lyht_find(tpdfs_global, &name, hash, NULL)) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                   "Invalid name \"%s\" of typedef - scoped type collide with a top-level type.", name);
+            return LY_EEXIST;
+        }
+    } else {
+        if (lyht_insert(tpdfs_global, &name, hash, NULL)) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                   "Invalid name \"%s\" of typedef - name collision with another top-level type.", name);
+            return LY_EEXIST;
+        }
+        if (!lyht_find(tpdfs_scoped, &name, hash, NULL)) {
+            LOGVAL(ctx->ctx, LY_VLOG_LINE, &ctx->line, LYVE_SYNTAX_YANG,
+                   "Invalid name \"%s\" of typedef - top-level type collide with a scoped type.", name);
+            return LY_EEXIST;
+        }
+    }
+
+    return LY_SUCCESS;
+}
+
+static int
+lysp_id_cmp(void *val1, void *val2, int UNUSED(mod), void *UNUSED(cb_data))
+{
+    return !strcmp(val1, val2);
+}
+
+LY_ERR
+lysp_check_typedefs(struct ly_parser_ctx *ctx)
+{
+    struct hash_table *ids_global;
+    struct hash_table *ids_scoped;
+    struct lysp_tpdf **typedefs;
+    unsigned int i, u;
+    LY_ERR ret = LY_EVALID;
+
+    /* check name collisions - typedefs and groupings */
+    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(ctx->mod->typedefs, i) {
+        if (lysp_check_typedef(ctx, NULL, &ctx->mod->typedefs[i], ids_global, ids_scoped)) {
+            goto cleanup;
+        }
+    }
+    for (u = 0; u < ctx->tpdfs_nodes.count; ++u) {
+        typedefs = lysp_node_typedefs((struct lysp_node *)ctx->tpdfs_nodes.objs[u]);
+        LY_ARRAY_FOR(*typedefs, i) {
+            if (lysp_check_typedef(ctx, (struct lysp_node *)ctx->tpdfs_nodes.objs[u], &(*typedefs)[i], ids_global, ids_scoped)) {
+                goto cleanup;
+            }
+        }
+    }
+    ret = LY_SUCCESS;
+cleanup:
+    lyht_free(ids_global);
+    lyht_free(ids_scoped);
+    ly_set_erase(&ctx->tpdfs_nodes, NULL);
+
+    return ret;
+}
+
 void
 lys_module_implement(struct lys_module *mod)
 {
@@ -349,51 +609,44 @@
     void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
     struct lysp_load_module_check_data check_data = {0};
 
-    /* Try to get submodule from the context, if already present */
-    inc->submodule = ly_ctx_get_submodule(ctx, mod->name, inc->name, inc->rev[0] ? inc->rev : NULL);
-    if (!inc->submodule || (!inc->rev[0] && inc->submodule->latest_revision != 2)) {
-        /* submodule not present in the context, get the input data and parse it */
-        if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+    /* submodule not present in the context, get the input data and parse it */
+    if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
 search_clb:
-            if (ctx->imp_clb) {
-                if (ctx->imp_clb(mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, ctx->imp_clb_data,
-                                      &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
-                    check_data.name = inc->name;
-                    check_data.revision = inc->rev[0] ? inc->rev : NULL;
-                    check_data.submoduleof = mod->name;
-                    submod = lys_parse_mem_(ctx, submodule_data, format, mod->implemented,
-                                            lysp_load_module_check, &check_data);
-                    if (submodule_data_free) {
-                        submodule_data_free((void*)submodule_data, ctx->imp_clb_data);
-                    }
+        if (ctx->imp_clb) {
+            if (ctx->imp_clb(mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, ctx->imp_clb_data,
+                                  &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
+                check_data.name = inc->name;
+                check_data.revision = inc->rev[0] ? inc->rev : NULL;
+                check_data.submoduleof = mod->name;
+                submod = lys_parse_mem_(ctx, submodule_data, format, mod->implemented,
+                                        lysp_load_module_check, &check_data);
+                if (submodule_data_free) {
+                    submodule_data_free((void*)submodule_data, ctx->imp_clb_data);
                 }
             }
-            if (!submod && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
-                goto search_file;
-            }
-        } else {
-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, inc->name, inc->rev[0] ? inc->rev : NULL, mod->implemented, &submod);
-            }
-            if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
-                goto search_clb;
-            }
         }
-        if (submod) {
-            if (!inc->rev[0] && (submod->parsed->latest_revision == 1)) {
-                /* update the latest_revision flag - here we have selected the latest available schema,
-                 * consider that even the callback provides correct latest revision */
-                submod->parsed->latest_revision = 2;
-            }
-
-            inc->submodule = submod->parsed;
-            ++inc->submodule->refcount;
-            free(submod);
+        if (!submod && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+            goto search_file;
         }
     } else {
-        ++inc->submodule->refcount;
+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, inc->name, inc->rev[0] ? inc->rev : NULL, mod->implemented, &submod);
+        }
+        if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+            goto search_clb;
+        }
+    }
+    if (submod) {
+        if (!inc->rev[0] && (submod->parsed->latest_revision == 1)) {
+            /* update the latest_revision flag - here we have selected the latest available schema,
+             * consider that even the callback provides correct latest revision */
+            submod->parsed->latest_revision = 2;
+        }
+
+        inc->submodule = submod->parsed;
+        free(submod);
     }
     if (!inc->submodule) {
         LOGVAL(ctx, LY_VLOG_NONE, NULL, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name, mod->name);
@@ -403,38 +656,159 @@
     return LY_SUCCESS;
 }
 
-#define FIND_MODULE(TYPE, MOD) \
+#define FIND_MODULE(TYPE, MOD, ID) \
     TYPE *imp; \
     if (!strncmp((MOD)->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
         /* it is the prefix of the module itself */ \
-        return (struct lys_module*)ly_ctx_get_module((MOD)->ctx, (MOD)->name, ((struct lysc_module*)(MOD))->revision); \
+        m = ly_ctx_get_module((MOD)->ctx, (MOD)->name, ((struct lysc_module*)(MOD))->revision); \
     } \
     /* search in imports */ \
-    LY_ARRAY_FOR((MOD)->imports, TYPE, imp) { \
-        if (!strncmp(imp->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
-            return imp->module; \
+    if (!m) { \
+        LY_ARRAY_FOR((MOD)->imports, TYPE, imp) { \
+            if (!strncmp(imp->prefix, prefix, len) && (MOD)->prefix[len] == '\0') { \
+                m = imp->module; \
+                break; \
+            } \
         } \
-    } \
-    return NULL
+    }
 
-struct lys_module *
+struct lysc_module *
 lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len)
 {
-    FIND_MODULE(struct lysc_import, mod);
+    const struct lys_module *m = NULL;
+
+    FIND_MODULE(struct lysc_import, mod, 1);
+    return m ? m->compiled : NULL;
 }
 
-struct lys_module *
+struct lysp_module *
 lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len)
 {
-    FIND_MODULE(struct lysp_import, mod);
+    const struct lys_module *m = NULL;
+
+    FIND_MODULE(struct lysp_import, mod, 1);
+    return m ? m->parsed : NULL;
 }
 
 struct lys_module *
 lys_module_find_prefix(struct lys_module *mod, const char *prefix, size_t len)
 {
+    const struct lys_module *m = NULL;
+
     if (mod->compiled) {
-        FIND_MODULE(struct lysc_import, mod->compiled);
+        FIND_MODULE(struct lysc_import, mod->compiled, 1);
     } else {
-        FIND_MODULE(struct lysp_import, mod->parsed);
+        FIND_MODULE(struct lysp_import, mod->parsed, 2);
+    }
+    return (struct lys_module*)m;
+}
+
+struct lysp_tpdf **
+lysp_node_typedefs(struct lysp_node *node)
+{
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysp_node_container*)node)->typedefs;
+    case LYS_LIST:
+        return &((struct lysp_node_list*)node)->typedefs;
+    case LYS_GROUPING:
+        return &((struct lysp_grp*)node)->typedefs;
+    case LYS_ACTION:
+        return &((struct lysp_action*)node)->typedefs;
+    case LYS_INOUT:
+        return &((struct lysp_action_inout*)node)->typedefs;
+    case LYS_NOTIF:
+        return &((struct lysp_notif*)node)->typedefs;
+    default:
+        return NULL;
     }
 }
+
+struct lysp_action **
+lysp_node_actions(struct lysp_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysp_node_container*)node)->actions;
+    case LYS_LIST:
+        return &((struct lysp_node_list*)node)->actions;
+    case LYS_GROUPING:
+        return &((struct lysp_grp*)node)->actions;
+    case LYS_AUGMENT:
+        return &((struct lysp_augment*)node)->actions;
+    default:
+        return NULL;
+    }
+}
+
+struct lysp_notif **
+lysp_node_notifs(struct lysp_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysp_node_container*)node)->notifs;
+    case LYS_LIST:
+        return &((struct lysp_node_list*)node)->notifs;
+    case LYS_GROUPING:
+        return &((struct lysp_grp*)node)->notifs;
+    case LYS_AUGMENT:
+        return &((struct lysp_augment*)node)->notifs;
+    default:
+        return NULL;
+    }
+}
+
+struct lysp_node **
+lysp_node_children(struct lysp_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysp_node_container*)node)->child;
+    case LYS_CHOICE:
+        return &((struct lysp_node_choice*)node)->child;
+    case LYS_LIST:
+        return &((struct lysp_node_list*)node)->child;
+    case LYS_CASE:
+        return &((struct lysp_node_case*)node)->child;
+    case LYS_GROUPING:
+        return &((struct lysp_grp*)node)->data;
+    case LYS_AUGMENT:
+        return &((struct lysp_augment*)node)->child;
+    case LYS_INOUT:
+        return &((struct lysp_action_inout*)node)->data;
+    case LYS_NOTIF:
+        return &((struct lysp_notif*)node)->data;
+    default:
+        return NULL;
+    }
+}
+
+struct lysc_node **
+lysc_node_children(struct lysc_node *node)
+{
+    assert(node);
+    switch (node->nodetype) {
+    case LYS_CONTAINER:
+        return &((struct lysc_node_container*)node)->child;
+    case LYS_CHOICE:
+        return &((struct lysc_node_choice*)node)->child;
+    case LYS_LIST:
+        return &((struct lysc_node_list*)node)->child;
+    case LYS_CASE:
+        return &((struct lysc_node_case*)node)->child;
+    case LYS_USES:
+        return &((struct lysc_node_uses*)node)->child;
+/* TODO
+    case LYS_INOUT:
+        return &((struct lysc_action_inout*)node)->child;
+    case LYS_NOTIF:
+        return &((struct lysc_notif*)node)->child;
+*/
+    default:
+        return NULL;
+    }
+}
+
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index c82b106..73a77b3 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -44,14 +44,16 @@
 struct ly_parser_ctx {
     struct ly_ctx *ctx;
     struct lysp_module *mod;
-    uint64_t line;      /* line number */
-    uint64_t indent;    /* current position on the line for YANG indentation */
+    struct ly_set tpdfs_nodes;
+    struct ly_set grps_nodes;
+    uint64_t line;      /**< line number */
+    uint64_t indent;    /**< current position on the line for YANG indentation */
 };
 
 /**
  * @brief Check the currently present prefixes in the module for collision with the new one.
  *
- * @param[in] ctx yang parser context.
+ * @param[in] ctx Context for logging.
  * @param[in] module Schema tree to check.
  * @param[in] value Newly added prefix value (including its location to distinguish collision with itself).
  * @return LY_EEXIST when prefix is already used in the module, LY_SUCCESS otherwise
@@ -61,13 +63,21 @@
 /**
  * @brief Check date string (4DIGIT "-" 2DIGIT "-" 2DIGIT)
  *
- * @param[in] ctx Context to store log message.
+ * @param[in] ctx Optional context for logging.
  * @param[in] date Date string to check (non-necessarily terminated by \0)
  * @param[in] date_len Length of the date string, 10 expected.
  * @param[in] stmt Statement name for error message.
  * @return LY_ERR value.
  */
-LY_ERR lysp_check_date(struct ly_ctx *ctx, const char *date, int date_len, const char *stmt);
+LY_ERR lysp_check_date(struct ly_parser_ctx *ctx, const char *date, int date_len, const char *stmt);
+
+/**
+ * @brief Check names of typedefs in the parsed module to detect collisions.
+ *
+ * @param[in] ctx Parser context, module where the type is being defined is taken from here.
+ * @return LY_ERR value.
+ */
+LY_ERR lysp_check_typedefs(struct ly_parser_ctx *ctx);
 
 /**
  * @brief Just move the newest revision into the first position, does not sort the rest
@@ -76,6 +86,19 @@
 void lysp_sort_revisions(struct lysp_revision *revs);
 
 /**
+ * @brief Find type specified type definition
+ *
+ * @param[in] id Name of the type including possible prefix. Module where the prefix is being searched is start_module.
+ * @param[in] start_node Context node where the type is being instantiated to be able to search typedefs in parents.
+ * @param[in] start_module Module where the type is being instantiated for search for typedefs.
+ * @param[out] tpdf Found type definition.
+ * @param[out] node Node where the found typedef is defined, NULL in case of a top-level typedef.
+ * @param[out] module Module where the found typedef is being defined, NULL in case of built-in YANG types.
+ */
+LY_ERR lysp_type_find(const char *id, struct lysp_node *start_node, struct lysp_module *start_module,
+                      const struct lysp_tpdf **tpdf, struct lysp_node **node, struct lysp_module **module);
+
+/**
  * @brief Find and parse module of the given name.
  *
  * @param[in] ctx libyang context.
@@ -101,6 +124,51 @@
 LY_ERR lysp_load_submodule(struct ly_ctx *ctx, struct lysp_module *mod, struct lysp_include *inc);
 
 /**
+ * @brief Get address of a node's typedefs list if any.
+ *
+ * Decides the node's type and in case it has an typedefs list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's tpdf member if any, NULL otherwise.
+ */
+struct lysp_tpdf **lysp_node_typedefs(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's actions list if any.
+ *
+ * Decides the node's type and in case it has an actions list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's actions member if any, NULL otherwise.
+ */
+struct lysp_action **lysp_node_actions(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's notifications list if any.
+ *
+ * Decides the node's type and in case it has a notifications list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's notifs member if any, NULL otherwise.
+ */
+struct lysp_notif **lysp_node_notifs(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * Decides the node's type and in case it has a children list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lysp_node **lysp_node_children(struct lysp_node *node);
+
+/**
+ * @brief Get address of a node's child pointer if any.
+ *
+ * Decides the node's type and in case it has a children list, returns its address.
+ * @param[in] node Node to check.
+ * @return Address of the node's child member if any, NULL otherwise.
+ */
+struct lysc_node **lysc_node_children(struct lysc_node *node);
+
+/**
  * @brief Find the module referenced by prefix in the provided parsed mod.
  *
  * @param[in] mod Schema module where the prefix was used.
@@ -108,7 +176,7 @@
  * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
  * @return Pointer to the module or NULL if the module is not found.
  */
-struct lys_module *lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len);
+struct lysp_module *lysp_module_find_prefix(struct lysp_module *mod, const char *prefix, size_t len);
 
 /**
  * @brief Find the module referenced by prefix in the provided compiled mod.
@@ -118,7 +186,7 @@
  * @param[in] len Length of the prefix since it is not necessary NULL-terminated.
  * @return Pointer to the module or NULL if the module is not found.
  */
-struct lys_module *lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len);
+struct lysc_module *lysc_module_find_prefix(struct lysc_module *mod, const char *prefix, size_t len);
 
 /**
  * @brief Find the module referenced by prefix in the provided mod.
@@ -225,6 +293,6 @@
 /**
  * @brief
  */
-LY_ERR yang_parse(struct ly_ctx *ctx, const char *data, struct lysp_module **mod_p);
+LY_ERR yang_parse(struct ly_parser_ctx *ctx, const char *data, struct lysp_module **mod_p);
 
 #endif /* LY_TREE_SCHEMA_INTERNAL_H_ */