diff --git a/src/plugins_exts.c b/src/plugins_exts.c index d796a68225..a19ef20835 100644 --- a/src/plugins_exts.c +++ b/src/plugins_exts.c @@ -559,15 +559,17 @@ lyplg_ext_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t st LIBYANG_API_DEF LY_ERR lyplg_ext_parsed_get_storage(const struct lysc_ext_instance *ext, int stmt, uint32_t storage_size, const void **storage) { - LY_ARRAY_COUNT_TYPE u; + LY_ARRAY_COUNT_TYPE u, v; const struct lysp_ext_instance *extp = NULL; + const struct lysp_submodule *submod; const char *extp_name; enum ly_stmt match = 0; void **s_p = NULL; LY_CHECK_ARG_RET(NULL, ext, ext->module->parsed, LY_EINVAL); - /* find the parsed ext instance */ + /* find the parsed ext instance, it may be a top-level extension instance of the module or + * any of its submodules */ LY_ARRAY_FOR(ext->module->parsed->exts, u) { extp = &ext->module->parsed->exts[u]; extp_name = strchr(extp->name, ':') + 1; @@ -577,6 +579,21 @@ lyplg_ext_parsed_get_storage(const struct lysc_ext_instance *ext, int stmt, uint } extp = NULL; } + for (v = 0; !extp && (v < LY_ARRAY_COUNT(ext->module->parsed->includes)); ++v) { + submod = ext->module->parsed->includes[v].submodule; + if (!submod) { + continue; + } + LY_ARRAY_FOR(submod->exts, u) { + extp = &submod->exts[u]; + extp_name = strchr(extp->name, ':') + 1; + + if (!strcmp(ext->def->name, extp_name)) { + break; + } + extp = NULL; + } + } assert(extp); if (!(stmt & LY_STMT_NODE_MASK)) { diff --git a/tests/utests/extensions/test_structure.c b/tests/utests/extensions/test_structure.c index d8bea8ea99..318a5a7992 100644 --- a/tests/utests/extensions/test_structure.c +++ b/tests/utests/extensions/test_structure.c @@ -448,6 +448,54 @@ test_xpath(void **state) lyd_free_all(tree); } +static LY_ERR +submod_imp_clb(const char *UNUSED(mod_name), const char *UNUSED(mod_rev), const char *submod_name, + const char *UNUSED(sub_rev), void *user_data, LYS_INFORMAT *format, const char **module_data, + void (**free_module_data)(void *model_data, void *user_data)) +{ + if (!submod_name || strcmp(submod_name, "c_sub")) { + return LY_ENOTFOUND; + } + + *module_data = user_data; + *format = LYS_IN_YANG; + *free_module_data = NULL; + return LY_SUCCESS; +} + +static void +test_submod(void **state) +{ + struct lys_module *mod; + char *printed = NULL; + const char *data, *submod; + + /* top-level extension instances of a submodule are compiled into the module + * but their parsed instances are stored in the submodule */ + submod = "submodule c_sub {yang-version 1.1; belongs-to c {prefix c;}" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:augment-structure \"/c:basetop/c:x\" {leaf z {type string;}}" + "}"; + ly_ctx_set_module_imp_clb(UTEST_LYCTX, submod_imp_clb, (void *)submod); + + data = "module c {yang-version 1.1; namespace urn:tests:extensions:structure:c; prefix c;" + "include c_sub;" + "import ietf-yang-structure-ext {prefix sx;}" + "sx:structure basetop {container x {leaf l {type string;}}}" + "}"; + UTEST_ADD_MODULE(data, LYS_IN_YANG, NULL, &mod); + + ly_ctx_set_module_imp_clb(UTEST_LYCTX, NULL, NULL); + + /* tree print with compiled nodes iterates compiled extension instances, the parsed one + * of a submodule extension instance used not to be found and asserted on */ + assert_int_equal(LY_SUCCESS, ly_ctx_set_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED)); + assert_int_equal(LY_SUCCESS, lys_print_mem(&printed, mod, LYS_OUT_TREE, 0)); + assert_int_equal(LY_SUCCESS, ly_ctx_unset_options(UTEST_LYCTX, LY_CTX_SET_PRIV_PARSED)); + assert_non_null(strstr(printed, "augment-structure /c:basetop/c:x:")); + free(printed); +} + int main(void) { @@ -456,6 +504,7 @@ main(void) UTEST(test_schema_invalid), UTEST(test_parse), UTEST(test_xpath), + UTEST(test_submod), }; return cmocka_run_group_tests(tests, NULL, NULL);