schema tree CHANGE better checking of name collisions
diff --git a/src/common.h b/src/common.h
index c075979..c60f93e 100644
--- a/src/common.h
+++ b/src/common.h
@@ -135,6 +135,7 @@
 #define LY_VCODE_INSTMT      LYVE_SYNTAX_YANG, "Invalid keyword \"%s\"."
 #define LY_VCODE_INCHILDSTMT LYVE_SYNTAX_YANG, "Invalid keyword \"%s\" as a child of \"%s\"."
 #define LY_VCODE_DUPSTMT     LYVE_SYNTAX_YANG, "Duplicate keyword \"%s\"."
+#define LY_VCODE_DUPIDENT    LYVE_SYNTAX_YANG, "Duplicate identifier \"%s\" of %s statement."
 #define LY_VCODE_INVAL       LYVE_SYNTAX_YANG, "Invalid value \"%.*s\" of \"%s\"."
 #define LY_VCODE_MISSTMT     LYVE_SYNTAX_YANG, "Missing mandatory keyword \"%s\" as a child of \"%s\"."
 #define LY_VCODE_INORD       LYVE_SYNTAX_YANG, "Invalid keyword \"%s\", it cannot appear after \"%s\"."
@@ -165,7 +166,7 @@
  * some of the currently present schemas.
  *
  * @param[in] ctx Context where to search
- * @param[in] module Name of the module where the submodule is supposed to belongs-to.
+ * @param[in] module Name of the module where the submodule is supposed to belongs-to. If NULL, the module name is not checked.
  * @param[in] submodule Name of the submodule to find.
  * @param[in] revision Optional revision of the submodule to find. If not specified, the latest revision is returned.
  * @return Pointer to the specified submodule if it is present in the context.
diff --git a/src/context.c b/src/context.c
index 2394faf..c0eca67 100644
--- a/src/context.c
+++ b/src/context.c
@@ -441,12 +441,18 @@
 {
     const struct lys_module *mod;
     struct lysp_include *inc;
-    unsigned int index = 0, u;
+    unsigned int v, u;
 
-    while ((mod = ly_ctx_get_module_by_iter(ctx, module, offsetof(struct lysp_module, name), &index))) {
+    assert(submodule);
+
+    for (v = 0; v < ctx->list.count; ++v) {
+        mod = ctx->list.objs[v];
         if (!mod->parsed) {
             continue;
         }
+        if (module && strcmp(module, mod->parsed->name)) {
+            continue;
+        }
 
         LY_ARRAY_FOR(mod->parsed->includes, u) {
             if (mod->parsed->includes[u].submodule && !strcmp(submodule, mod->parsed->includes[u].submodule->name)) {
diff --git a/src/parser_yang.c b/src/parser_yang.c
index 5435eab..ab6f7ac 100644
--- a/src/parser_yang.c
+++ b/src/parser_yang.c
@@ -46,6 +46,16 @@
 #define is_yangidentchar(c) ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || \
         c == '_' || c == '-' || c == '.')
 
+#define CHECK_UNIQUENESS(CTX, ARRAY, MEMBER, STMT, IDENT) \
+    if (ARRAY) { \
+        for (unsigned int u = 0; u < LY_ARRAY_SIZE(ARRAY) - 1; ++u) { \
+            if (!strcmp((ARRAY)[u].MEMBER, IDENT)) { \
+                LOGVAL_YANG(CTX, LY_VCODE_DUPIDENT, IDENT, STMT); \
+                return LY_EVALID; \
+            } \
+        } \
+    }
+
 #define INSERT_WORD(CTX, BUF, TARGET, WORD, LEN) \
     if (BUF) {(TARGET) = lydict_insert_zc((CTX)->ctx, WORD);}\
     else {(TARGET) = lydict_insert((CTX)->ctx, WORD, LEN);}
@@ -1305,6 +1315,13 @@
 
     INSERT_WORD(ctx, buf, inc->name, word, word_len);
 
+    /* submodules share the namespace with the module names, so there must not be
+     * a module of the same name in the context, no need for revision matching */
+    if (!strcmp(ctx->mod->name, inc->name) || ly_ctx_get_module_latest(ctx->ctx, inc->name)) {
+        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between module and submodule of name \"%s\".", inc->name);
+        return LY_EVALID;
+    }
+
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
         switch (kw) {
         case YANG_DESCRIPTION:
@@ -4304,8 +4321,10 @@
     /* get value */
     ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
     LY_CHECK_RET(ret);
-
     INSERT_WORD(ctx, buf, feat->name, word, word_len);
+
+    CHECK_UNIQUENESS(ctx, *features, name, "feature", feat->name);
+
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
         LY_CHECK_RET(ret);
 
@@ -4359,8 +4378,10 @@
     /* get value */
     ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
     LY_CHECK_RET(ret);
-
     INSERT_WORD(ctx, buf, ident->name, word, word_len);
+
+    CHECK_UNIQUENESS(ctx, *identities, name, "identity", ident->name);
+
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
         LY_CHECK_RET(ret);
 
@@ -4414,8 +4435,16 @@
     /* (sub)module name */
     ret = get_argument(ctx, data, Y_IDENTIF_ARG, &word, &buf, &word_len);
     LY_CHECK_RET(ret);
-
     INSERT_WORD(ctx, buf, mod->name, word, word_len);
+
+    /* submodules share the namespace with the module names, so there must not be
+     * a submodule of the same name in the context, no need for revision matching */
+    if (ly_ctx_get_submodule(ctx->ctx, NULL, mod->name, NULL)) {
+        LOGVAL_YANG(ctx, LYVE_SYNTAX_YANG, "Name collision between %s of name \"%s\".",
+                    mod->submodule ? "submodules" : "module and submodule", mod->name);
+        return LY_EVALID;
+    }
+
     YANG_READ_SUBSTMT_FOR(ctx, data, kw, word, word_len, ret) {
         LY_CHECK_RET(ret);
 
@@ -4641,6 +4670,7 @@
         mod->submodule = 1;
     }
     mod->ctx = ctx;
+    context.mod = mod;
 
     /* substatements */
     ret = parse_sub_module(&context, &data, mod);
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 874ca01..fe38709 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -1281,7 +1281,7 @@
         LOGERR(ctx, LY_EINVAL, "Invalid schema input format.");
         break;
     }
-    LY_CHECK_RET(ret, NULL);
+    LY_CHECK_ERR_RET(ret, free(mod), NULL);
 
     /* make sure that the newest revision is at position 0 */
     lysp_sort_revisions(mod->parsed->revs);
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 8971864..4e07c7d 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -871,8 +871,10 @@
     struct lysc_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
 
     uint8_t implemented:1;           /**< flag if the module is implemented, not just imported */
-    uint8_t latest_revision:1;       /**< flag if the module was loaded without specific revision and is
-                                          the latest revision found */
+    uint8_t latest_revision:2;       /**< flag to mark the latest available revision:
+                                          1 - the latest revision in searchdirs was not searched yet and this is the
+                                          latest revision in the current context
+                                          2 - searchdirs were searched and this is the latest available revision */
     uint8_t version;                 /**< yang-version (LYS_VERSION values) */
 };
 
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index 0c5c3ac..0bc2443 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -43,6 +43,7 @@
  */
 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 */
 };