0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 8.2.4332: Vim9: incomplete test for existing script variable in block

Problem:    Vim9: incomplete test for existing script variable in block.
Solution:   Add a couple more tests.  Fix uncovered problem.
This commit is contained in:
Bram Moolenaar
2022-02-08 20:35:30 +00:00
parent 3a5988c025
commit dce2441a60
7 changed files with 86 additions and 24 deletions

View File

@@ -2,8 +2,8 @@
int lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx); 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 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_is_vim9(void);
int script_var_exists(char_u *name, size_t len, cctx_T *cctx); 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, int is_arg); 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_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); 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); lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type);

View File

@@ -1057,6 +1057,43 @@ def Test_call_wrong_args()
END END
v9.CheckScriptSuccess(lines) 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 # argument name declared later is only found when compiling
lines =<< trim END lines =<< trim END
vim9script vim9script

View File

@@ -55,6 +55,7 @@ func_tbl_get(void)
* If "argtypes" is not NULL also get the type: "arg: type" (:def function). * 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 "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 "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. * Return a pointer to after the type.
* When something is wrong return "arg". * When something is wrong return "arg".
*/ */
@@ -65,6 +66,7 @@ one_function_arg(
garray_T *argtypes, garray_T *argtypes,
int types_optional, int types_optional,
evalarg_T *evalarg, evalarg_T *evalarg,
exarg_T *eap,
int is_vararg, int is_vararg,
int skip) int skip)
{ {
@@ -87,7 +89,8 @@ one_function_arg(
// Vim9 script: cannot use script var name for argument. In function: also // Vim9 script: cannot use script var name for argument. In function: also
// check local vars and arguments. // check local vars and arguments.
if (!skip && argtypes != NULL && check_defined(arg, p - arg, 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; return arg;
if (newargs != NULL && ga_grow(newargs, 1) == FAIL) if (newargs != NULL && ga_grow(newargs, 1) == FAIL)
@@ -210,7 +213,7 @@ get_function_args(
int *varargs, int *varargs,
garray_T *default_args, garray_T *default_args,
int skip, int skip,
exarg_T *eap, exarg_T *eap, // can be NULL
garray_T *lines_to_free) garray_T *lines_to_free)
{ {
int mustend = FALSE; int mustend = FALSE;
@@ -279,7 +282,7 @@ get_function_args(
arg = p; arg = p;
p = one_function_arg(p, newargs, argtypes, types_optional, p = one_function_arg(p, newargs, argtypes, types_optional,
evalarg, TRUE, skip); evalarg, eap, TRUE, skip);
if (p == arg) if (p == arg)
break; break;
if (*skipwhite(p) == '=') if (*skipwhite(p) == '=')
@@ -295,7 +298,7 @@ get_function_args(
arg = p; arg = p;
p = one_function_arg(p, newargs, argtypes, types_optional, p = one_function_arg(p, newargs, argtypes, types_optional,
evalarg, FALSE, skip); evalarg, eap, FALSE, skip);
if (p == arg) if (p == arg)
break; break;

View File

@@ -746,6 +746,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
4332,
/**/ /**/
4331, 4331,
/**/ /**/

View File

@@ -152,11 +152,12 @@ arg_exists(
* Lookup a script-local variable in the current script, possibly defined in a * Lookup a script-local variable in the current script, possibly defined in a
* block that contains the function "cctx->ctx_ufunc". * block that contains the function "cctx->ctx_ufunc".
* "cctx" is NULL at the script level. * "cctx" is NULL at the script level.
* "cstack_T" is NULL in a function.
* If "len" is <= 0 "name" must be NUL terminated. * If "len" is <= 0 "name" must be NUL terminated.
* Return NULL when not found. * Return NULL when not found.
*/ */
static sallvar_T * 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); scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
hashitem_T *hi; hashitem_T *hi;
@@ -183,11 +184,22 @@ find_script_var(char_u *name, size_t len, cctx_T *cctx)
if (cctx == NULL) if (cctx == NULL)
{ {
// Not in a function scope, find variable with block id equal to or // Not in a function scope, find variable with block ID equal to or
// smaller than the current block id. // smaller than the current block id. If "cstack" is not NULL go up
// the block scopes (more accurate).
while (sav != NULL) 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; break;
sav = sav->sav_next; sav = sav->sav_next;
} }
@@ -225,10 +237,11 @@ script_is_vim9()
/* /*
* Lookup a variable (without s: prefix) in the current script. * Lookup a variable (without s: prefix) in the current script.
* "cctx" is NULL at the script level. * "cctx" is NULL at the script level.
* "cstack" is NULL in a function.
* Returns OK or FAIL. * Returns OK or FAIL.
*/ */
int 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) if (current_sctx.sc_sid <= 0)
return FAIL; 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 // Check script variables that were visible where the function was
// defined. // defined.
if (find_script_var(name, len, cctx) != NULL) if (find_script_var(name, len, cctx, cstack) != NULL)
return OK; return OK;
} }
else else
@@ -267,7 +280,7 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
return (cctx != NULL return (cctx != NULL
&& (lookup_local(name, len, NULL, cctx) == OK && (lookup_local(name, len, NULL, cctx) == OK
|| arg_exists(name, len, NULL, NULL, 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; || 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. * Return FAIL and give an error if it defined.
*/ */
int 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]; int c = p[len];
ufunc_T *ufunc = NULL; 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 == '_') if (len == 1 && *p == '_')
return OK; return OK;
if (script_var_exists(p, len, cctx) == OK) if (script_var_exists(p, len, cctx, cstack) == OK)
{ {
if (is_arg) if (is_arg)
semsg(_(e_argument_already_declared_in_script_str), p); 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; return -1;
if (sid == current_sctx.sc_sid) 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) if (sav == NULL)
return -2; 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); semsg(_(e_namespace_not_supported_str), name_start);
return NULL; 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; return NULL;
if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0])) if (!ASCII_ISUPPER(is_global ? name_start[2] : name_start[0]))
{ {
@@ -1356,9 +1375,9 @@ compile_lhs(
&& STRNCMP(var_start, "s:", 2) == 0; && STRNCMP(var_start, "s:", 2) == 0;
int script_var = (script_namespace int script_var = (script_namespace
? script_var_exists(var_start + 2, lhs->lhs_varlen - 2, ? script_var_exists(var_start + 2, lhs->lhs_varlen - 2,
cctx) cctx, NULL)
: script_var_exists(var_start, lhs->lhs_varlen, : script_var_exists(var_start, lhs->lhs_varlen,
cctx)) == OK; cctx, NULL)) == OK;
imported_T *import = imported_T *import =
find_imported(var_start, lhs->lhs_varlen, FALSE, cctx); 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) else if (check_defined(var_start, lhs->lhs_varlen, cctx,
== FAIL) NULL, FALSE) == FAIL)
return 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) for (i = 0; i < ufunc->uf_args.ga_len; ++i)
{ {
arg = ((char_u **)(ufunc->uf_args.ga_data))[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; r = FAIL;
break; break;

View File

@@ -501,7 +501,7 @@ compile_load(
{ {
// "var" can be script-local even without using "s:" if it // "var" can be script-local even without using "s:" if it
// already exists in a Vim9 script or when it's imported. // 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) || find_imported(name, 0, FALSE, cctx) != NULL)
res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE);

View File

@@ -600,7 +600,8 @@ handle_import(
goto erret; goto erret;
} }
else if (imported == NULL 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; goto erret;
if (imported == NULL) if (imported == NULL)