patch 9.1.1014: Vim9: variable not found in transitive import
Problem: Vim9: variable not found in transitive import
Solution: Allow nested import (Hirohito Higashi)
fixe: #16379
closes: #16440
Signed-off-by: Hirohito Higashi <h.east.727@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 2f64ba3..018f10d 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt* For Vim version 9.1. Last change: 2025 Jan 12
+*options.txt* For Vim version 9.1. Last change: 2025 Jan 14
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -5582,6 +5582,7 @@
command recursion, see |E169|.
See also |:function|.
Also used for maximum depth of callback functions.
+ Also used for maximum depth of import. See |:import-cycle|.
*'maxmapdepth'* *'mmd'* *E223*
'maxmapdepth' 'mmd' number (default 1000)
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 9310df8..05c2126 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -4148,6 +4148,7 @@
E1042 vim9.txt /*E1042*
E1043 vim9.txt /*E1043*
E1044 vim9.txt /*E1044*
+E1045 vim9.txt /*E1045*
E1047 vim9.txt /*E1047*
E1048 vim9.txt /*E1048*
E1049 vim9.txt /*E1049*
diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt
index a978ea3..05b451c 100644
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -1,4 +1,4 @@
-*vim9.txt* For Vim version 9.1. Last change: 2024 Dec 23
+*vim9.txt* For Vim version 9.1. Last change: 2025 Jan 14
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -2051,13 +2051,14 @@
>
noremap ,a <ScriptCmd>:call s:that.OtherFunc()<CR>
<
- *:import-cycle*
-The `import` commands are executed when encountered. If script A imports
-script B, and B (directly or indirectly) imports A, this will be skipped over.
-At this point items in A after "import B" will not have been processed and
-defined yet. Therefore cyclic imports can exist and not result in an error
-directly, but may result in an error for items in A after "import B" not being
-defined. This does not apply to autoload imports, see the next section.
+ *:import-cycle* *E1045*
+The `import` commands are executed when encountered. It can be nested up to
+'maxfuncdepth' levels deep. If script A imports script B, and B (directly or
+indirectly) imports A, this will be skipped over. At this point items in A
+after "import B" will not have been processed and defined yet. Therefore
+cyclic imports can exist and not result in an error directly, but may result
+in an error for items in A after "import B" not being defined. This does not
+apply to autoload imports, see the next section.
Importing an autoload script ~
diff --git a/src/errors.h b/src/errors.h
index ad36e33..2811a32 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -2738,7 +2738,8 @@
INIT(= N_("E1043: Invalid command after :export"));
EXTERN char e_export_with_invalid_argument[]
INIT(= N_("E1044: Export with invalid argument"));
-// E1045 not used
+EXTERN char e_import_nesting_too_deep[]
+ INIT(= N_("E1045: Import nesting too deep"));
// E1046 not used
EXTERN char e_syntax_error_in_import_str[]
INIT(= N_("E1047: Syntax error in import: %s"));
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 23281bc..fc0edeb 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -3507,7 +3507,73 @@
v9.CheckScriptSuccess(lines)
enddef
-def Test_abstract_class()
+" Test for multi level import
+def Test_multi_level_import_normal()
+ var lines =<< trim END
+ vim9script
+ export class Property
+ public var value: string
+ endclass
+ END
+ writefile(lines, 'aa.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './aa.vim'
+ export class View
+ var content = aa.Property.new('')
+ endclass
+ END
+ writefile(lines, 'bb.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './bb.vim'
+ class MyView extends bb.View
+ def new(value: string)
+ this.content.value = value
+ enddef
+ endclass
+ var myView = MyView.new('This should be ok')
+ END
+ v9.CheckScriptSuccess(lines)
+enddef
+
+" Test for multi level import
+def Test_multi_level_import_nest_over()
+ var lines =<< trim END
+ vim9script
+ import './xbb.vim'
+ export class Property
+ public var value: string
+ endclass
+ END
+ writefile(lines, 'xaa.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ import './xaa.vim'
+ export class View
+ var content = aa.Property.new('')
+ endclass
+ END
+ writefile(lines, 'xbb.vim', 'D')
+
+ lines =<< trim END
+ vim9script
+ set maxfuncdepth=100
+ import './xbb.vim'
+ class MyView extends bb.View
+ def new(value: string)
+ this.content.value = value
+ enddef
+ endclass
+ var myView = MyView.new('This should be ok')
+ END
+ v9.CheckSourceFailure(lines, 'E1045: Import nesting too deep', 3)
+enddef
+
+def Test_abtstract_class()
var lines =<< trim END
vim9script
abstract class Base
diff --git a/src/version.c b/src/version.c
index 2cf4f46..57d7ec7 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1014,
+/**/
1013,
/**/
1012,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index a2dd77a..fa02871 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -778,6 +778,7 @@
static imported_T *
find_imported_in_script(char_u *name, size_t len, int sid)
{
+ static int nesting = 0;
scriptitem_T *si;
int idx;
@@ -792,6 +793,19 @@
: STRLEN(import->imp_name) == len
&& STRNCMP(name, import->imp_name, len) == 0)
return import;
+ else
+ {
+ if (nesting >= p_mfd)
+ {
+ emsg(_(e_import_nesting_too_deep));
+ return NULL;
+ }
+ ++nesting;
+ import = find_imported_in_script(name, len, import->imp_sid);
+ --nesting;
+ if (import != NULL)
+ return import;
+ }
}
return NULL;
}