]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.4332: Vim9: incomplete test for existing script variable in block v8.2.4332
authorBram Moolenaar <Bram@vim.org>
Tue, 8 Feb 2022 20:35:30 +0000 (20:35 +0000)
committerBram Moolenaar <Bram@vim.org>
Tue, 8 Feb 2022 20:35:30 +0000 (20:35 +0000)
Problem:    Vim9: incomplete test for existing script variable in block.
Solution:   Add a couple more tests.  Fix uncovered problem.

src/proto/vim9compile.pro
src/testdir/test_vim9_func.vim
src/userfunc.c
src/version.c
src/vim9compile.c
src/vim9expr.c
src/vim9script.c

index b7f7539215b91fb32f06faa0e9d73855a5139628..0790decb8a821ecfe7dddee8d5c4091934033bcb 100644 (file)
@@ -2,8 +2,8 @@
 int lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx);
 int arg_exists(char_u *name, size_t len, int *idxp, type_T **type, int *gen_load_outer, cctx_T *cctx);
 int script_is_vim9(void);
-int script_var_exists(char_u *name, size_t len, cctx_T *cctx);
-int check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg);
+int script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack);
+int check_defined(char_u *p, size_t len, cctx_T *cctx, cstack_T *cstack, int is_arg);
 int need_type_where(type_T *actual, type_T *expected, int offset, where_T where, cctx_T *cctx, int silent, int actual_is_const);
 int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const);
 lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type);
index e68cbcf152d68c35db4d21ec7e334fc1cbc50991..46e562d1ed42920cc0ac948a3e399c90b741caf8 100644 (file)
@@ -1057,6 +1057,43 @@ def Test_call_wrong_args()
   END
   v9.CheckScriptSuccess(lines)
 
+  # with another variable in another block
+  lines =<< trim END
+    vim9script
+    if true
+      var name = 'piet'
+      # define a function so that the variable isn't cleared
+      def GetItem(): string
+        return item
+      enddef
+    endif
+    if true
+      var name = 'peter'
+      def FuncOne(name: string)
+        echo name
+      enddef
+    endif
+  END
+  v9.CheckScriptFailure(lines, 'E1168:')
+
+  # only variable in another block is OK
+  lines =<< trim END
+    vim9script
+    if true
+      var name = 'piet'
+      # define a function so that the variable isn't cleared
+      def GetItem(): string
+        return item
+      enddef
+    endif
+    if true
+      def FuncOne(name: string)
+        echo name
+      enddef
+    endif
+  END
+  v9.CheckScriptSuccess(lines)
+
   # argument name declared later is only found when compiling
   lines =<< trim END
     vim9script
index 0c54e35749473c56387402877b8c266ef22199b3..59415dbd731ebda2cc1cf2b0fd8a61693c56c510 100644 (file)
@@ -55,6 +55,7 @@ func_tbl_get(void)
  * If "argtypes" is not NULL also get the type: "arg: type" (:def function).
  * If "types_optional" is TRUE a missing type is OK, use "any".
  * If "evalarg" is not NULL use it to check for an already declared name.
+ * If "eap" is not NULL use it to check for an already declared name.
  * Return a pointer to after the type.
  * When something is wrong return "arg".
  */
@@ -65,6 +66,7 @@ one_function_arg(
        garray_T    *argtypes,
        int         types_optional,
        evalarg_T   *evalarg,
+       exarg_T     *eap,
        int         is_vararg,
        int         skip)
 {
@@ -87,7 +89,8 @@ one_function_arg(
     // Vim9 script: cannot use script var name for argument. In function: also
     // check local vars and arguments.
     if (!skip && argtypes != NULL && check_defined(arg, p - arg,
-                   evalarg == NULL ? NULL : evalarg->eval_cctx, TRUE) == FAIL)
+                              evalarg == NULL ? NULL : evalarg->eval_cctx,
+                              eap == NULL ? NULL : eap->cstack, TRUE) == FAIL)
        return arg;
 
     if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
@@ -210,7 +213,7 @@ get_function_args(
     int                *varargs,
     garray_T   *default_args,
     int                skip,
-    exarg_T    *eap,
+    exarg_T    *eap,           // can be NULL
     garray_T   *lines_to_free)
 {
     int                mustend = FALSE;
@@ -279,7 +282,7 @@ get_function_args(
 
                arg = p;
                p = one_function_arg(p, newargs, argtypes, types_optional,
-                                                         evalarg, TRUE, skip);
+                                                    evalarg, eap, TRUE, skip);
                if (p == arg)
                    break;
                if (*skipwhite(p) == '=')
@@ -295,7 +298,7 @@ get_function_args(
 
            arg = p;
            p = one_function_arg(p, newargs, argtypes, types_optional,
-                                                        evalarg, FALSE, skip);
+                                                   evalarg, eap, FALSE, skip);
            if (p == arg)
                break;
 
index e0bb6e14bfdc5f25298a6de8c5527f9faa1202ca..9f3dfd2f6560a9c583f49d9cf19d191310b36477 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4332,
 /**/
     4331,
 /**/
index debead41814b9987828c31a1e4746bd4f16e293c..5f80988b80ab483ffbb4c912061f2166c4b71673 100644 (file)
@@ -152,11 +152,12 @@ arg_exists(
  * Lookup a script-local variable in the current script, possibly defined in a
  * block that contains the function "cctx->ctx_ufunc".
  * "cctx" is NULL at the script level.
+ * "cstack_T" is NULL in a function.
  * If "len" is <= 0 "name" must be NUL terminated.
  * Return NULL when not found.
  */
     static sallvar_T *
-find_script_var(char_u *name, size_t len, cctx_T *cctx)
+find_script_var(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack)
 {
     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
     hashitem_T     *hi;
@@ -183,11 +184,22 @@ find_script_var(char_u *name, size_t len, cctx_T *cctx)
 
     if (cctx == NULL)
     {
-       // Not in a function scope, find variable with block id equal to or
-       // smaller than the current block id.
+       // Not in a function scope, find variable with block ID equal to or
+       // smaller than the current block id.  If "cstack" is not NULL go up
+       // the block scopes (more accurate).
        while (sav != NULL)
        {
-           if (sav->sav_block_id <= si->sn_current_block_id)
+           if (cstack != NULL)
+           {
+               int idx;
+
+               for (idx = cstack->cs_idx; idx >= 0; --idx)
+                   if (cstack->cs_block_id[idx] == sav->sav_block_id)
+                       break;
+               if (idx >= 0)
+                   break;
+           }
+           else if (sav->sav_block_id <= si->sn_current_block_id)
                break;
            sav = sav->sav_next;
        }
@@ -225,10 +237,11 @@ script_is_vim9()
 /*
  * Lookup a variable (without s: prefix) in the current script.
  * "cctx" is NULL at the script level.
+ * "cstack" is NULL in a function.
  * Returns OK or FAIL.
  */
     int
-script_var_exists(char_u *name, size_t len, cctx_T *cctx)
+script_var_exists(char_u *name, size_t len, cctx_T *cctx, cstack_T *cstack)
 {
     if (current_sctx.sc_sid <= 0)
        return FAIL;
@@ -236,7 +249,7 @@ script_var_exists(char_u *name, size_t len, cctx_T *cctx)
     {
        // Check script variables that were visible where the function was
        // defined.
-       if (find_script_var(name, len, cctx) != NULL)
+       if (find_script_var(name, len, cctx, cstack) != NULL)
            return OK;
     }
     else
@@ -267,7 +280,7 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
     return (cctx != NULL
                && (lookup_local(name, len, NULL, cctx) == OK
                    || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK))
-           || script_var_exists(name, len, cctx) == OK
+           || script_var_exists(name, len, cctx, NULL) == OK
            || find_imported(name, len, FALSE, cctx) != NULL;
 }
 
@@ -309,7 +322,12 @@ item_exists(char_u *name, size_t len, int cmd UNUSED, cctx_T *cctx)
  * Return FAIL and give an error if it defined.
  */
     int
-check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
+check_defined(
+       char_u      *p,
+       size_t      len,
+       cctx_T      *cctx,
+       cstack_T    *cstack,
+       int         is_arg)
 {
     int                c = p[len];
     ufunc_T    *ufunc = NULL;
@@ -318,7 +336,7 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
     if (len == 1 && *p == '_')
        return OK;
 
-    if (script_var_exists(p, len, cctx) == OK)
+    if (script_var_exists(p, len, cctx, cstack) == OK)
     {
        if (is_arg)
            semsg(_(e_argument_already_declared_in_script_str), p);
@@ -526,7 +544,7 @@ get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx)
        return -1;
     if (sid == current_sctx.sc_sid)
     {
-       sallvar_T *sav = find_script_var(name, 0, cctx);
+       sallvar_T *sav = find_script_var(name, 0, cctx, NULL);
 
        if (sav == NULL)
            return -2;
@@ -884,7 +902,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
        semsg(_(e_namespace_not_supported_str), name_start);
        return NULL;
     }
-    if (check_defined(name_start, name_end - name_start, cctx, FALSE) == FAIL)
+    if (check_defined(name_start, name_end - name_start, cctx,
+                                                         NULL, FALSE) == FAIL)
        return NULL;
     if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0]))
     {
@@ -1356,9 +1375,9 @@ compile_lhs(
                                       && STRNCMP(var_start, "s:", 2) == 0;
                int script_var = (script_namespace
                        ? script_var_exists(var_start + 2, lhs->lhs_varlen - 2,
-                                                                         cctx)
+                                                                   cctx, NULL)
                          : script_var_exists(var_start, lhs->lhs_varlen,
-                                                                 cctx)) == OK;
+                                                           cctx, NULL)) == OK;
                imported_T  *import =
                        find_imported(var_start, lhs->lhs_varlen, FALSE, cctx);
 
@@ -1442,8 +1461,8 @@ compile_lhs(
                        }
                    }
                }
-               else if (check_defined(var_start, lhs->lhs_varlen, cctx, FALSE)
-                                                                      == FAIL)
+               else if (check_defined(var_start, lhs->lhs_varlen, cctx,
+                                                         NULL, FALSE) == FAIL)
                    return FAIL;
            }
        }
@@ -2470,7 +2489,7 @@ check_args_shadowing(ufunc_T *ufunc, cctx_T *cctx)
     for (i = 0; i < ufunc->uf_args.ga_len; ++i)
     {
        arg = ((char_u **)(ufunc->uf_args.ga_data))[i];
-       if (check_defined(arg, STRLEN(arg), cctx, TRUE) == FAIL)
+       if (check_defined(arg, STRLEN(arg), cctx, NULL, TRUE) == FAIL)
        {
            r = FAIL;
            break;
index 402803768befc5456e437c7e0485e97f42f1acc1..b44f9288d926838bf8a4dfc5dd352eb3f1709550 100644 (file)
@@ -501,7 +501,7 @@ compile_load(
            {
                // "var" can be script-local even without using "s:" if it
                // already exists in a Vim9 script or when it's imported.
-               if (script_var_exists(*arg, len, cctx) == OK
+               if (script_var_exists(*arg, len, cctx, NULL) == OK
                        || find_imported(name, 0, FALSE, cctx) != NULL)
                   res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);
 
index 872108827e8fc0f089d06cecc500591f12de4420..1b8988d55ff9d9accb3b539efa2306ec29716ef0 100644 (file)
@@ -600,7 +600,8 @@ handle_import(
            goto erret;
        }
        else if (imported == NULL
-               && check_defined(as_name, STRLEN(as_name), cctx, FALSE) == FAIL)
+               && check_defined(as_name, STRLEN(as_name), cctx, NULL,
+                                                               FALSE) == FAIL)
            goto erret;
 
        if (imported == NULL)