schema parsers BUGFIX handling includes in submodules

The result of parsing includes in submodule was incorrectly checked.

Instead of parsing submodule all the times, check the submodule presence
in main module and re-use it.

In YANG 1.0, main module is not required to include all the submodules.
Some of the submodules can be included via another submodule. In such a
case, the include record is now injected also into the main module to be
simply available and to unify it with YANG 1.1. Such a record is marked
as injected and it is not printed.

On the other hand, YANG 1.1 requires all the submodules to be present in
main module. So the situation described above raises error.

Fixes #1353
diff --git a/src/printer_yang.c b/src/printer_yang.c
index 4afc83d..7dc0109 100644
--- a/src/printer_yang.c
+++ b/src/printer_yang.c
@@ -2037,6 +2037,10 @@
         ly_print_(ctx->out, "%*s}\n", INDENT);
     }
     LY_ARRAY_FOR(modp->includes, u) {
+        if (modp->includes[u].injected) {
+            /* do not print the includes injected from submodules */
+            continue;
+        }
         if (modp->includes[u].rev[0] || modp->includes[u].dsc || modp->includes[u].ref || modp->includes[u].exts) {
             ly_print_(ctx->out, "%s%*sinclude %s {\n", u ? "" : "\n",  INDENT, modp->includes[u].name);
             LEVEL++;
diff --git a/src/printer_yin.c b/src/printer_yin.c
index 26a9eb6..8926394 100644
--- a/src/printer_yin.c
+++ b/src/printer_yin.c
@@ -1390,6 +1390,10 @@
         ypr_close(ctx, "import", 1);
     }
     LY_ARRAY_FOR(modp->includes, u) {
+        if (modp->includes[u].injected) {
+            /* do not print the includes injected from submodules */
+            continue;
+        }
         if (modp->includes[u].rev[0] || modp->includes[u].dsc || modp->includes[u].ref || modp->includes[u].exts) {
             ypr_open(ctx, "include", "module", modp->includes[u].name, 1);
             LEVEL++;
diff --git a/src/schema_compile.h b/src/schema_compile.h
index 2dcc835..17f8105 100644
--- a/src/schema_compile.h
+++ b/src/schema_compile.h
@@ -115,7 +115,7 @@
  * @param[out] DUP Where to store the result.
  */
 #define DUP_STRING(CTX, ORIG, DUP, RET) if (ORIG) {RET = lydict_insert(CTX, ORIG, 0, &DUP);}
-
+#define DUP_STRING_RET(CTX, ORIG, DUP) if (ORIG) {LY_ERR __ret = lydict_insert(CTX, ORIG, 0, &DUP); LY_CHECK_RET(__ret);}
 #define DUP_STRING_GOTO(CTX, ORIG, DUP, RET, GOTO) if (ORIG) {LY_CHECK_GOTO(RET = lydict_insert(CTX, ORIG, 0, &DUP), GOTO);}
 
 #define DUP_ARRAY(CTX, ORIG_ARRAY, NEW_ARRAY, DUP_FUNC) \
diff --git a/src/tree_schema.c b/src/tree_schema.c
index 2935a67..324f3f2 100644
--- a/src/tree_schema.c
+++ b/src/tree_schema.c
@@ -751,7 +751,6 @@
 lys_resolve_import_include(struct lys_parser_ctx *pctx, struct lysp_module *pmod)
 {
     struct lysp_import *imp;
-    struct lysp_include *inc;
     LY_ARRAY_COUNT_TYPE u, v;
 
     pmod->parsing = 1;
@@ -768,12 +767,8 @@
             }
         }
     }
-    LY_ARRAY_FOR(pmod->includes, u) {
-        inc = &pmod->includes[u];
-        if (!inc->submodule) {
-            LY_CHECK_RET(lysp_load_submodule(pctx, inc));
-        }
-    }
+    LY_CHECK_RET(lysp_load_submodules(pctx, pmod));
+
     pmod->parsing = 0;
 
     return LY_SUCCESS;
diff --git a/src/tree_schema.h b/src/tree_schema.h
index 418944a..51312ef 100644
--- a/src/tree_schema.h
+++ b/src/tree_schema.h
@@ -462,6 +462,9 @@
     const char *ref;                 /**< reference */
     struct lysp_ext_instance *exts;  /**< list of the extension instances ([sized array](@ref sizedarrays)) */
     char rev[LY_REV_SIZE];           /**< revision-date of the included submodule */
+    ly_bool injected;                /**< flag to mark includes copied into main module from submodules,
+                                          only for backward compatibility with YANG 1.0, which does not require the
+                                          main module to include all submodules. */
 };
 
 /**
diff --git a/src/tree_schema_free.c b/src/tree_schema_free.c
index dbb2d11..0098040 100644
--- a/src/tree_schema_free.c
+++ b/src/tree_schema_free.c
@@ -69,10 +69,22 @@
     FREE_ARRAY(ctx, import->exts, lysp_ext_instance_free);
 }
 
-void
-lysp_include_free(struct ly_ctx *ctx, struct lysp_include *include)
+/**
+ * @brief Common function to erase include record in main module and submodule.
+ *
+ * There is a difference since the main module is expected to have the complete list if the included submodules and
+ * the parsed submodule is shared with any include in a submodule. Therefore, the referenced submodules in the include
+ * record are freed only from main module's records.
+ *
+ * @param[in] ctx libyang context
+ * @param[in] include The include record to be erased, the record itself is not freed.
+ * @param[in] main_module Flag to get know if the include record is placed in main module so also the referenced submodule
+ * is supposed to be freed.
+ */
+static void
+lysp_include_free_(struct ly_ctx *ctx, struct lysp_include *include, ly_bool main_module)
 {
-    if (include->submodule) {
+    if (main_module && include->submodule) {
         lysp_module_free((struct lysp_module *)include->submodule);
     }
     FREE_STRING(ctx, include->name);
@@ -82,6 +94,18 @@
 }
 
 void
+lysp_include_free_submodule(struct ly_ctx *ctx, struct lysp_include *include)
+{
+    return lysp_include_free_(ctx, include, 0);
+}
+
+void
+lysp_include_free(struct ly_ctx *ctx, struct lysp_include *include)
+{
+    return lysp_include_free_(ctx, include, 1);
+}
+
+void
 lysp_revision_free(struct ly_ctx *ctx, struct lysp_revision *rev)
 {
     FREE_STRING(ctx, rev->dsc);
@@ -423,7 +447,7 @@
     ctx = module->mod->ctx;
 
     FREE_ARRAY(ctx, module->imports, lysp_import_free);
-    FREE_ARRAY(ctx, module->includes, lysp_include_free);
+    FREE_ARRAY(ctx, module->includes, module->is_submod ? lysp_include_free_submodule : lysp_include_free);
 
     FREE_ARRAY(ctx, module->revs, lysp_revision_free);
     FREE_ARRAY(ctx, module->extensions, lysp_ext_free);
diff --git a/src/tree_schema_helpers.c b/src/tree_schema_helpers.c
index 5bbe14b..f0b10df 100644
--- a/src/tree_schema_helpers.c
+++ b/src/tree_schema_helpers.c
@@ -929,61 +929,180 @@
     return LY_SUCCESS;
 }
 
-LY_ERR
-lysp_load_submodule(struct lys_parser_ctx *pctx, struct lysp_include *inc)
+/**
+ * @brief Try to find the parsed submodule in main module for the given include record.
+ *
+ * @param[in] pctx main parser context
+ * @param[in] inc The include record with missing parsed submodule. According to include info try to find
+ * the corresponding parsed submodule in main module's includes.
+ * @return LY_SUCCESS - the parsed submodule was found and inserted into the @p inc record
+ * @return LY_ENOT - the parsed module was not found.
+ * @return LY_EVALID - YANG rule violation
+ */
+static LY_ERR
+lysp_get_submodule(struct lys_parser_ctx *pctx, struct lysp_include *inc)
 {
-    struct ly_ctx *ctx = (struct ly_ctx *)(PARSER_CTX(pctx));
-    struct lysp_submodule *submod = NULL;
-    const char *submodule_data = NULL;
-    LYS_INFORMAT format = LYS_IN_UNKNOWN;
+    LY_ARRAY_COUNT_TYPE i;
+    struct lysp_module *main_pmod = pctx->parsed_mod->mod->parsed;
 
-    void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
-    struct lysp_load_module_check_data check_data = {0};
-    struct ly_in *in;
+    LY_ARRAY_FOR(main_pmod->includes, i) {
+        if (strcmp(main_pmod->includes[i].name, inc->name)) {
+            continue;
+        }
 
-    /* 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(pctx->parsed_mod->mod->name, NULL, inc->name, inc->rev[0] ? inc->rev : NULL, ctx->imp_clb_data,
-                    &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
-                LY_CHECK_RET(ly_in_new_memory(submodule_data, &in));
-                check_data.name = inc->name;
-                check_data.revision = inc->rev[0] ? inc->rev : NULL;
-                check_data.submoduleof = pctx->parsed_mod->mod->name;
-                lys_parse_submodule(ctx, in, format, pctx, lysp_load_module_check, &check_data, &submod);
-                ly_in_free(in, 0);
-                if (submodule_data_free) {
-                    submodule_data_free((void *)submodule_data, ctx->imp_clb_data);
+        if (inc->rev[0] && strncmp(inc->rev, main_pmod->includes[i].rev, LY_REV_SIZE)) {
+            LOGVAL(PARSER_CTX(pctx), LYVE_REFERENCE,
+                    "Submodule %s includes different revision (%s) of the submodule %s:%s included by the main module %s.",
+                    ((struct lysp_submodule *)pctx->parsed_mod)->name, inc->rev,
+                    main_pmod->includes[i].name, main_pmod->includes[i].rev, main_pmod->mod->name);
+            return LY_EVALID;
+        }
+
+        inc->submodule = main_pmod->includes[i].submodule;
+        return inc->submodule ? LY_SUCCESS : LY_ENOT;
+    }
+
+    if (main_pmod->version == LYS_VERSION_1_1) {
+        LOGVAL(PARSER_CTX(pctx), LYVE_REFERENCE,
+                "YANG 1.1 requires all submodules to be included from main module. "
+                "But submodule \"%s\" includes submodule \"%s\" which is not included by main module \"%s\".",
+                ((struct lysp_submodule *)pctx->parsed_mod)->name, inc->name, main_pmod->mod->name);
+        return LY_EVALID;
+    } else {
+        return LY_ENOT;
+    }
+}
+
+/**
+ * @brief Make the copy of the given include record into the main module.
+ *
+ * YANG 1.0 does not require the main module to include all the submodules. Therefore, parsing submodules can cause
+ * reallocating and extending the includes array in the main module by the submodules included only in submodules.
+ *
+ * @param[in] pctx main parser context
+ * @param[in] inc Include record to copy into main module taken from @p pctx.
+ * @return LY_ERR value.
+ */
+static LY_ERR
+lysp_inject_submodule(struct lys_parser_ctx *pctx, struct lysp_include *inc)
+{
+    LY_ARRAY_COUNT_TYPE i;
+    struct lysp_include *inc_new, *inc_tofill = NULL;
+    struct lysp_module *main_pmod = pctx->parsed_mod->mod->parsed;
+
+    /* first, try to find the corresponding record with missing parsed submodule */
+    LY_ARRAY_FOR(main_pmod->includes, i) {
+        if (strcmp(main_pmod->includes[i].name, inc->name)) {
+            continue;
+        }
+        inc_tofill = &main_pmod->includes[i];
+        break;
+    }
+
+    if (inc_tofill) {
+        inc_tofill->submodule = inc->submodule;
+    } else {
+        LY_ARRAY_NEW_RET(PARSER_CTX(pctx), main_pmod->includes, inc_new, LY_EMEM);
+
+        inc_new->submodule = inc->submodule;
+        DUP_STRING_RET(PARSER_CTX(pctx), inc->name, inc_new->name);
+        DUP_STRING_RET(PARSER_CTX(pctx), inc->dsc, inc_new->dsc);
+        DUP_STRING_RET(PARSER_CTX(pctx), inc->ref, inc_new->ref);
+        /* TODO duplicate extensions */
+        memcpy(inc_new->rev, inc->rev, LY_REV_SIZE);
+        inc_new->injected = 1;
+    }
+    return LY_SUCCESS;
+}
+
+LY_ERR
+lysp_load_submodules(struct lys_parser_ctx *pctx, struct lysp_module *pmod)
+{
+    LY_ARRAY_COUNT_TYPE u;
+    struct ly_ctx *ctx = PARSER_CTX(pctx);
+
+    LY_ARRAY_FOR(pmod->includes, u) {
+        LY_ERR ret = LY_SUCCESS;
+        struct lysp_submodule *submod = NULL;
+        struct lysp_include *inc = &pmod->includes[u];
+
+        if (inc->submodule) {
+            continue;
+        }
+
+        if (pmod->is_submod) {
+            /* try to find the submodule in the main module or its submodules */
+            ret = lysp_get_submodule(pctx, inc);
+            LY_CHECK_RET(ret && ret != LY_ENOT, ret);
+            LY_CHECK_RET(ret == LY_SUCCESS, LY_SUCCESS); /* submodule found in linked with the inc */
+        }
+
+        /* submodule not present in the main module, get the input data and parse it */
+        if (!(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+    search_clb:
+            if (ctx->imp_clb) {
+                const char *submodule_data = NULL;
+                LYS_INFORMAT format = LYS_IN_UNKNOWN;
+                void (*submodule_data_free)(void *module_data, void *user_data) = NULL;
+                struct lysp_load_module_check_data check_data = {0};
+                struct ly_in *in;
+
+                if (ctx->imp_clb(pctx->parsed_mod->mod->name, NULL, inc->name,
+                        inc->rev[0] ? inc->rev : NULL, ctx->imp_clb_data,
+                        &format, &submodule_data, &submodule_data_free) == LY_SUCCESS) {
+                    LY_CHECK_RET(ly_in_new_memory(submodule_data, &in));
+                    check_data.name = inc->name;
+                    check_data.revision = inc->rev[0] ? inc->rev : NULL;
+                    check_data.submoduleof = pctx->parsed_mod->mod->name;
+                    lys_parse_submodule(ctx, in, format, pctx, lysp_load_module_check, &check_data, &submod);
+
+                    /* update inc pointer - parsing another (YANG 1.0) submodule can cause injecting
+                     * submodule's include into main module, where it is missing */
+                    inc = &pmod->includes[u];
+
+                    ly_in_free(in, 0);
+                    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)) {
-            /* submodule was not received from the callback or there is no callback set */
-            lys_module_localfile(ctx, inc->name, inc->rev[0] ? inc->rev : NULL, NULL, 0, pctx,
-                    pctx->parsed_mod->mod->name, 1, NULL, (void **)&submod);
-        }
-        if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
-            goto search_clb;
-        }
-    }
-    if (submod) {
-        if (!inc->rev[0] && (submod->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->latest_revision = 2;
-        }
+            if (!submod && !(ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+                goto search_file;
+            }
+        } else {
+    search_file:
+            if (!(ctx->flags & LY_CTX_DISABLE_SEARCHDIRS)) {
+                /* submodule was not received from the callback or there is no callback set */
+                lys_module_localfile(ctx, inc->name,
+                        inc->rev[0] ? inc->rev : NULL, NULL, 0, pctx,
+                        pctx->parsed_mod->mod->name, 1, NULL, (void **)&submod);
 
-        inc->submodule = submod;
-    }
-    if (!inc->submodule) {
-        LOGVAL(ctx, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name, pctx->parsed_mod->mod->name);
-        return LY_EVALID;
+                /* update inc pointer - parsing another (YANG 1.0) submodule can cause injecting
+                 * submodule's include into main module, where it is missing */
+                inc = &pmod->includes[u];
+            }
+            if (!submod && (ctx->flags & LY_CTX_PREFER_SEARCHDIRS)) {
+                goto search_clb;
+            }
+        }
+        if (submod) {
+            if (!inc->rev[0] && (submod->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->latest_revision = 2;
+            }
+
+            inc->submodule = submod;
+            if (ret == LY_ENOT) {
+                /* the submodule include is not present in YANG 1.0 main module - add it there */
+                LY_CHECK_RET(lysp_inject_submodule(pctx, &pmod->includes[u]));
+            }
+        }
+        if (!inc->submodule) {
+            LOGVAL(ctx, LYVE_REFERENCE, "Including \"%s\" submodule into \"%s\" failed.", inc->name,
+                    pctx->parsed_mod->is_submod ? ((struct lysp_submodule *)pctx->parsed_mod)->name : pctx->parsed_mod->mod->name);
+            return LY_EVALID;
+        }
     }
 
     return LY_SUCCESS;
diff --git a/src/tree_schema_internal.h b/src/tree_schema_internal.h
index dbdf49b..5a2097b 100644
--- a/src/tree_schema_internal.h
+++ b/src/tree_schema_internal.h
@@ -296,14 +296,16 @@
         const char **features, struct lys_glob_unres *unres, struct lys_module **mod);
 
 /**
- * @brief Parse included submodule into the simply parsed YANG module.
+ * @brief Parse included submodules into the simply parsed YANG module.
+ *
+ * YANG 1.0 does not require the main module to include all the submodules. Therefore, parsing submodules can cause
+ * reallocating and extending the includes array in the main module by the submodules included only in submodules.
  *
  * @param[in] pctx main parser context
- * @param[in,out] inc Include structure holding all available information about the include statement, the parsed
- * submodule is stored into this structure.
+ * @param[in] pmod Parsed module with the includes array to be processed.
  * @return LY_ERR value.
  */
-LY_ERR lysp_load_submodule(struct lys_parser_ctx *pctx, struct lysp_include *inc);
+LY_ERR lysp_load_submodules(struct lys_parser_ctx *pctx, struct lysp_module *pmod);
 
 /**
  * @brief Free a parsed restriction.