patch 9.1.0257: Vim9: :call may not find imported class members

Commit: 
https://github.com/vim/vim/commit/f1750ca0c2ef91c6e4857655ca8fdf8dd8f5abb8
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Tue Apr 2 20:41:04 2024 +0200

    patch 9.1.0257: Vim9: :call may not find imported class members
    
    Problem:  Vim9: :call may not find imported class members
              (mityu)
    Solution: Set the typval of an imported lval variable correctly
              (Yegappan Lakshmanan)
    
    fixes: #14334
    closes: #14386
    
    Signed-off-by: Yegappan Lakshmanan <yegap...@yahoo.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/eval.c b/src/eval.c
index 2647e7a59..a4fde0ef2 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1145,6 +1145,91 @@ get_lval_check_access(
     return OK;
 }
 
+/*
+ * Get lval information for a variable imported from script "imp_sid".  On
+ * success, updates "lp" with the variable name, type, script ID and typval.
+ * The variable name starts at or after "p".
+ * If "rettv" is not NULL it points to the value to be assigned.  This used to
+ * match the rhs and lhs types.
+ * Returns a pointer to the character after the variable name if the imported
+ * variable is valid and writable.
+ * Returns NULL if the variable is not exported or typval is not found or the
+ * rhs type doesn't match the lhs type or the variable is not writable.
+ */
+    static char_u *
+get_lval_imported(
+    lval_T     *lp,
+    typval_T   *rettv,
+    scid_T     imp_sid,
+    char_u     *p,
+    dictitem_T **dip,
+    int                fne_flags,
+    int                vim9script)
+{
+    ufunc_T    *ufunc;
+    type_T     *type = NULL;
+    int                cc;
+    int                rc = FAIL;
+
+    p = skipwhite(p);
+
+    import_check_sourced_sid(&imp_sid);
+    lp->ll_sid = imp_sid;
+    lp->ll_name = p;
+    p = find_name_end(lp->ll_name, NULL, NULL, fne_flags);
+    lp->ll_name_end = p;
+
+    // check the item is exported
+    cc = *p;
+    *p = NUL;
+    if (find_exported(imp_sid, lp->ll_name, &ufunc, &type, NULL, NULL,
+                                                               TRUE) == -1)
+       goto failed;
+
+    if (vim9script && type != NULL)
+    {
+       where_T     where = WHERE_INIT;
+
+       // In a vim9 script, do type check and make sure the variable is
+       // writable.
+       if (check_typval_type(type, rettv, where) == FAIL)
+           goto failed;
+    }
+
+    // Get the typval for the exported item
+    hashtab_T *ht = &SCRIPT_VARS(imp_sid);
+    if (ht == NULL)
+       goto failed;
+
+    dictitem_T *di = find_var_in_ht(ht, 0, lp->ll_name, TRUE);
+    if (di == NULL)
+       // variable is not found
+       goto success;
+
+    *dip = di;
+
+    // Check whether the variable is writable.
+    svar_T *sv = find_typval_in_script(&di->di_tv, imp_sid, FALSE);
+    if (sv != NULL && sv->sv_const != 0)
+    {
+       semsg(_(e_cannot_change_readonly_variable_str), lp->ll_name);
+       goto failed;
+    }
+
+    // check whether variable is locked
+    if (value_check_lock(di->di_tv.v_lock, lp->ll_name, FALSE))
+       goto failed;
+
+    lp->ll_tv = &di->di_tv;
+
+success:
+    rc = OK;
+
+failed:
+    *p = cc;
+    return rc == OK ? p : NULL;
+}
+
 /*
  * Get an lval: variable, Dict item or List item that can be assigned a value
  * to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
@@ -1177,7 +1262,7 @@ get_lval(
     char_u     *p;
     char_u     *expr_start, *expr_end;
     int                cc;
-    dictitem_T *v;
+    dictitem_T *v = NULL;
     typval_T   var1;
     typval_T   var2;
     int                empty1 = FALSE;
@@ -1311,28 +1396,13 @@ get_lval(
     if (*p == '.')
     {
        imported_T *import = find_imported(lp->ll_name, p - lp->ll_name, TRUE);
-
        if (import != NULL)
        {
-           ufunc_T *ufunc;
-           type_T *type;
-
-           import_check_sourced_sid(&import->imp_sid);
-           lp->ll_sid = import->imp_sid;
-           lp->ll_name = skipwhite(p + 1);
-           p = find_name_end(lp->ll_name, NULL, NULL, fne_flags);
-           lp->ll_name_end = p;
-
-           // check the item is exported
-           cc = *p;
-           *p = NUL;
-           if (find_exported(import->imp_sid, lp->ll_name, &ufunc, &type,
-                                                      NULL, NULL, TRUE) == -1)
-           {
-               *p = cc;
+           p++;        // skip '.'
+           p = get_lval_imported(lp, rettv, import->imp_sid, p, &v,
+                                               fne_flags, vim9script);
+           if (p == NULL)
                return NULL;
-           }
-           *p = cc;
        }
     }
 
@@ -1352,7 +1422,7 @@ get_lval(
        lp->ll_tv = lval_root->lr_tv;
        v = NULL;
     }
-    else
+    else if (lp->ll_tv == NULL)
     {
        cc = *p;
        *p = NUL;
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index cf900f763..93481d55b 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -3121,6 +3121,28 @@ def Test_class_import()
   v9.CheckScriptSuccess(lines)
 enddef
 
+" Test for importing a class into a legacy script and calling the class method
+def Test_class_method_from_legacy_script()
+  var lines =<< trim END
+    vim9script
+    export class A
+      static var name: string = 'a'
+      static def SetName(n: string)
+        name = n
+      enddef
+    endclass
+  END
+  writefile(lines, 'Xvim9export.vim', 'D')
+
+  lines =<< trim END
+    import './Xvim9export.vim' as vim9
+
+    call s:vim9.A.SetName('b')
+    call assert_equal('b', s:vim9.A.name)
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " Test for implementing an imported interface
 def Test_implement_imported_interface()
   var lines =<< trim END
@@ -3220,6 +3242,23 @@ def Test_abstract_class()
     endclass
   END
   v9.CheckSourceFailure(lines, 'E1359: Cannot define a "new" method in an 
abstract class', 4)
+
+  # extending an abstract class with class methods and variables
+  lines =<< trim END
+    vim9script
+    abstract class A
+      static var s: string = 'vim'
+      static def Fn(): list<number>
+        return [10]
+      enddef
+    endclass
+    class B extends A
+    endclass
+    var b = B.new()
+    assert_equal('vim', A.s)
+    assert_equal([10], A.Fn())
+  END
+  v9.CheckScriptSuccess(lines)
 enddef
 
 def Test_closure_in_class()
diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim
index fa1aeb10e..8d68f1e47 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -2054,6 +2054,13 @@ def Test_import_vim9_from_legacy()
     export def GetText(): string
        return 'text'
     enddef
+    export var exported_nr: number = 22
+    def AddNum(n: number)
+      exported_nr += n
+    enddef
+    export var exportedDict: dict<func> = {Fn: AddNum}
+    export const CONST = 10
+    export final finalVar = 'abc'
   END
   writefile(vim9_lines, 'Xvim9_export.vim', 'D')
 
@@ -2072,6 +2079,13 @@ def Test_import_vim9_from_legacy()
     " imported symbol is script-local
     call assert_equal('exported', s:vim9.exported)
     call assert_equal('text', s:vim9.GetText())
+    call s:vim9.exportedDict.Fn(5)
+    call assert_equal(27, s:vim9.exported_nr)
+    call call(s:vim9.exportedDict.Fn, [3])
+    call assert_equal(30, s:vim9.exported_nr)
+    call assert_fails('let s:vim9.CONST = 20', 'E46: Cannot change read-only 
variable "CONST"')
+    call assert_fails('let s:vim9.finalVar = ""', 'E46: Cannot change 
read-only variable "finalVar"')
+    call assert_fails('let s:vim9.non_existing_var = 20', 'E1048: Item not 
found in script: non_existing_var')
   END
   writefile(legacy_lines, 'Xlegacy_script.vim', 'D')
 
diff --git a/src/version.c b/src/version.c
index 3d0474aad..0e5492626 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    257,
 /**/
     256,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1rrjMa-004OYK-Cm%40256bit.org.

Raspunde prin e-mail lui