]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0335: String interpolation fails for List type v9.1.0335
authorYegappan Lakshmanan <yegappan@yahoo.com>
Mon, 15 Apr 2024 17:19:52 +0000 (19:19 +0200)
committerChristian Brabandt <cb@256bit.org>
Mon, 15 Apr 2024 17:19:52 +0000 (19:19 +0200)
Problem:  String interpolation fails for List type
Solution: use implicit string(list) for string interpolation and :put =
          (Yegappan Lakshmanan)

related: #14529
closes: #14556

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
15 files changed:
src/eval.c
src/evalvars.c
src/proto/eval.pro
src/proto/vim9instr.pro
src/testdir/test_expr.vim
src/testdir/test_let.vim
src/testdir/test_put.vim
src/testdir/test_vim9_assign.vim
src/version.c
src/vim9.h
src/vim9cmds.c
src/vim9compile.c
src/vim9execute.c
src/vim9expr.c
src/vim9instr.c

index d81ef17526ab5c4e6689a4b41064a9066c7d8b79..51ee6043196fa6c213c7ae091d3d626e41ff8968 100644 (file)
@@ -575,17 +575,16 @@ skip_expr_concatenate(
 
 /*
  * Convert "tv" to a string.
- * When "convert" is TRUE convert a List into a sequence of lines and a Dict
- * into a textual representation of the Dict.
+ * When "join_list" is TRUE convert a List into a sequence of lines.
  * Returns an allocated string (NULL when out of memory).
  */
     char_u *
-typval2string(typval_T *tv, int convert)
+typval2string(typval_T *tv, int join_list)
 {
     garray_T   ga;
     char_u     *retval;
 
-    if (convert && tv->v_type == VAR_LIST)
+    if (join_list && tv->v_type == VAR_LIST)
     {
        ga_init2(&ga, sizeof(char), 80);
        if (tv->vval.v_list != NULL)
@@ -597,8 +596,16 @@ typval2string(typval_T *tv, int convert)
        ga_append(&ga, NUL);
        retval = (char_u *)ga.ga_data;
     }
-    else if (convert && tv->v_type == VAR_DICT)
-       retval = dict2string(tv, get_copyID(), FALSE);
+    else if (tv->v_type == VAR_LIST || tv->v_type == VAR_DICT)
+    {
+       char_u  *tofree;
+       char_u  numbuf[NUMBUFLEN];
+
+       retval = tv2string(tv, &tofree, numbuf, 0);
+       // Make a copy if we have a value but it's not in allocated memory.
+       if (retval != NULL && tofree == NULL)
+           retval = vim_strsave(retval);
+    }
     else
        retval = vim_strsave(tv_get_string(tv));
     return retval;
@@ -607,13 +614,13 @@ typval2string(typval_T *tv, int convert)
 /*
  * Top level evaluation function, returning a string.  Does not handle line
  * breaks.
- * When "convert" is TRUE convert a List into a sequence of lines.
+ * When "join_list" is TRUE convert a List into a sequence of lines.
  * Return pointer to allocated memory, or NULL for failure.
  */
     char_u *
 eval_to_string_eap(
     char_u     *arg,
-    int                convert,
+    int                join_list,
     exarg_T    *eap,
     int                use_simple_function)
 {
@@ -631,7 +638,7 @@ eval_to_string_eap(
        retval = NULL;
     else
     {
-       retval = typval2string(&tv, convert);
+       retval = typval2string(&tv, join_list);
        clear_tv(&tv);
     }
     clear_evalarg(&evalarg, NULL);
@@ -642,10 +649,10 @@ eval_to_string_eap(
     char_u *
 eval_to_string(
     char_u     *arg,
-    int                convert,
+    int                join_list,
     int                use_simple_function)
 {
-    return eval_to_string_eap(arg, convert, NULL, use_simple_function);
+    return eval_to_string_eap(arg, join_list, NULL, use_simple_function);
 }
 
 /*
index d4dd0add2aa2a98887ed929b471a04a1aff4ddc3..70bb6da2ce9089f146d00776bc9e2b2b1c878582 100644 (file)
@@ -662,7 +662,7 @@ eval_one_expr_in_str(char_u *p, garray_T *gap, int evaluate)
     if (evaluate)
     {
        *block_end = NUL;
-       expr_val = eval_to_string(block_start, TRUE, FALSE);
+       expr_val = eval_to_string(block_start, FALSE, FALSE);
        *block_end = '}';
        if (expr_val == NULL)
            return NULL;
index 47fd83db09671156f3fd9561d070198c933fec31..1c2d05dff74ced8b4a984040bb51d3dbe11805a2 100644 (file)
@@ -14,9 +14,9 @@ void init_evalarg(evalarg_T *evalarg);
 void clear_evalarg(evalarg_T *evalarg, exarg_T *eap);
 int skip_expr(char_u **pp, evalarg_T *evalarg);
 int skip_expr_concatenate(char_u **arg, char_u **start, char_u **end, evalarg_T *evalarg);
-char_u *typval2string(typval_T *tv, int convert);
-char_u *eval_to_string_eap(char_u *arg, int convert, exarg_T *eap, int use_simple_function);
-char_u *eval_to_string(char_u *arg, int convert, int use_simple_function);
+char_u *typval2string(typval_T *tv, int join_list);
+char_u *eval_to_string_eap(char_u *arg, int join_list, exarg_T *eap, int use_simple_function);
+char_u *eval_to_string(char_u *arg, int join_list, int use_simple_function);
 char_u *eval_to_string_safe(char_u *arg, int use_sandbox, int keep_script_version, int use_simple_function);
 varnumber_T eval_to_number(char_u *expr, int use_simple_function);
 typval_T *eval_expr(char_u *arg, exarg_T *eap);
index 0fb449d99120d195dc3b6aa16112e0b95a047c2f..1b2f79c959240dc5a3073bc09fac6a41f0ccd261 100644 (file)
@@ -7,7 +7,7 @@ int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
 int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
 int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type);
 int generate_STORE_THIS(cctx_T *cctx, int idx);
-int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
+int may_generate_2STRING(int offset, int tostring_flags, cctx_T *cctx);
 int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
 vartype_T operator_type(type_T *type1, type_T *type2);
 int generate_two_op(cctx_T *cctx, char_u *op);
index 1fa460d09adee1ba583d415f3779304f6f9badc0..6acfe002e2786ac1a9443be454b79e4b5cbd53f9 100644 (file)
@@ -953,6 +953,18 @@ func Test_string_interp()
     #" Dict interpolation
     VAR d = {'a': 10, 'b': [1, 2]}
     call assert_equal("{'a': 10, 'b': [1, 2]}", $'{d}')
+    VAR emptydict = {}
+    call assert_equal("a{}b", $'a{emptydict}b')
+    VAR nulldict = test_null_dict()
+    call assert_equal("a{}b", $'a{nulldict}b')
+
+    #" List interpolation
+    VAR l = ['a', 'b', 'c']
+    call assert_equal("['a', 'b', 'c']", $'{l}')
+    VAR emptylist = []
+    call assert_equal("a[]b", $'a{emptylist}b')
+    VAR nulllist = test_null_list()
+    call assert_equal("a[]b", $'a{nulllist}b')
 
     #" Stray closing brace.
     call assert_fails('echo $"moo}"', 'E1278:')
index fec02731c4f00043a8d7cadd61b397533ef1787d..2aba1d3e220242694e36a445d5f4dc49aa116e75 100644 (file)
@@ -696,6 +696,41 @@ END
   END
   call assert_equal(["let d2 = {'a': 10, 'b': 'ss', 'c': {}}"], code)
 
+  " Empty dictionary
+  let d1 = {}
+  let code =<< eval trim END
+    let d2 = {d1}
+  END
+  call assert_equal(["let d2 = {}"], code)
+
+  " null dictionary
+  let d1 = test_null_dict()
+  let code =<< eval trim END
+    let d2 = {d1}
+  END
+  call assert_equal(["let d2 = {}"], code)
+
+  " Evaluate a List
+  let l1 = ['a', 'b', 'c']
+  let code =<< eval trim END
+    let l2 = {l1}
+  END
+  call assert_equal(["let l2 = ['a', 'b', 'c']"], code)
+
+  " Empty List
+  let l1 = []
+  let code =<< eval trim END
+    let l2 = {l1}
+  END
+  call assert_equal(["let l2 = []"], code)
+
+  " Null List
+  let l1 = test_null_list()
+  let code =<< eval trim END
+    let l2 = {l1}
+  END
+  call assert_equal(["let l2 = []"], code)
+
   let code = 'xxx'
   let code =<< eval trim END
     let n = {5 +
index 6f1e992e36c675348ac0859ade7890018beac891..69c2943aafeb6fa1bb0d5dd2b70daa0dc6011bcf 100644 (file)
@@ -328,4 +328,12 @@ func Test_put_dict()
   bw!
 endfunc
 
+func Test_put_list()
+  new
+  let l = ['a', 'b', 'c']
+  put! =l
+  call assert_equal(['a', 'b', 'c', ''], getline(1, '$'))
+  bw!
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index 4da368c663b0f1e74fd3ef1897f95c7b887fa4f7..4296c37092f2dfd49ac54b3e3f1d4e64f222805a 100644 (file)
@@ -2994,6 +2994,56 @@ def Test_heredoc_expr()
   CODE
   v9.CheckDefAndScriptSuccess(lines)
 
+  # Evaluate an empty dictionary
+  lines =<< trim CODE
+    var d1 = {}
+    var code =<< trim eval END
+      var d2 = {d1}
+    END
+    assert_equal(["var d2 = {}"], code)
+  CODE
+  v9.CheckDefAndScriptSuccess(lines)
+
+  # Evaluate a null dictionary
+  lines =<< trim CODE
+    var d1 = test_null_dict()
+    var code =<< trim eval END
+      var d2 = {d1}
+    END
+    assert_equal(["var d2 = {}"], code)
+  CODE
+  v9.CheckDefAndScriptSuccess(lines)
+
+  # Evaluate a List
+  lines =<< trim CODE
+    var l1 = ['a', 'b', 'c']
+    var code =<< trim eval END
+      var l2 = {l1}
+    END
+    assert_equal(["var l2 = ['a', 'b', 'c']"], code)
+  CODE
+  v9.CheckDefAndScriptSuccess(lines)
+
+  # Evaluate an empty List
+  lines =<< trim CODE
+    var l1 = []
+    var code =<< trim eval END
+      var l2 = {l1}
+    END
+    assert_equal(["var l2 = []"], code)
+  CODE
+  v9.CheckDefAndScriptSuccess(lines)
+
+  # Evaluate a null List
+  lines =<< trim CODE
+    var l1 = test_null_list()
+    var code =<< trim eval END
+      var l2 = {l1}
+    END
+    assert_equal(["var l2 = []"], code)
+  CODE
+  v9.CheckDefAndScriptSuccess(lines)
+
   lines =<< trim CODE
     var code =<< eval trim END
       var s = "{$SOME_ENV_VAR}"
index 7a942c2dc1fe3b32f6e7da3addf5e96161e218a6..b580f0162d9bad6235764d16c38293e3da9e9ec6 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    335,
 /**/
     334,
 /**/
index dd5ad734c79396ef585e22ccac97474c380af637..65de61820776b08f76c939a6c19e16a99ecd5a37 100644 (file)
@@ -460,7 +460,7 @@ typedef struct {
 // arguments to ISN_2STRING and ISN_2STRING_ANY
 typedef struct {
     int                offset;
-    int                tolerant;
+    int                flags;
 } tostring_T;
 
 // arguments to ISN_2BOOL
@@ -880,3 +880,10 @@ typedef enum {
 
 // flags for call_def_function()
 #define DEF_USE_PT_ARGV            1   // use the partial arguments
+
+// Flag used for conversion to string by may_generate_2STRING()
+#define TOSTRING_NONE          0x0
+// Convert a List to series of values separated by newline
+#define TOSTRING_INTERPOLATE   0x1
+// Convert a List to a textual representation of the list "[...]"
+#define TOSTRING_TOLERANT      0x2
index ad245b9c89fc6591186eb1c79b7f33043ff7770e..694bb3349f57d5ef229aa2c13d8bb37d2f5295df 100644 (file)
@@ -1931,7 +1931,7 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
        return NULL;
     if (cctx->ctx_skip == SKIP_YES)
        return p;
-    if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
+    if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
        return NULL;
     if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL)
        return NULL;
@@ -2359,7 +2359,7 @@ compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx)
            p += 2;
            if (compile_expr0(&p, cctx) == FAIL)
                return NULL;
-           may_generate_2STRING(-1, TRUE, cctx);
+           may_generate_2STRING(-1, TOSTRING_TOLERANT, cctx);
            ++count;
            p = skipwhite(p);
            if (*p != '`')
index 7e1d911c1cbfeabb045f09951360247c57d33988..0e05f820c5e3397c915308780a1d1b9680c16370 100644 (file)
@@ -1222,7 +1222,7 @@ compile_one_expr_in_str(char_u *p, cctx_T *cctx)
     }
     if (compile_expr0(&block_start, cctx) == FAIL)
        return NULL;
-    may_generate_2STRING(-1, TRUE, cctx);
+    may_generate_2STRING(-1, TOSTRING_INTERPOLATE, cctx);
 
     return block_end + 1;
 }
@@ -2421,7 +2421,7 @@ compile_assign_unlet(
            return FAIL;
        }
        if (dest_type == VAR_DICT
-                             && may_generate_2STRING(-1, FALSE, cctx) == FAIL)
+               && may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
            return FAIL;
        if (dest_type == VAR_LIST || dest_type == VAR_BLOB)
        {
@@ -2975,7 +2975,7 @@ compile_assignment(
 
            if (*op == '.')
            {
-               if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
+               if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
                    goto theend;
            }
            else
index b3e89396e82afe338b8e9d2c8ffc1a20bb40fcea..5af3af68ba006e7118a13407db3dd0576f168769 100644 (file)
@@ -1638,7 +1638,7 @@ store_var(char_u *name, typval_T *tv)
  * Return FAIL if not allowed.
  */
     static int
-do_2string(typval_T *tv, int is_2string_any, int tolerant)
+do_2string(typval_T *tv, int is_2string_any, int tostring_flags)
 {
     if (tv->v_type == VAR_STRING)
        return OK;
@@ -1657,7 +1657,7 @@ do_2string(typval_T *tv, int is_2string_any, int tolerant)
            case VAR_BLOB:      break;
 
            case VAR_LIST:
-                               if (tolerant)
+                               if (tostring_flags & TOSTRING_TOLERANT)
                                {
                                    char_u      *s, *e, *p;
                                    garray_T    ga;
@@ -1690,6 +1690,8 @@ do_2string(typval_T *tv, int is_2string_any, int tolerant)
                                    tv->vval.v_string = ga.ga_data;
                                    return OK;
                                }
+                               if (tostring_flags & TOSTRING_INTERPOLATE)
+                                   break;
                                // FALLTHROUGH
            default:    to_string_error(tv->v_type);
                        return FAIL;
@@ -5685,7 +5687,7 @@ exec_instructions(ectx_T *ectx)
                SOURCING_LNUM = iptr->isn_lnum;
                if (do_2string(STACK_TV_BOT(iptr->isn_arg.tostring.offset),
                                iptr->isn_type == ISN_2STRING_ANY,
-                                     iptr->isn_arg.tostring.tolerant) == FAIL)
+                                     iptr->isn_arg.tostring.flags) == FAIL)
                            goto on_error;
                break;
 
index 97a7f4e08aae9d18ae7b7ce5c70e9f8929ec4032..ac4d836229aa20ffe1f785d28de7a2fc427254d5 100644 (file)
@@ -142,7 +142,7 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
            typep->type_curr = &t_any;
            typep->type_decl = &t_any;
        }
-       if (may_generate_2STRING(-1, FALSE, cctx) == FAIL
+       if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL
                || generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
            return FAIL;
        if (keeping_dict != NULL)
@@ -1598,7 +1598,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
            }
            if (isn->isn_type == ISN_PUSHS)
                key = isn->isn_arg.string;
-           else if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
+           else if (may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
                return FAIL;
            *arg = skipwhite(*arg);
            if (**arg != ']')
@@ -3014,8 +3014,8 @@ compile_expr6(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
            ppconst->pp_is_const = FALSE;
            if (*op == '.')
            {
-               if (may_generate_2STRING(-2, FALSE, cctx) == FAIL
-                       || may_generate_2STRING(-1, FALSE, cctx) == FAIL)
+               if (may_generate_2STRING(-2, TOSTRING_NONE, cctx) == FAIL
+                       || may_generate_2STRING(-1, TOSTRING_NONE, cctx) == FAIL)
                    return FAIL;
                if (generate_CONCAT(cctx, 2) == FAIL)
                    return FAIL;
index 48ebf1ae4b7cb7c1f0f14833e5a109886ae4d999..4df63fd09a46159c2a8560c4b1e854a3e9c236b3 100644 (file)
@@ -191,10 +191,12 @@ generate_STORE_THIS(cctx_T *cctx, int idx)
 /*
  * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
  * But only for simple types.
- * When "tolerant" is TRUE convert most types to string, e.g. a List.
+ * When tostring_flags has TOSTRING_TOLERANT, convert a List to a series of
+ * strings.  When tostring_flags has TOSTRING_INTERPOLATE, convert a List or a
+ * Dict to the corresponding textual representation.
  */
     int
-may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
+may_generate_2STRING(int offset, int tostring_flags, cctx_T *cctx)
 {
     isn_T      *isn;
     isntype_T  isntype = ISN_2STRING;
@@ -223,11 +225,13 @@ may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
        // conversion possible when tolerant
        case VAR_LIST:
        case VAR_DICT:
-                        if (tolerant)
+                        if (tostring_flags & TOSTRING_TOLERANT)
                         {
                             isntype = ISN_2STRING_ANY;
                             break;
                         }
+                        if (tostring_flags & TOSTRING_INTERPOLATE)
+                            break;
                         // FALLTHROUGH
 
        // conversion not possible
@@ -249,7 +253,7 @@ may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
     if ((isn = generate_instr(cctx, isntype)) == NULL)
        return FAIL;
     isn->isn_arg.tostring.offset = offset;
-    isn->isn_arg.tostring.tolerant = tolerant;
+    isn->isn_arg.tostring.flags = tostring_flags;
 
     return OK;
 }