mirror of
https://github.com/vim/vim.git
synced 2025-09-27 04:14:06 -04:00
patch 9.1.0943: Vim9: vim9compile.c can be further improved
Problem: vim9compile.c can be further improved Solution: Refactor the compile_lhs function (Yegappan Lakshmanan) closes: #16245 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
80d72aa547
commit
d0186c54c2
@@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
943,
|
||||
/**/
|
||||
942,
|
||||
/**/
|
||||
|
@@ -1643,30 +1643,32 @@ lhs_class_member_modifiable(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out the LHS type and other properties for an assignment or one item
|
||||
* of ":unlet" with an index.
|
||||
* Returns OK or FAIL.
|
||||
* Initialize "lhs" with default values
|
||||
*/
|
||||
int
|
||||
compile_lhs(
|
||||
char_u *var_start,
|
||||
lhs_T *lhs,
|
||||
cmdidx_T cmdidx,
|
||||
int heredoc,
|
||||
int has_cmd, // "var" before "var_start"
|
||||
int oplen,
|
||||
cctx_T *cctx)
|
||||
static void
|
||||
lhs_init_defaults(lhs_T *lhs)
|
||||
{
|
||||
char_u *var_end;
|
||||
int is_decl = is_decl_command(cmdidx);
|
||||
|
||||
CLEAR_POINTER(lhs);
|
||||
lhs->lhs_dest = dest_local;
|
||||
lhs->lhs_vimvaridx = -1;
|
||||
lhs->lhs_scriptvar_idx = -1;
|
||||
lhs->lhs_member_idx = -1;
|
||||
}
|
||||
|
||||
// "dest_end" is the end of the destination, including "[expr]" or
|
||||
/*
|
||||
* When compiling a LHS variable name, find the end of the destination and the
|
||||
* end of the variable name.
|
||||
*/
|
||||
static int
|
||||
lhs_find_var_end(
|
||||
lhs_T *lhs,
|
||||
char_u *var_start,
|
||||
int is_decl,
|
||||
char_u **var_endp)
|
||||
{
|
||||
char_u *var_end = *var_endp;
|
||||
|
||||
// "lhs_dest_end" is the end of the destination, including "[expr]" or
|
||||
// ".name".
|
||||
// "var_end" is the end of the variable/option/etc. name.
|
||||
lhs->lhs_dest_end = skip_var_one(var_start, FALSE);
|
||||
@@ -1689,7 +1691,31 @@ compile_lhs(
|
||||
--lhs->lhs_dest_end;
|
||||
if (is_decl && var_end == var_start + 2 && var_end[-1] == ':')
|
||||
--var_end;
|
||||
|
||||
lhs->lhs_end = lhs->lhs_dest_end;
|
||||
*var_endp = var_end;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set various fields in "lhs"
|
||||
*/
|
||||
static int
|
||||
lhs_init(
|
||||
lhs_T *lhs,
|
||||
char_u *var_start,
|
||||
int is_decl,
|
||||
int heredoc,
|
||||
char_u **var_endp)
|
||||
{
|
||||
char_u *var_end = *var_endp;
|
||||
|
||||
lhs_init_defaults(lhs);
|
||||
|
||||
// Find the end of the variable and the destination
|
||||
if (lhs_find_var_end(lhs, var_start, is_decl, &var_end) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
// compute the length of the destination without "[expr]" or ".name"
|
||||
lhs->lhs_varlen = var_end - var_start;
|
||||
@@ -1702,12 +1728,211 @@ compile_lhs(
|
||||
// Something follows after the variable: "var[idx]" or "var.key".
|
||||
lhs->lhs_has_index = TRUE;
|
||||
|
||||
if (heredoc)
|
||||
lhs->lhs_type = &t_list_string;
|
||||
else
|
||||
lhs->lhs_type = &t_any;
|
||||
lhs->lhs_type = heredoc ? &t_list_string : &t_any;
|
||||
|
||||
if (cctx->ctx_skip != SKIP_YES)
|
||||
*var_endp = var_end;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile a LHS class variable name.
|
||||
*/
|
||||
static int
|
||||
compile_lhs_class_variable(
|
||||
cctx_T *cctx,
|
||||
lhs_T *lhs,
|
||||
class_T *defcl,
|
||||
int is_decl)
|
||||
{
|
||||
if (cctx->ctx_ufunc->uf_defclass != defcl)
|
||||
{
|
||||
// A class variable can be accessed without the class name
|
||||
// only inside a class.
|
||||
semsg(_(e_class_variable_str_accessible_only_inside_class_str),
|
||||
lhs->lhs_name, defcl->class_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (is_decl)
|
||||
{
|
||||
semsg(_(e_variable_already_declared_in_class_str), lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
ocmember_T *m = &defcl->class_class_members[lhs->lhs_classmember_idx];
|
||||
if (oc_var_check_ro(defcl, m))
|
||||
return FAIL;
|
||||
|
||||
lhs->lhs_dest = dest_class_member;
|
||||
// The class variable is defined either in the current class or
|
||||
// in one of the parent class in the hierarchy.
|
||||
lhs->lhs_class = defcl;
|
||||
lhs->lhs_type = oc_member_type_by_idx(defcl, FALSE,
|
||||
lhs->lhs_classmember_idx);
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile an imported LHS variable
|
||||
*/
|
||||
static int
|
||||
compile_lhs_import_var(
|
||||
lhs_T *lhs,
|
||||
imported_T *import,
|
||||
char_u *var_start,
|
||||
char_u **var_endp,
|
||||
char_u **rawnamep)
|
||||
{
|
||||
char_u *var_end = *var_endp;
|
||||
char_u *dot = vim_strchr(var_start, '.');
|
||||
char_u *p;
|
||||
|
||||
// for an import the name is what comes after the dot
|
||||
if (dot == NULL)
|
||||
{
|
||||
semsg(_(e_no_dot_after_imported_name_str), var_start);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p = skipwhite(dot + 1);
|
||||
var_end = to_name_end(p, TRUE);
|
||||
if (var_end == p)
|
||||
{
|
||||
semsg(_(e_missing_name_after_imported_name_str), var_start);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
vim_free(lhs->lhs_name);
|
||||
lhs->lhs_varlen = var_end - p;
|
||||
lhs->lhs_name = vim_strnsave(p, lhs->lhs_varlen);
|
||||
if (lhs->lhs_name == NULL)
|
||||
return FAIL;
|
||||
*rawnamep = lhs->lhs_name;
|
||||
lhs->lhs_scriptvar_sid = import->imp_sid;
|
||||
|
||||
// TODO: where do we check this name is exported?
|
||||
|
||||
// Check if something follows: "exp.var[idx]" or
|
||||
// "exp.var.key".
|
||||
lhs->lhs_has_index = lhs->lhs_dest_end > skipwhite(var_end);
|
||||
|
||||
*var_endp = var_end;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process a script-local variable when compiling a LHS variable name.
|
||||
*/
|
||||
static int
|
||||
compile_lhs_script_var(
|
||||
cctx_T *cctx,
|
||||
lhs_T *lhs,
|
||||
char_u *var_start,
|
||||
char_u *var_end,
|
||||
int is_decl)
|
||||
{
|
||||
int script_namespace = FALSE;
|
||||
int script_var = FALSE;
|
||||
imported_T *import;
|
||||
char_u *var_name;
|
||||
int var_name_len;
|
||||
|
||||
if (lhs->lhs_varlen > 1 && STRNCMP(var_start, "s:", 2) == 0)
|
||||
script_namespace = TRUE;
|
||||
|
||||
if (script_namespace)
|
||||
{
|
||||
var_name = var_start + 2;
|
||||
var_name_len = lhs->lhs_varlen - 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
var_name = var_start;
|
||||
var_name_len = lhs->lhs_varlen;
|
||||
}
|
||||
|
||||
if (script_var_exists(var_name, var_name_len, cctx, NULL) == OK)
|
||||
script_var = TRUE;
|
||||
|
||||
import = find_imported(var_start, lhs->lhs_varlen, FALSE);
|
||||
|
||||
if (script_namespace || script_var || import != NULL)
|
||||
{
|
||||
char_u *rawname = lhs->lhs_name + (lhs->lhs_name[1] == ':' ? 2 : 0);
|
||||
|
||||
if (script_namespace && current_script_is_vim9())
|
||||
{
|
||||
semsg(_(e_cannot_use_s_colon_in_vim9_script_str), var_start);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (is_decl)
|
||||
{
|
||||
if (script_namespace)
|
||||
semsg(_(e_cannot_declare_script_variable_in_function_str),
|
||||
lhs->lhs_name);
|
||||
else
|
||||
semsg(_(e_variable_already_declared_in_script_str),
|
||||
lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
else if (cctx->ctx_ufunc->uf_script_ctx_version == SCRIPT_VERSION_VIM9
|
||||
&& script_namespace
|
||||
&& !script_var && import == NULL)
|
||||
{
|
||||
semsg(_(e_unknown_variable_str), lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
lhs->lhs_dest = current_script_is_vim9() ? dest_script_v9 :
|
||||
dest_script;
|
||||
|
||||
// existing script-local variables should have a type
|
||||
lhs->lhs_scriptvar_sid = current_sctx.sc_sid;
|
||||
if (import != NULL)
|
||||
{
|
||||
if (compile_lhs_import_var(lhs, import, var_start, &var_end,
|
||||
&rawname) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (SCRIPT_ID_VALID(lhs->lhs_scriptvar_sid))
|
||||
{
|
||||
// Check writable only when no index follows.
|
||||
lhs->lhs_scriptvar_idx = get_script_item_idx(
|
||||
lhs->lhs_scriptvar_sid, rawname,
|
||||
lhs->lhs_has_index ? ASSIGN_FINAL :
|
||||
ASSIGN_CONST, cctx, NULL);
|
||||
if (lhs->lhs_scriptvar_idx >= 0)
|
||||
{
|
||||
scriptitem_T *si = SCRIPT_ITEM(lhs->lhs_scriptvar_sid);
|
||||
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data)
|
||||
+ lhs->lhs_scriptvar_idx;
|
||||
|
||||
lhs->lhs_type = sv->sv_type;
|
||||
}
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
return check_defined(var_start, lhs->lhs_varlen, cctx, NULL, FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compile the LHS destination.
|
||||
*/
|
||||
static int
|
||||
compile_lhs_var_dest(
|
||||
cctx_T *cctx,
|
||||
lhs_T *lhs,
|
||||
int cmdidx,
|
||||
char_u *var_start,
|
||||
char_u *var_end,
|
||||
int is_decl)
|
||||
{
|
||||
int declare_error = FALSE;
|
||||
|
||||
@@ -1715,8 +1940,9 @@ compile_lhs(
|
||||
&lhs->lhs_opt_flags, &lhs->lhs_vimvaridx,
|
||||
&lhs->lhs_type, cctx) == FAIL)
|
||||
return FAIL;
|
||||
if (lhs->lhs_dest != dest_local
|
||||
&& cmdidx != CMD_const && cmdidx != CMD_final)
|
||||
|
||||
if (lhs->lhs_dest != dest_local && cmdidx != CMD_const
|
||||
&& cmdidx != CMD_final)
|
||||
{
|
||||
// Specific kind of variable recognized.
|
||||
declare_error = is_decl;
|
||||
@@ -1730,8 +1956,8 @@ compile_lhs(
|
||||
&& *var_end == '.') == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (lookup_local(var_start, lhs->lhs_varlen,
|
||||
&lhs->lhs_local_lvar, cctx) == OK)
|
||||
if (lookup_local(var_start, lhs->lhs_varlen, &lhs->lhs_local_lvar,
|
||||
cctx) == OK)
|
||||
{
|
||||
lhs->lhs_lvar = &lhs->lhs_local_lvar;
|
||||
}
|
||||
@@ -1772,134 +1998,14 @@ compile_lhs(
|
||||
else if ((lhs->lhs_classmember_idx = cctx_class_member_idx(
|
||||
cctx, var_start, lhs->lhs_varlen, &defcl)) >= 0)
|
||||
{
|
||||
if (cctx->ctx_ufunc->uf_defclass != defcl)
|
||||
{
|
||||
// A class variable can be accessed without the class name
|
||||
// only inside a class.
|
||||
semsg(_(e_class_variable_str_accessible_only_inside_class_str),
|
||||
lhs->lhs_name, defcl->class_name);
|
||||
if (compile_lhs_class_variable(cctx, lhs, defcl, is_decl)
|
||||
== FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
if (is_decl)
|
||||
{
|
||||
semsg(_(e_variable_already_declared_in_class_str),
|
||||
lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
ocmember_T *m =
|
||||
&defcl->class_class_members[lhs->lhs_classmember_idx];
|
||||
if (oc_var_check_ro(defcl, m))
|
||||
return FAIL;
|
||||
|
||||
lhs->lhs_dest = dest_class_member;
|
||||
// The class variable is defined either in the current class or
|
||||
// in one of the parent class in the hierarchy.
|
||||
lhs->lhs_class = defcl;
|
||||
lhs->lhs_type = oc_member_type_by_idx(defcl, FALSE,
|
||||
lhs->lhs_classmember_idx);
|
||||
}
|
||||
else
|
||||
{
|
||||
int script_namespace = lhs->lhs_varlen > 1
|
||||
&& STRNCMP(var_start, "s:", 2) == 0;
|
||||
int script_var = (script_namespace
|
||||
? script_var_exists(var_start + 2, lhs->lhs_varlen - 2,
|
||||
cctx, NULL)
|
||||
: script_var_exists(var_start, lhs->lhs_varlen,
|
||||
cctx, NULL)) == OK;
|
||||
imported_T *import =
|
||||
find_imported(var_start, lhs->lhs_varlen, FALSE);
|
||||
|
||||
if (script_namespace || script_var || import != NULL)
|
||||
{
|
||||
char_u *rawname = lhs->lhs_name
|
||||
+ (lhs->lhs_name[1] == ':' ? 2 : 0);
|
||||
|
||||
if (script_namespace && current_script_is_vim9())
|
||||
{
|
||||
semsg(_(e_cannot_use_s_colon_in_vim9_script_str),
|
||||
var_start);
|
||||
return FAIL;
|
||||
}
|
||||
if (is_decl)
|
||||
{
|
||||
if (script_namespace)
|
||||
semsg(_(e_cannot_declare_script_variable_in_function_str),
|
||||
lhs->lhs_name);
|
||||
else
|
||||
semsg(_(e_variable_already_declared_in_script_str),
|
||||
lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
else if (cctx->ctx_ufunc->uf_script_ctx_version
|
||||
== SCRIPT_VERSION_VIM9
|
||||
&& script_namespace
|
||||
&& !script_var && import == NULL)
|
||||
{
|
||||
semsg(_(e_unknown_variable_str), lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
lhs->lhs_dest = current_script_is_vim9()
|
||||
? dest_script_v9 : dest_script;
|
||||
|
||||
// existing script-local variables should have a type
|
||||
lhs->lhs_scriptvar_sid = current_sctx.sc_sid;
|
||||
if (import != NULL)
|
||||
{
|
||||
char_u *dot = vim_strchr(var_start, '.');
|
||||
char_u *p;
|
||||
|
||||
// for an import the name is what comes after the dot
|
||||
if (dot == NULL)
|
||||
{
|
||||
semsg(_(e_no_dot_after_imported_name_str),
|
||||
var_start);
|
||||
return FAIL;
|
||||
}
|
||||
p = skipwhite(dot + 1);
|
||||
var_end = to_name_end(p, TRUE);
|
||||
if (var_end == p)
|
||||
{
|
||||
semsg(_(e_missing_name_after_imported_name_str),
|
||||
var_start);
|
||||
return FAIL;
|
||||
}
|
||||
vim_free(lhs->lhs_name);
|
||||
lhs->lhs_varlen = var_end - p;
|
||||
lhs->lhs_name = vim_strnsave(p, lhs->lhs_varlen);
|
||||
if (lhs->lhs_name == NULL)
|
||||
return FAIL;
|
||||
rawname = lhs->lhs_name;
|
||||
lhs->lhs_scriptvar_sid = import->imp_sid;
|
||||
// TODO: where do we check this name is exported?
|
||||
|
||||
// Check if something follows: "exp.var[idx]" or
|
||||
// "exp.var.key".
|
||||
lhs->lhs_has_index = lhs->lhs_dest_end
|
||||
> skipwhite(var_end);
|
||||
}
|
||||
if (SCRIPT_ID_VALID(lhs->lhs_scriptvar_sid))
|
||||
{
|
||||
// Check writable only when no index follows.
|
||||
lhs->lhs_scriptvar_idx = get_script_item_idx(
|
||||
lhs->lhs_scriptvar_sid, rawname,
|
||||
lhs->lhs_has_index ? ASSIGN_FINAL : ASSIGN_CONST,
|
||||
cctx, NULL);
|
||||
if (lhs->lhs_scriptvar_idx >= 0)
|
||||
{
|
||||
scriptitem_T *si = SCRIPT_ITEM(
|
||||
lhs->lhs_scriptvar_sid);
|
||||
svar_T *sv =
|
||||
((svar_T *)si->sn_var_vals.ga_data)
|
||||
+ lhs->lhs_scriptvar_idx;
|
||||
lhs->lhs_type = sv->sv_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (check_defined(var_start, lhs->lhs_varlen, cctx,
|
||||
NULL, FALSE) == FAIL)
|
||||
if (compile_lhs_script_var(cctx, lhs, var_start, var_end,
|
||||
is_decl) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
@@ -1909,13 +2015,81 @@ compile_lhs(
|
||||
vim9_declare_error(lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
// handle "a:name" as a name, not index "name" in "a"
|
||||
if (lhs->lhs_varlen > 1 || var_start[lhs->lhs_varlen] != ':')
|
||||
var_end = lhs->lhs_dest_end;
|
||||
/*
|
||||
* When compiling a LHS variable name, for a class or an object, set the LHS
|
||||
* member type.
|
||||
*/
|
||||
static int
|
||||
compile_lhs_set_oc_member_type(
|
||||
cctx_T *cctx,
|
||||
lhs_T *lhs,
|
||||
char_u *var_start)
|
||||
{
|
||||
class_T *cl = lhs->lhs_type->tt_class;
|
||||
int is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
|
||||
char_u *name = var_start + lhs->lhs_varlen + 1;
|
||||
size_t namelen = lhs->lhs_end - var_start - lhs->lhs_varlen - 1;
|
||||
|
||||
if (lhs->lhs_dest != dest_option && lhs->lhs_dest != dest_func_option)
|
||||
ocmember_T *m = member_lookup(cl, lhs->lhs_type->tt_type,
|
||||
name, namelen, &lhs->lhs_member_idx);
|
||||
if (m == NULL)
|
||||
{
|
||||
member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (IS_ENUM(cl))
|
||||
{
|
||||
if (!inside_class(cctx, cl))
|
||||
{
|
||||
semsg(_(e_enumvalue_str_cannot_be_modified),
|
||||
cl->class_name, m->ocm_name);
|
||||
return FAIL;
|
||||
}
|
||||
if (lhs->lhs_type->tt_type == VAR_OBJECT &&
|
||||
lhs->lhs_member_idx < 2)
|
||||
{
|
||||
char *msg = lhs->lhs_member_idx == 0 ?
|
||||
e_enum_str_name_cannot_be_modified :
|
||||
e_enum_str_ordinal_cannot_be_modified;
|
||||
semsg(_(msg), cl->class_name);
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// If it is private member variable, then accessing it outside the
|
||||
// class is not allowed.
|
||||
// If it is a read only class variable, then it can be modified
|
||||
// only inside the class where it is defined.
|
||||
if ((m->ocm_access != VIM_ACCESS_ALL) &&
|
||||
((is_object && !inside_class(cctx, cl))
|
||||
|| (!is_object && cctx->ctx_ufunc->uf_class != cl)))
|
||||
{
|
||||
char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE)
|
||||
? e_cannot_access_protected_variable_str
|
||||
: e_variable_is_not_writable_str;
|
||||
emsg_var_cl_define(msg, m->ocm_name, 0, cl);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (!IS_CONSTRUCTOR_METHOD(cctx->ctx_ufunc)
|
||||
&& oc_var_check_ro(cl, m))
|
||||
return FAIL;
|
||||
|
||||
lhs->lhs_member_type = m->ocm_type;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* When compiling a LHS variable, set the LHS variable type.
|
||||
*/
|
||||
static int
|
||||
compile_lhs_set_type(cctx_T *cctx, lhs_T *lhs, char_u *var_end, int is_decl)
|
||||
{
|
||||
if (is_decl && *skipwhite(var_end) == ':')
|
||||
{
|
||||
@@ -1927,34 +2101,52 @@ compile_lhs(
|
||||
semsg(_(e_no_white_space_allowed_before_colon_str), var_end);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (!VIM_ISWHITE(var_end[1]))
|
||||
{
|
||||
semsg(_(e_white_space_required_after_str_str), ":", var_end);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
p = skipwhite(var_end + 1);
|
||||
lhs->lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE);
|
||||
if (lhs->lhs_type == NULL)
|
||||
return FAIL;
|
||||
|
||||
lhs->lhs_has_type = TRUE;
|
||||
lhs->lhs_end = p;
|
||||
}
|
||||
else if (lhs->lhs_lvar != NULL)
|
||||
lhs->lhs_type = lhs->lhs_lvar->lv_type;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (oplen == 3 && !heredoc
|
||||
&& lhs->lhs_dest != dest_global
|
||||
&& !lhs->lhs_has_index
|
||||
&& lhs->lhs_type->tt_type != VAR_STRING
|
||||
&& lhs->lhs_type->tt_type != VAR_ANY)
|
||||
/*
|
||||
* Returns TRUE if "lhs" is a concatenable string.
|
||||
*/
|
||||
static int
|
||||
lhs_concatenable(lhs_T *lhs)
|
||||
{
|
||||
emsg(_(e_can_only_concatenate_to_string));
|
||||
return FAIL;
|
||||
return lhs->lhs_dest == dest_global
|
||||
|| lhs->lhs_has_index
|
||||
|| lhs->lhs_type->tt_type == VAR_STRING
|
||||
|| lhs->lhs_type->tt_type == VAR_ANY;
|
||||
}
|
||||
|
||||
if (lhs->lhs_lvar == NULL && lhs->lhs_dest == dest_local
|
||||
&& cctx->ctx_skip != SKIP_YES)
|
||||
/*
|
||||
* Create a new local variable when compiling a LHS variable.
|
||||
*/
|
||||
static int
|
||||
compile_lhs_new_local_var(
|
||||
cctx_T *cctx,
|
||||
lhs_T *lhs,
|
||||
char_u *var_start,
|
||||
int cmdidx,
|
||||
int oplen,
|
||||
int is_decl,
|
||||
int has_cmd,
|
||||
int heredoc)
|
||||
{
|
||||
if (oplen > 1 && !heredoc)
|
||||
{
|
||||
@@ -1962,6 +2154,7 @@ compile_lhs(
|
||||
semsg(_(e_cannot_use_operator_on_new_variable_str), lhs->lhs_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (!is_decl || (lhs->lhs_has_index && !has_cmd
|
||||
&& cctx->ctx_skip != SKIP_YES))
|
||||
{
|
||||
@@ -1970,24 +2163,51 @@ compile_lhs(
|
||||
}
|
||||
|
||||
// Check the name is valid for a funcref.
|
||||
if ((lhs->lhs_type->tt_type == VAR_FUNC
|
||||
if (lhs->lhs_type->tt_type == VAR_FUNC
|
||||
|| lhs->lhs_type->tt_type == VAR_PARTIAL)
|
||||
&& var_wrong_func_name(lhs->lhs_name, TRUE))
|
||||
{
|
||||
if (var_wrong_func_name(lhs->lhs_name, TRUE))
|
||||
return FAIL;
|
||||
|
||||
// New local variable.
|
||||
int assign = cmdidx == CMD_final ? ASSIGN_FINAL
|
||||
: cmdidx == CMD_const ? ASSIGN_CONST : ASSIGN_VAR;
|
||||
lhs->lhs_lvar = reserve_local(cctx, var_start, lhs->lhs_varlen,
|
||||
assign, lhs->lhs_type);
|
||||
if (lhs->lhs_lvar == NULL)
|
||||
return FAIL;
|
||||
lhs->lhs_new_local = TRUE;
|
||||
}
|
||||
|
||||
lhs->lhs_member_type = lhs->lhs_type;
|
||||
if (lhs->lhs_has_index)
|
||||
// New local variable.
|
||||
int assign;
|
||||
switch (cmdidx)
|
||||
{
|
||||
case CMD_final:
|
||||
assign = ASSIGN_FINAL; break;
|
||||
case CMD_const:
|
||||
assign = ASSIGN_CONST; break;
|
||||
default:
|
||||
assign = ASSIGN_VAR; break;
|
||||
}
|
||||
|
||||
lhs->lhs_lvar = reserve_local(cctx, var_start, lhs->lhs_varlen, assign,
|
||||
lhs->lhs_type);
|
||||
if (lhs->lhs_lvar == NULL)
|
||||
return FAIL;
|
||||
|
||||
lhs->lhs_new_local = TRUE;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* When compiling a LHS variable name, set the LHS member type.
|
||||
*/
|
||||
static int
|
||||
compile_lhs_set_member_type(
|
||||
cctx_T *cctx,
|
||||
lhs_T *lhs,
|
||||
char_u *var_start,
|
||||
int is_decl,
|
||||
int has_cmd)
|
||||
{
|
||||
lhs->lhs_member_type = lhs->lhs_type;
|
||||
|
||||
if (!lhs->lhs_has_index)
|
||||
return OK;
|
||||
|
||||
char_u *after = var_start + lhs->lhs_varlen;
|
||||
char_u *p;
|
||||
|
||||
@@ -2027,6 +2247,7 @@ compile_lhs(
|
||||
int use_class = lhs->lhs_type != NULL
|
||||
&& (lhs->lhs_type->tt_type == VAR_CLASS
|
||||
|| lhs->lhs_type->tt_type == VAR_OBJECT);
|
||||
|
||||
if (lhs->lhs_type == NULL
|
||||
|| (use_class ? lhs->lhs_type->tt_class == NULL
|
||||
: lhs->lhs_type->tt_member == NULL))
|
||||
@@ -2036,62 +2257,73 @@ compile_lhs(
|
||||
else if (use_class)
|
||||
{
|
||||
// for an object or class member get the type of the member
|
||||
class_T *cl = lhs->lhs_type->tt_class;
|
||||
int is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
|
||||
char_u *name = var_start + lhs->lhs_varlen + 1;
|
||||
size_t namelen = lhs->lhs_end - var_start - lhs->lhs_varlen - 1;
|
||||
|
||||
ocmember_T *m = member_lookup(cl, lhs->lhs_type->tt_type,
|
||||
name, namelen, &lhs->lhs_member_idx);
|
||||
if (m == NULL)
|
||||
{
|
||||
member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen);
|
||||
if (compile_lhs_set_oc_member_type(cctx, lhs, var_start) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (IS_ENUM(cl))
|
||||
{
|
||||
if (!inside_class(cctx, cl))
|
||||
{
|
||||
semsg(_(e_enumvalue_str_cannot_be_modified),
|
||||
cl->class_name, m->ocm_name);
|
||||
return FALSE;
|
||||
}
|
||||
if (lhs->lhs_type->tt_type == VAR_OBJECT &&
|
||||
lhs->lhs_member_idx < 2)
|
||||
{
|
||||
char *msg = lhs->lhs_member_idx == 0 ?
|
||||
e_enum_str_name_cannot_be_modified :
|
||||
e_enum_str_ordinal_cannot_be_modified;
|
||||
semsg(_(msg), cl->class_name);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// If it is private member variable, then accessing it outside the
|
||||
// class is not allowed.
|
||||
// If it is a read only class variable, then it can be modified
|
||||
// only inside the class where it is defined.
|
||||
if ((m->ocm_access != VIM_ACCESS_ALL) &&
|
||||
((is_object && !inside_class(cctx, cl))
|
||||
|| (!is_object && cctx->ctx_ufunc->uf_class != cl)))
|
||||
{
|
||||
char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE)
|
||||
? e_cannot_access_protected_variable_str
|
||||
: e_variable_is_not_writable_str;
|
||||
emsg_var_cl_define(msg, m->ocm_name, 0, cl);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (!IS_CONSTRUCTOR_METHOD(cctx->ctx_ufunc)
|
||||
&& oc_var_check_ro(cl, m))
|
||||
return FAIL;
|
||||
|
||||
lhs->lhs_member_type = m->ocm_type;
|
||||
}
|
||||
else
|
||||
lhs->lhs_member_type = lhs->lhs_type->tt_member;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Figure out the LHS type and other properties for an assignment or one item
|
||||
* of ":unlet" with an index.
|
||||
* Returns OK or FAIL.
|
||||
*/
|
||||
int
|
||||
compile_lhs(
|
||||
char_u *var_start,
|
||||
lhs_T *lhs,
|
||||
cmdidx_T cmdidx,
|
||||
int heredoc,
|
||||
int has_cmd, // "var" before "var_start"
|
||||
int oplen,
|
||||
cctx_T *cctx)
|
||||
{
|
||||
char_u *var_end;
|
||||
int is_decl = is_decl_command(cmdidx);
|
||||
|
||||
if (lhs_init(lhs, var_start, is_decl, heredoc, &var_end) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (cctx->ctx_skip != SKIP_YES)
|
||||
{
|
||||
// compile the LHS destination
|
||||
if (compile_lhs_var_dest(cctx, lhs, cmdidx, var_start, var_end,
|
||||
is_decl) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
// handle "a:name" as a name, not index "name" in "a"
|
||||
if (lhs->lhs_varlen > 1 || var_start[lhs->lhs_varlen] != ':')
|
||||
var_end = lhs->lhs_dest_end;
|
||||
|
||||
if (lhs->lhs_dest != dest_option && lhs->lhs_dest != dest_func_option)
|
||||
{
|
||||
// set the LHS variable type
|
||||
if (compile_lhs_set_type(cctx, lhs, var_end, is_decl) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (oplen == 3 && !heredoc && !lhs_concatenable(lhs))
|
||||
{
|
||||
emsg(_(e_can_only_concatenate_to_string));
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (lhs->lhs_lvar == NULL && lhs->lhs_dest == dest_local
|
||||
&& cctx->ctx_skip != SKIP_YES)
|
||||
{
|
||||
if (compile_lhs_new_local_var(cctx, lhs, var_start, cmdidx, oplen,
|
||||
is_decl, has_cmd, heredoc) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (compile_lhs_set_member_type(cctx, lhs, var_start, is_decl, has_cmd)
|
||||
== FAIL)
|
||||
return FAIL;
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user