tree schema BUGFIX unlinking derived identities on error

Fixes #1808
diff --git a/src/context.c b/src/context.c
index 15043d3..7fb58df 100644
--- a/src/context.c
+++ b/src/context.c
@@ -1251,7 +1251,7 @@
             lysc_module_free(mod->compiled);
             mod->compiled = NULL;
         }
-        lys_module_free(ctx->list.objs[ctx->list.count - 1]);
+        lys_module_free(ctx->list.objs[ctx->list.count - 1], 0);
     }
     free(ctx->list.objs);
 
diff --git a/src/tree_schema.c b/src/tree_schema.c
index eec6e97..c7f75ef 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -1141,7 +1141,7 @@
         }
 
         /* free the module */
-        lys_module_free(m);
+        lys_module_free(m, 1);
     }
 
     if (unres->implementing.count) {
@@ -1713,7 +1713,7 @@
         }
     }
     if (!module_created) {
-        lys_module_free(mod);
+        lys_module_free(mod, 0);
         mod = mod_dup;
     }
 
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index fdb0bd5..c6c9344 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -611,6 +611,74 @@
     FREE_ARRAY(ctx, must->exts, lysc_ext_instance_free);
 }
 
+static void
+lysc_ident_derived_unlink(const struct lysc_ident *ident)
+{
+    LY_ARRAY_COUNT_TYPE u, v, w;
+    const struct lysp_submodule *submod;
+    const struct lysp_module *base_pmod;
+    const struct lysp_ident *identp = NULL;
+    const struct lys_module *mod;
+    const char *base_name;
+
+    /* find the parsed identity */
+    LY_ARRAY_FOR(ident->module->parsed->identities, u) {
+        if (ident->module->parsed->identities[u].name == ident->name) {
+            identp = &ident->module->parsed->identities[u];
+            base_pmod = ident->module->parsed;
+            break;
+        }
+    }
+    if (!identp) {
+        LY_ARRAY_FOR(ident->module->parsed->includes, v) {
+            submod = ident->module->parsed->includes[v].submodule;
+            LY_ARRAY_FOR(submod->identities, u) {
+                if (submod->identities[u].name == ident->name) {
+                    identp = &submod->identities[u];
+                    base_pmod = (struct lysp_module *)submod;
+                    break;
+                }
+            }
+        }
+    }
+    assert(identp);
+
+    /* remove link from all the foreign bases, it may not be there if identity compilation failed */
+    LY_ARRAY_FOR(identp->bases, u) {
+        base_name = strchr(identp->bases[u], ':');
+        if (!base_name) {
+            continue;
+        }
+
+        /* prefixed identity */
+        mod = ly_resolve_prefix(ident->module->ctx, identp->bases[u], base_name - identp->bases[u], LY_VALUE_SCHEMA,
+                (void *)base_pmod);
+        if (!mod) {
+            continue;
+        }
+        ++base_name;
+
+        /* find the compiled base */
+        LY_ARRAY_FOR(mod->identities, v) {
+            if (!strcmp(mod->identities[v].name, base_name)) {
+                /* find the derived link */
+                LY_ARRAY_FOR(mod->identities[v].derived, w) {
+                    if (mod->identities[v].derived[w] == ident) {
+                        /* remove the link */
+                        LY_ARRAY_DECREMENT(mod->identities[v].derived);
+                        if (w < LY_ARRAY_COUNT(mod->identities[v].derived)) {
+                            memmove(mod->identities[v].derived + w, mod->identities[v].derived + w + 1,
+                                    (LY_ARRAY_COUNT(mod->identities[v].derived) - w) * sizeof ident);
+                        }
+                        break;
+                    }
+                }
+                break;
+            }
+        }
+    }
+}
+
 void
 lysc_ident_free(struct ly_ctx *ctx, struct lysc_ident *ident)
 {
@@ -1014,14 +1082,23 @@
 }
 
 void
-lys_module_free(struct lys_module *module)
+lys_module_free(struct lys_module *module, ly_bool remove_links)
 {
+    LY_ARRAY_COUNT_TYPE u;
+
     if (!module) {
         return;
     }
+
     assert(!module->implemented);
     assert(!module->compiled);
 
+    if (remove_links) {
+        /* remove derived identity links */
+        LY_ARRAY_FOR(module->identities, u) {
+            lysc_ident_derived_unlink(&module->identities[u]);
+        }
+    }
     FREE_ARRAY(module->ctx, module->identities, lysc_ident_free);
     lysp_module_free(module->parsed);
 
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index a5ff013..a62f273 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -809,8 +809,10 @@
  * @brief Free the schema structure. It just frees, it does not remove the schema from its context.
  *
  * @param[in,out] module Schema module structure to free.
+ * @param[in] remove_links Whether to remove links in other modules to structures in this module. Not needed if
+ * the whole context is being freed.
  */
-void lys_module_free(struct lys_module *module);
+void lys_module_free(struct lys_module *module, ly_bool remove_links);
 
 /**
  * @brief match yang keyword
diff --git a/tests/utests/schema/test_parser_yang.c b/tests/utests/schema/test_parser_yang.c
index 9625ea9..6c18238 100644
--- a/tests/utests/schema/test_parser_yang.c
+++ b/tests/utests/schema/test_parser_yang.c
@@ -104,7 +104,7 @@
 static int
 teardown(void **state)
 {
-    lys_module_free(PARSER_CUR_PMOD(YCTX)->mod);
+    lys_module_free(PARSER_CUR_PMOD(YCTX)->mod, 0);
     LOG_LOCBACK(0, 0, 0, 1);
 
     ly_set_free(YCTX->parsed_mods, NULL);
@@ -552,7 +552,7 @@
     struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
     struct lysp_module *pmod;
 
-    lys_module_free(PARSER_CUR_PMOD(ctx)->mod);
+    lys_module_free(PARSER_CUR_PMOD(ctx)->mod, 0);
     pmod = calloc(1, sizeof *pmod);
     ctx->parsed_mods->objs[0] = pmod;
     pmod->mod = calloc(1, sizeof *pmod->mod);
@@ -570,7 +570,7 @@
     struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
     struct lysp_submodule *submod;
 
-    lys_module_free(PARSER_CUR_PMOD(ctx)->mod);
+    lys_module_free(PARSER_CUR_PMOD(ctx)->mod, 0);
     submod = calloc(1, sizeof *submod);
     ctx->parsed_mods->objs[0] = submod;
     submod->mod = calloc(1, sizeof *submod->mod);
@@ -772,7 +772,7 @@
     assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m));
     CHECK_LOG_CTX("Trailing garbage \"module q {names...\" after module, expected end-of-input.", "Line number 1.");
     yang_parser_ctx_free(ctx_p);
-    lys_module_free(m);
+    lys_module_free(m, 0);
 
     in.current = "prefix " SCHEMA_BEGINNING "}";
     m = calloc(1, sizeof *m);
@@ -780,7 +780,7 @@
     assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m));
     CHECK_LOG_CTX("Invalid keyword \"prefix\", expected \"module\" or \"submodule\".", "Line number 1.");
     yang_parser_ctx_free(ctx_p);
-    lys_module_free(m);
+    lys_module_free(m, 0);
 
     in.current = "module " SCHEMA_BEGINNING "leaf enum {type enumeration {enum seven { position 7;}}}}";
     m = calloc(1, sizeof *m);
@@ -788,7 +788,7 @@
     assert_int_equal(LY_EVALID, yang_parse_module(&ctx_p, &in, m));
     CHECK_LOG_CTX("Invalid keyword \"position\" as a child of \"enum\".", "Line number 1.");
     yang_parser_ctx_free(ctx_p);
-    lys_module_free(m);
+    lys_module_free(m, 0);
 
     /* extensions */
     TEST_GENERIC("prefix:test;}", mod->exts,
diff --git a/tests/utests/schema/test_parser_yin.c b/tests/utests/schema/test_parser_yin.c
index 59353a4..81c4eae 100644
--- a/tests/utests/schema/test_parser_yin.c
+++ b/tests/utests/schema/test_parser_yin.c
@@ -162,7 +162,7 @@
 static int
 teardown_ctx(void **UNUSED(state))
 {
-    lys_module_free(PARSER_CUR_PMOD(YCTX)->mod);
+    lys_module_free(PARSER_CUR_PMOD(YCTX)->mod, 0);
     yin_parser_ctx_free(YCTX);
     YCTX = NULL;
 
@@ -3428,7 +3428,7 @@
     struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
     struct lysp_module *pmod;
 
-    lys_module_free(PARSER_CUR_PMOD(ctx)->mod);
+    lys_module_free(PARSER_CUR_PMOD(ctx)->mod, 0);
     pmod = calloc(1, sizeof *pmod);
     ctx->parsed_mods->objs[0] = pmod;
     pmod->mod = calloc(1, sizeof *pmod->mod);
@@ -3558,7 +3558,7 @@
     struct ly_ctx *ly_ctx = PARSER_CUR_PMOD(ctx)->mod->ctx;
     struct lysp_submodule *submod;
 
-    lys_module_free(PARSER_CUR_PMOD(ctx)->mod);
+    lys_module_free(PARSER_CUR_PMOD(ctx)->mod, 0);
     submod = calloc(1, sizeof *submod);
     ctx->parsed_mods->objs[0] = submod;
     submod->mod = calloc(1, sizeof *submod->mod);
@@ -3717,7 +3717,7 @@
     assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS);
     assert_null(mod->parsed->exts->child->next->child);
     assert_string_equal(mod->parsed->exts->child->next->arg, "test");
-    lys_module_free(mod);
+    lys_module_free(mod, 0);
     yin_parser_ctx_free(yin_ctx);
     ly_in_free(in, 0);
     mod = NULL;
@@ -3755,7 +3755,7 @@
             "</module>\n";
     assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
     assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS);
-    lys_module_free(mod);
+    lys_module_free(mod, 0);
     yin_parser_ctx_free(yin_ctx);
     ly_in_free(in, 0);
     mod = NULL;
@@ -3770,7 +3770,7 @@
             "</module>\n";
     assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
     assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_SUCCESS);
-    lys_module_free(mod);
+    lys_module_free(mod, 0);
     yin_parser_ctx_free(yin_ctx);
     ly_in_free(in, 0);
     mod = NULL;
@@ -3783,7 +3783,7 @@
     assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
     assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EINVAL);
     CHECK_LOG_CTX("Input data contains submodule which cannot be parsed directly without its main module.", NULL);
-    lys_module_free(mod);
+    lys_module_free(mod, 0);
     yin_parser_ctx_free(yin_ctx);
     ly_in_free(in, 0);
 
@@ -3798,7 +3798,7 @@
     assert_int_equal(ly_in_new_memory(data, &in), LY_SUCCESS);
     assert_int_equal(yin_parse_module(&yin_ctx, in, mod), LY_EVALID);
     CHECK_LOG_CTX("Trailing garbage \"<module>\" after module, expected end-of-input.", "Line number 6.");
-    lys_module_free(mod);
+    lys_module_free(mod, 0);
     yin_parser_ctx_free(yin_ctx);
     ly_in_free(in, 0);
     mod = NULL;