0
0
mirror of https://github.com/vim/vim.git synced 2025-07-25 10:54:51 -04:00

patch 8.2.0201: cannot assign to an imported variable

Problem:    Cannot assign to an imported variable.
Solution:   Make it work.
This commit is contained in:
Bram Moolenaar 2020-02-03 20:50:59 +01:00
parent b283a8a680
commit 4e12a5df37
6 changed files with 136 additions and 82 deletions

View File

@ -2296,7 +2296,7 @@ get_var_tv(
if (tv == NULL && current_sctx.sc_version == SCRIPT_VERSION_VIM9) if (tv == NULL && current_sctx.sc_version == SCRIPT_VERSION_VIM9)
{ {
imported_T *import = find_imported(name, NULL); imported_T *import = find_imported(name, 0, NULL);
// imported variable from another script // imported variable from another script
if (import != NULL) if (import != NULL)
@ -2472,7 +2472,7 @@ lookup_scriptvar(char_u *name, size_t len, cctx_T *dummy UNUSED)
res = HASHITEM_EMPTY(hi) ? -1 : 1; res = HASHITEM_EMPTY(hi) ? -1 : 1;
// if not script-local, then perhaps imported // if not script-local, then perhaps imported
if (res == -1 && find_imported(p, NULL) != NULL) if (res == -1 && find_imported(p, 0, NULL) != NULL)
res = 1; res = 1;
if (p != buffer) if (p != buffer)

View File

@ -4,7 +4,7 @@ type_T *parse_type(char_u **arg, garray_T *type_list);
char *vartype_name(vartype_T type); char *vartype_name(vartype_T type);
char *type_name(type_T *type, char **tofree); char *type_name(type_T *type, char **tofree);
int get_script_item_idx(int sid, char_u *name, int check_writable); int get_script_item_idx(int sid, char_u *name, int check_writable);
imported_T *find_imported(char_u *name, cctx_T *cctx); imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx);
char_u *to_name_end(char_u *arg); char_u *to_name_end(char_u *arg);
char_u *to_name_const_end(char_u *arg); char_u *to_name_const_end(char_u *arg);
int assignment_len(char_u *p, int *heredoc); int assignment_len(char_u *p, int *heredoc);

View File

@ -320,9 +320,11 @@ def Test_import_absolute()
\ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"', \ 'import exported from "' .. escape(getcwd(), '\') .. '/Xexport_abs.vim"',
\ 'def UseExported()', \ 'def UseExported()',
\ ' g:imported_abs = exported', \ ' g:imported_abs = exported',
\ ' exported = 8888',
\ ' g:imported_after = exported',
\ 'enddef', \ 'enddef',
\ 'UseExported()', \ 'UseExported()',
\ 'g:import_disassabled = execute("disass UseExported")', \ 'g:import_disassembled = execute("disass UseExported")',
\ ] \ ]
writefile(import_lines, 'Ximport_abs.vim') writefile(import_lines, 'Ximport_abs.vim')
writefile(s:export_script_lines, 'Xexport_abs.vim') writefile(s:export_script_lines, 'Xexport_abs.vim')
@ -330,12 +332,19 @@ def Test_import_absolute()
source Ximport_abs.vim source Ximport_abs.vim
assert_equal(9876, g:imported_abs) assert_equal(9876, g:imported_abs)
assert_equal(8888, g:imported_after)
assert_match('<SNR>\d\+_UseExported.*' assert_match('<SNR>\d\+_UseExported.*'
\ .. 'g:imported_abs = exported.*' \ .. 'g:imported_abs = exported.*'
\ .. '0 LOADSCRIPT exported from .*Xexport_abs.vim.*' \ .. '0 LOADSCRIPT exported from .*Xexport_abs.vim.*'
\ .. '1 STOREG g:imported_abs', g:import_disassabled) \ .. '1 STOREG g:imported_abs.*'
\ .. 'exported = 8888.*'
\ .. '3 STORESCRIPT exported in .*Xexport_abs.vim.*'
\ .. 'g:imported_after = exported.*'
\ .. '4 LOADSCRIPT exported from .*Xexport_abs.vim.*'
\ .. '5 STOREG g:imported_after.*'
\, g:import_disassembled)
unlet g:imported_abs unlet g:imported_abs
unlet g:import_disassabled unlet g:import_disassembled
delete('Ximport_abs.vim') delete('Ximport_abs.vim')
delete('Xexport_abs.vim') delete('Xexport_abs.vim')

View File

@ -678,7 +678,7 @@ find_func_even_dead(char_u *name, cctx_T *cctx)
return func; return func;
// Find imported funcion before global one. // Find imported funcion before global one.
imported = find_imported(name, cctx); imported = find_imported(name, 0, cctx);
if (imported != NULL && imported->imp_funcname != NULL) if (imported != NULL && imported->imp_funcname != NULL)
{ {
hi = hash_find(&func_hashtab, imported->imp_funcname); hi = hash_find(&func_hashtab, imported->imp_funcname);

View File

@ -742,6 +742,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 */
/**/
201,
/**/ /**/
200, 200,
/**/ /**/

View File

@ -1467,7 +1467,7 @@ get_script_item_idx(int sid, char_u *name, int check_writable)
* Find "name" in imported items of the current script/ * Find "name" in imported items of the current script/
*/ */
imported_T * imported_T *
find_imported(char_u *name, cctx_T *cctx) find_imported(char_u *name, size_t len, cctx_T *cctx)
{ {
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
int idx; int idx;
@ -1478,7 +1478,9 @@ find_imported(char_u *name, cctx_T *cctx)
imported_T *import = ((imported_T *)cctx->ctx_imports.ga_data) imported_T *import = ((imported_T *)cctx->ctx_imports.ga_data)
+ idx; + idx;
if (STRCMP(name, import->imp_name) == 0) if (len == 0 ? STRCMP(name, import->imp_name) == 0
: STRLEN(import->imp_name) == len
&& STRNCMP(name, import->imp_name, len) == 0)
return import; return import;
} }
@ -1486,7 +1488,9 @@ find_imported(char_u *name, cctx_T *cctx)
{ {
imported_T *import = ((imported_T *)si->sn_imports.ga_data) + idx; imported_T *import = ((imported_T *)si->sn_imports.ga_data) + idx;
if (STRCMP(name, import->imp_name) == 0) if (len == 0 ? STRCMP(name, import->imp_name) == 0
: STRLEN(import->imp_name) == len
&& STRNCMP(name, import->imp_name, len) == 0)
return import; return import;
} }
return NULL; return NULL;
@ -1517,7 +1521,7 @@ compile_load_scriptvar(cctx_T *cctx, char_u *name)
return OK; return OK;
} }
import = find_imported(name, cctx); import = find_imported(name, 0, cctx);
if (import != NULL) if (import != NULL)
{ {
// TODO: check this is a variable, not a function // TODO: check this is a variable, not a function
@ -3071,6 +3075,16 @@ heredoc_getline(
[cctx->ctx_lnum]); [cctx->ctx_lnum]);
} }
typedef enum {
dest_local,
dest_option,
dest_env,
dest_global,
dest_vimvar,
dest_script,
dest_reg,
} assign_dest_T;
/* /*
* compile "let var [= expr]", "const var = expr" and "var = expr" * compile "let var [= expr]", "const var = expr" and "var = expr"
* "arg" points to "var". * "arg" points to "var".
@ -3086,14 +3100,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
garray_T *instr = &cctx->ctx_instr; garray_T *instr = &cctx->ctx_instr;
int idx = -1; int idx = -1;
char_u *op; char_u *op;
int option = FALSE;
int opt_type; int opt_type;
assign_dest_T dest = dest_local;
int opt_flags = 0; int opt_flags = 0;
int global = FALSE;
int env = FALSE;
int reg = FALSE;
int vimvaridx = -1; int vimvaridx = -1;
int script = FALSE;
int oplen = 0; int oplen = 0;
int heredoc = FALSE; int heredoc = FALSE;
type_T *type; type_T *type;
@ -3125,7 +3135,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
long numval; long numval;
char_u *stringval = NULL; char_u *stringval = NULL;
option = TRUE; dest = dest_option;
if (cmdidx == CMD_const) if (cmdidx == CMD_const)
{ {
emsg(_(e_const_option)); emsg(_(e_const_option));
@ -3159,7 +3169,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
} }
else if (*arg == '$') else if (*arg == '$')
{ {
env = TRUE; dest = dest_env;
if (is_decl) if (is_decl)
{ {
semsg(_("E1065: Cannot declare an environment variable: %s"), name); semsg(_("E1065: Cannot declare an environment variable: %s"), name);
@ -3173,7 +3183,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
emsg_invreg(arg[1]); emsg_invreg(arg[1]);
return FAIL; return FAIL;
} }
reg = TRUE; dest = dest_reg;
if (is_decl) if (is_decl)
{ {
semsg(_("E1066: Cannot declare a register: %s"), name); semsg(_("E1066: Cannot declare a register: %s"), name);
@ -3182,7 +3192,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
} }
else if (STRNCMP(arg, "g:", 2) == 0) else if (STRNCMP(arg, "g:", 2) == 0)
{ {
global = TRUE; dest = dest_global;
if (is_decl) if (is_decl)
{ {
semsg(_("E1016: Cannot declare a global variable: %s"), name); semsg(_("E1016: Cannot declare a global variable: %s"), name);
@ -3197,6 +3207,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
semsg(_(e_var_notfound), arg); semsg(_(e_var_notfound), arg);
goto theend; goto theend;
} }
dest = dest_vimvar;
if (is_decl) if (is_decl)
{ {
semsg(_("E1064: Cannot declare a v: variable: %s"), name); semsg(_("E1064: Cannot declare a v: variable: %s"), name);
@ -3232,9 +3243,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
} }
else if ((STRNCMP(arg, "s:", 2) == 0 else if ((STRNCMP(arg, "s:", 2) == 0
? lookup_script(arg + 2, varlen - 2) ? lookup_script(arg + 2, varlen - 2)
: lookup_script(arg, varlen)) == OK) : lookup_script(arg, varlen)) == OK
|| find_imported(arg, varlen, cctx) != NULL)
{ {
script = TRUE; dest = dest_script;
if (is_decl) if (is_decl)
{ {
semsg(_("E1054: Variable already declared in the script: %s"), semsg(_("E1054: Variable already declared in the script: %s"),
@ -3244,7 +3256,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
} }
} }
if (!option) if (dest != dest_option)
{ {
if (is_decl && *p == ':') if (is_decl && *p == ':')
{ {
@ -3279,15 +3291,15 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
semsg(_(e_white_both), buf); semsg(_(e_white_both), buf);
} }
if (oplen == 3 && !heredoc && !global && type->tt_type != VAR_STRING if (oplen == 3 && !heredoc && dest != dest_global
&& type->tt_type != VAR_UNKNOWN) && type->tt_type != VAR_STRING && type->tt_type != VAR_UNKNOWN)
{ {
emsg("E1019: Can only concatenate to string"); emsg("E1019: Can only concatenate to string");
goto theend; goto theend;
} }
// +=, /=, etc. require an existing variable // +=, /=, etc. require an existing variable
if (idx < 0 && !global && !env && !reg && !option) if (idx < 0 && dest == dest_local)
{ {
if (oplen > 1 && !heredoc) if (oplen > 1 && !heredoc)
{ {
@ -3328,20 +3340,32 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
// for "+=", "*=", "..=" etc. first load the current value // for "+=", "*=", "..=" etc. first load the current value
if (*op != '=') if (*op != '=')
{ {
if (option) switch (dest)
// TODO: check the option exists {
generate_LOAD(cctx, ISN_LOADOPT, 0, name + 1, type); case dest_option:
else if (global) // TODO: check the option exists
generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type); generate_LOAD(cctx, ISN_LOADOPT, 0, name + 1, type);
else if (env) break;
// Include $ in the name here case dest_global:
generate_LOAD(cctx, ISN_LOADENV, 0, name, type); generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type);
else if (reg) break;
generate_LOAD(cctx, ISN_LOADREG, arg[1], NULL, &t_string); case dest_script:
else if (vimvaridx >= 0) compile_load_scriptvar(cctx, name);
generate_LOADV(cctx, name + 2, TRUE); break;
else case dest_env:
generate_LOAD(cctx, ISN_LOAD, idx, NULL, type); // Include $ in the name here
generate_LOAD(cctx, ISN_LOADENV, 0, name, type);
break;
case dest_reg:
generate_LOAD(cctx, ISN_LOADREG, arg[1], NULL, &t_string);
break;
case dest_vimvar:
generate_LOADV(cctx, name + 2, TRUE);
break;
case dest_local:
generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
break;
}
} }
// compile the expression // compile the expression
@ -3377,7 +3401,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
emsg(_("E1021: const requires a value")); emsg(_("E1021: const requires a value"));
goto theend; goto theend;
} }
else if (!has_type || option) else if (!has_type || dest == dest_option)
{ {
emsg(_("E1022: type or initialization required")); emsg(_("E1022: type or initialization required"));
goto theend; goto theend;
@ -3427,49 +3451,68 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
} }
} }
if (option) switch (dest)
generate_STOREOPT(cctx, name + 1, opt_flags);
else if (global)
// include g: with the name, easier to execute that way
generate_STORE(cctx, ISN_STOREG, 0, name);
else if (env)
generate_STORE(cctx, ISN_STOREENV, 0, name + 1);
else if (reg)
generate_STORE(cctx, ISN_STOREREG, name[1], NULL);
else if (vimvaridx >= 0)
generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
else if (script)
{ {
char_u *rawname = name + (name[1] == ':' ? 2 : 0); case dest_option:
generate_STOREOPT(cctx, name + 1, opt_flags);
break;
case dest_global:
// include g: with the name, easier to execute that way
generate_STORE(cctx, ISN_STOREG, 0, name);
break;
case dest_env:
generate_STORE(cctx, ISN_STOREENV, 0, name + 1);
break;
case dest_reg:
generate_STORE(cctx, ISN_STOREREG, name[1], NULL);
break;
case dest_vimvar:
generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
break;
case dest_script:
{
char_u *rawname = name + (name[1] == ':' ? 2 : 0);
imported_T *import = NULL;
int sid = current_sctx.sc_sid;
idx = get_script_item_idx(current_sctx.sc_sid, rawname, TRUE); if (name[1] != ':')
// TODO: specific type {
if (idx < 0) import = find_imported(name, 0, cctx);
generate_OLDSCRIPT(cctx, ISN_STORES, rawname, if (import != NULL)
current_sctx.sc_sid, &t_any); sid = import->imp_sid;
else }
generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
current_sctx.sc_sid, idx, &t_any);
}
else
{
isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
// optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE into idx = get_script_item_idx(sid, rawname, TRUE);
// ISN_STORENR // TODO: specific type
if (instr->ga_len == instr_count + 1 && isn->isn_type == ISN_PUSHNR) if (idx < 0)
{ generate_OLDSCRIPT(cctx, ISN_STORES, rawname, sid, &t_any);
varnumber_T val = isn->isn_arg.number; else
garray_T *stack = &cctx->ctx_type_stack; generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
sid, idx, &t_any);
}
break;
case dest_local:
{
isn_T *isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
isn->isn_type = ISN_STORENR; // optimization: turn "var = 123" from ISN_PUSHNR + ISN_STORE
isn->isn_arg.storenr.str_idx = idx; // into ISN_STORENR
isn->isn_arg.storenr.str_val = val; if (instr->ga_len == instr_count + 1
if (stack->ga_len > 0) && isn->isn_type == ISN_PUSHNR)
--stack->ga_len; {
} varnumber_T val = isn->isn_arg.number;
else garray_T *stack = &cctx->ctx_type_stack;
generate_STORE(cctx, ISN_STORE, idx, NULL);
isn->isn_type = ISN_STORENR;
isn->isn_arg.storenr.str_idx = idx;
isn->isn_arg.storenr.str_val = val;
if (stack->ga_len > 0)
--stack->ga_len;
}
else
generate_STORE(cctx, ISN_STORE, idx, NULL);
}
break;
} }
ret = p; ret = p;
@ -4619,7 +4662,6 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
// "funcname(" is always a function call. // "funcname(" is always a function call.
// "varname[]" is an expression. // "varname[]" is an expression.
// "g:varname" is an expression.
// "varname->expr" is an expression. // "varname->expr" is an expression.
if (*p == '(' if (*p == '('
|| *p == '[' || *p == '['
@ -4643,7 +4685,8 @@ compile_def_function(ufunc_T *ufunc, int set_return_type)
|| *ea.cmd == '@' || *ea.cmd == '@'
|| ((p - ea.cmd) > 2 && ea.cmd[1] == ':') || ((p - ea.cmd) > 2 && ea.cmd[1] == ':')
|| lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0 || lookup_local(ea.cmd, p - ea.cmd, &cctx) >= 0
|| lookup_script(ea.cmd, p - ea.cmd) == OK) || lookup_script(ea.cmd, p - ea.cmd) == OK
|| find_imported(ea.cmd, p - ea.cmd, &cctx) != NULL)
{ {
line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx); line = compile_assignment(ea.cmd, &ea, CMD_SIZE, &cctx);
if (line == NULL) if (line == NULL)