]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.1119: Vim9: Not able to use an autoloaded class from another autoloaded... v9.1.1119
authorYegappan Lakshmanan <yegappan@yahoo.com>
Mon, 17 Feb 2025 19:21:23 +0000 (20:21 +0100)
committerChristian Brabandt <cb@256bit.org>
Mon, 17 Feb 2025 19:21:23 +0000 (20:21 +0100)
Problem:  Vim9: Not able to use an autoloaded class from another
          autoloaded script (Elliot)
Solution: make it work (Yegappan Lakshmanan)

fixes: #15031
closes: #16652

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/errors.h
src/testdir/test_vim9_class.vim
src/testdir/test_vim9_import.vim
src/version.c
src/vim9class.c
src/vim9execute.c

index 34a0d5832fe5a0f48497172cc8256ce5ee05a0e2..a1165056ba1e341c21f1b4959cbd55ba16568e62 100644 (file)
@@ -3552,7 +3552,8 @@ EXTERN char e_type_can_only_be_defined_in_vim9_script[]
        INIT(= N_("E1393: Type can only be defined in Vim9 script"));
 EXTERN char e_type_name_must_start_with_uppercase_letter_str[]
        INIT(= N_("E1394: Type name must start with an uppercase letter: %s"));
-// E1395 unused
+EXTERN char e_using_null_class[]
+       INIT(= N_("E1395: Using a null class"));
 EXTERN char e_typealias_already_exists_for_str[]
        INIT(= N_("E1396: Type alias \"%s\" already exists"));
 EXTERN char e_missing_typealias_name[]
index 0f692ed9cff7bec3eda4df24dbd9a1d10968940b..1e63f22df911f94592512b860bd3c0db2c555cf8 100644 (file)
@@ -544,7 +544,7 @@ def Test_using_null_class()
   var lines =<< trim END
     @_ = null_class.member
   END
-  v9.CheckDefExecAndScriptFailure(lines, ['E715: Dictionary required', 'E1363: Incomplete type'])
+  v9.CheckDefExecAndScriptFailure(lines, ['E1395: Using a null class', 'E1363: Incomplete type'])
 
   # Test for using a null class as a value
   lines =<< trim END
index c92aed065b06e819a8df747c60a65b691774328b..8d81697b2c2802ea7b67105c9e77cc7c149e5260 100644 (file)
@@ -3494,4 +3494,75 @@ def Test_vim9_import_and_class_extends_2()
   &rtp = save_rtp
 enddef
 
+" Test for using an autoloaded class from another autoloaded script
+def Test_class_from_auloaded_script()
+  mkdir('Xdir', 'R')
+  var save_rtp = &rtp
+  &rtp = getcwd()
+  exe 'set rtp^=' .. getcwd() .. '/Xdir'
+
+  mkdir('Xdir/autoload/SomeClass/bar', 'p')
+
+  var lines =<< trim END
+    vim9script
+
+    export class Baz
+      static var v1: string = "v1"
+      var v2: string = "v2"
+      def GetName(): string
+        return "baz"
+      enddef
+    endclass
+  END
+  writefile(lines, 'Xdir/autoload/SomeClass/bar/baz.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+
+    import autoload './bar/baz.vim'
+
+    export def MyTestFoo(): string
+      assert_fails('var x = baz.Baz.NonExisting()', 'E1325: Method "NonExisting" not found in class "Baz"')
+      assert_fails('var x = baz.Baz.foobar', 'E1337: Class variable "foobar" not found in class "Baz"')
+
+      const instance = baz.Baz.new()
+      return $'{instance.GetName()} {baz.Baz.v1} {instance.v2}'
+    enddef
+  END
+  writefile(lines, 'Xdir/autoload/SomeClass/foo.vim', 'D')
+
+  lines =<< trim END
+    vim9script
+
+    import autoload 'SomeClass/foo.vim'
+    import autoload 'SomeClass/bar/baz.vim'
+
+    def NotInAutoload()
+      # Use non-existing class method and variable
+      assert_fails('var x = baz.Baz.NonExisting()', 'E1325: Method "NonExisting" not found in class "Baz"')
+
+      var caught_exception = false
+      try
+        var x = baz.Baz.foobar
+      catch /E1337: Class variable "foobar" not found in class "Baz"/
+        caught_exception = true
+      endtry
+      assert_true(caught_exception)
+
+      const instance = baz.Baz.new()
+      assert_equal("baz v1 v2", $'{instance.GetName()} {baz.Baz.v1} {instance.v2}')
+    enddef
+
+    def InAutoload()
+      assert_equal("baz v1 v2", foo.MyTestFoo())
+    enddef
+
+    NotInAutoload()
+    InAutoload()
+  END
+  v9.CheckScriptSuccess(lines)
+
+  &rtp = save_rtp
+enddef
+
 " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
index 29fa07854a5625fa7db49dca5321a0ae5ff98bf7..7ba180e30e5f3d706cb5abd9c7cd82d6c1b92e39 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1119,
 /**/
     1118,
 /**/
index b5e3a13be7f86bd22cccb38bc8f7692d54185242..f0413425f63c63b9bd9cfe8955be92ab94f42855 100644 (file)
@@ -2053,8 +2053,7 @@ early_ret:
     tv.v_type = VAR_CLASS;
     tv.vval.v_class = cl;
     SOURCING_LNUM = start_lnum;
-    int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
-                                               NULL, &tv, FALSE, 0, 0);
+    int rc = set_var_const(cl->class_name, 0, NULL, &tv, FALSE, 0, 0);
     if (rc == FAIL)
        goto cleanup;
 
@@ -2874,7 +2873,7 @@ ex_type(exarg_T *eap)
        tv.vval.v_class = type->tt_class;
        ++tv.vval.v_class->class_refcount;
     }
-    set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE,
+    set_var_const(name_start, 0, NULL, &tv, FALSE,
                                                ASSIGN_CONST | ASSIGN_FINAL, 0);
 
 done:
index c7f0e673b22845c56920e080c1f5828cd874426c..65f6536fdb1466bcff20d741d0cdea20cbb9366a 100644 (file)
@@ -3162,34 +3162,53 @@ object_required_error(typval_T *tv)
 }
 
 /*
- * Accessing the member of an object stored in a variable of type "any".
+ * Accessing the variable or method of an object or a class stored in a
+ * variable of type "any".
  * Returns OK if the member variable is present.
  * Returns FAIL if the variable is not found.
  */
     static int
-any_var_get_obj_member(class_T *current_class, isn_T *iptr, typval_T *tv)
+var_any_get_oc_member(class_T *current_class, isn_T *iptr, typval_T *tv)
 {
-    object_T   *obj = tv->vval.v_object;
+    int                is_object = tv->v_type == VAR_OBJECT;
+    class_T    *tv_cl;
+    object_T   *obj = NULL;
     typval_T   mtv;
 
-    if (obj == NULL)
+    if (is_object)
     {
-       SOURCING_LNUM = iptr->isn_lnum;
-       emsg(_(e_using_null_object));
-       return FAIL;
+       obj = tv->vval.v_object;
+       if (obj == NULL)
+       {
+           SOURCING_LNUM = iptr->isn_lnum;
+           emsg(_(e_using_null_object));
+           return FAIL;
+       }
+       tv_cl = obj->obj_class;
+    }
+    else
+    {
+       tv_cl = tv->vval.v_class;
+       if (tv_cl == NULL)
+       {
+           SOURCING_LNUM = iptr->isn_lnum;
+           emsg(_(e_using_null_class));
+           return FAIL;
+       }
     }
 
-    // get_member_tv() needs the object information in the typval argument.
-    // So set the object information.
+    // get_member_tv() needs the class/object information in the typval
+    // argument.  So set the object information.
     copy_tv(tv, &mtv);
 
-    // 'name' can either be a object variable or a object method
+    // 'name' can either be an instance or class variable or method
     int                namelen = (int)STRLEN(iptr->isn_arg.string);
     int                save_did_emsg = did_emsg;
 
-    if (get_member_tv(obj->obj_class, TRUE, iptr->isn_arg.string, namelen,
+    if (get_member_tv(tv_cl, is_object, iptr->isn_arg.string, namelen,
                                                current_class, &mtv) == OK)
     {
+       // instance or class variable
        copy_tv(&mtv, tv);
        clear_tv(&mtv);
        return OK;
@@ -3198,31 +3217,36 @@ any_var_get_obj_member(class_T *current_class, isn_T *iptr, typval_T *tv)
     if (did_emsg != save_did_emsg)
        return FAIL;
 
-    // could be a member function
-    ufunc_T    *obj_method;
-    int                obj_method_idx;
+    // could be a class or instance method
+    ufunc_T    *oc_method;
+    int                oc_method_idx;
 
-    obj_method = method_lookup(obj->obj_class, VAR_OBJECT,
-                               iptr->isn_arg.string, namelen,
-                               &obj_method_idx);
-    if (obj_method == NULL)
+    oc_method = method_lookup(tv_cl, tv->v_type, iptr->isn_arg.string,
+                                               namelen, &oc_method_idx);
+    if (oc_method == NULL)
     {
+       char    *msg;
+
        SOURCING_LNUM = iptr->isn_lnum;
-       semsg(_(e_variable_not_found_on_object_str_str), iptr->isn_arg.string,
-               obj->obj_class->class_name);
+       if (is_object)
+           msg = e_variable_not_found_on_object_str_str;
+       else
+           msg = e_class_variable_str_not_found_in_class_str;
+       semsg(_(msg), iptr->isn_arg.string, tv_cl->class_name);
        return FAIL;
     }
 
     // Protected methods are not accessible outside the class
-    if (*obj_method->uf_name == '_'
-                       && !class_instance_of(current_class, obj->obj_class))
+    if (*oc_method->uf_name == '_'
+                       && !class_instance_of(current_class, tv_cl))
     {
-       semsg(_(e_cannot_access_protected_method_str), obj_method->uf_name);
+       semsg(_(e_cannot_access_protected_method_str), oc_method->uf_name);
        return FAIL;
     }
 
-    // Create a partial for the member function
-    if (obj_method_to_partial_tv(obj, obj_method, tv) == FAIL)
+    // Create a partial for the instance or class method
+    if (obj_method_to_partial_tv(is_object ? obj : NULL, oc_method, tv)
+                                                               == FAIL)
        return FAIL;
 
     return OK;
@@ -5671,15 +5695,16 @@ exec_instructions(ectx_T *ectx)
 
                    tv = STACK_TV_BOT(-1);
 
-                   if (tv->v_type == VAR_OBJECT)
+                   if (tv->v_type == VAR_OBJECT
+                           || tv->v_type == VAR_CLASS)
                    {
                        if (dict_stack_save(tv) == FAIL)
                            goto on_fatal_error;
 
                        ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data)
                                        + ectx->ec_dfunc_idx)->df_ufunc;
-                       // Class object (not a Dict)
-                       if (any_var_get_obj_member(ufunc->uf_class, iptr, tv) == FAIL)
+                       // Class or an object (not a Dict)
+                       if (var_any_get_oc_member(ufunc->uf_class, iptr, tv) == FAIL)
                            goto on_error;
                    }
                    else