0
0
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:
Yegappan Lakshmanan
2024-12-18 20:16:20 +01:00
committed by Christian Brabandt
parent 80d72aa547
commit d0186c54c2
2 changed files with 602 additions and 368 deletions

View File

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

View File

@@ -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;
}