]> granicus.if.org Git - vim/commitdiff
patch 8.2.2209: Vim9: return type of => lambda not parsed v8.2.2209
authorBram Moolenaar <Bram@vim.org>
Fri, 25 Dec 2020 11:38:04 +0000 (12:38 +0100)
committerBram Moolenaar <Bram@vim.org>
Fri, 25 Dec 2020 11:38:04 +0000 (12:38 +0100)
Problem:    Vim9: return type of => lambda not parsed.
Solution:   Parse and use the return type.

src/eval.c
src/proto/vim9type.pro
src/testdir/test_vim9_expr.vim
src/userfunc.c
src/version.c
src/vim9compile.c
src/vim9script.c
src/vim9type.c

index e61207048ccf9782d00cad2ebc7ac00b64e4a34e..e3a3592c8cb3cbf54b20d13d4986d6d6307f4c94 100644 (file)
@@ -868,7 +868,9 @@ get_lval(
                char_u   *tp = skipwhite(p + 1);
 
                // parse the type after the name
-               lp->ll_type = parse_type(&tp, &si->sn_type_list);
+               lp->ll_type = parse_type(&tp, &si->sn_type_list, !quiet);
+               if (lp->ll_type == NULL && !quiet)
+                   return NULL;
                lp->ll_name_end = tp;
            }
        }
index 90f2345b8e320483579d38eb706584ddc349c112..4409cd6e22bb0015bc693703a90cd0997f9bfea5 100644 (file)
@@ -17,7 +17,7 @@ void arg_type_mismatch(type_T *expected, type_T *actual, int argidx);
 int check_type(type_T *expected, type_T *actual, int give_msg, int argidx);
 int check_arg_type(type_T *expected, type_T *actual, int argidx);
 char_u *skip_type(char_u *start, int optional);
-type_T *parse_type(char_u **arg, garray_T *type_gap);
+type_T *parse_type(char_u **arg, garray_T *type_gap, int give_error);
 void common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap);
 type_T *get_member_type_from_stack(type_T **stack_top, int count, int skip, garray_T *type_gap);
 char *vartype_name(vartype_T type);
index f79c1f6ff5004550a04bcc0029eaec525fdf32a6..c48172997495458ad4b04c75ff26bf52ac1e8846 100644 (file)
@@ -1951,7 +1951,7 @@ def Test_expr7_new_lambda()
       assert_equal([1, 3, 5], res)
 
       # Lambda returning a dict
-      var Lmb = () => {key: 42}
+      var Lmb = () => ({key: 42})
       assert_equal({key: 42}, Lmb())
   END
   CheckDefSuccess(lines)
@@ -1960,11 +1960,16 @@ def Test_expr7_new_lambda()
   CheckDefFailure(["var Ref = (a)=> a + 1"], 'E1001:')
   CheckDefFailure(["var Ref = (a) =>a + 1"], 'E1001:')
 
+  CheckDefSuccess(["var Ref: func(number): string = (a: number): string => 'x'"])
+  CheckDefSuccess(["var Ref: func(number): any = (a: number): any => 'x'"])
+  CheckDefFailure(["var Ref: func(number): number = (a: number): string => 'x'"], 'E1012:')
+  CheckDefFailure(["var Ref: func(number): string = (a: number): string => 99"], 'E1012:')
+
   CheckDefFailure(["filter([1, 2], (k,v) => 1)"], 'E1069:', 1)
   # error is in first line of the lambda
   CheckDefFailure(["var L = (a) -> a + b"], 'E1001:', 1)
 
-# TODO: lambda after -> doesn't work yet
+# TODO: ->(lambda)() doesn't work yet
 #  assert_equal('xxxyyy', 'xxx'->((a, b) => a .. b)('yyy'))
 
 #  CheckDefExecFailure(["var s = 'asdf'->{a -> a}('x')"],
@@ -1973,9 +1978,9 @@ def Test_expr7_new_lambda()
 #        'E1106: 2 arguments too many')
 #  CheckDefFailure(["echo 'asdf'->{a -> a}(x)"], 'E1001:', 1)
 
-  CheckDefSuccess(['var Fx = (a) => {k1: 0,', ' k2: 1}'])
-  CheckDefFailure(['var Fx = (a) => {k1: 0', ' k2: 1}'], 'E722:', 2)
-  CheckDefFailure(['var Fx = (a) => {k1: 0,', ' k2 1}'], 'E720:', 2)
+  CheckDefSuccess(['var Fx = (a) => ({k1: 0,', ' k2: 1})'])
+  CheckDefFailure(['var Fx = (a) => ({k1: 0', ' k2: 1})'], 'E722:', 2)
+  CheckDefFailure(['var Fx = (a) => ({k1: 0,', ' k2 1})'], 'E720:', 2)
 
   CheckDefSuccess(['var Fx = (a) => [0,', ' 1]'])
   CheckDefFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2)
index f9f302a17060c573a1189c21d092c96b4124d3d7..5ba90ae830881db02d03210e4ba503caf2cc65d6 100644 (file)
@@ -349,7 +349,7 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
                    // will get the type from the default value
                    type = &t_unknown;
                else
-                   type = parse_type(&p, &fp->uf_type_list);
+                   type = parse_type(&p, &fp->uf_type_list, TRUE);
                if (type == NULL)
                    return FAIL;
                fp->uf_arg_types[i] = type;
@@ -369,7 +369,7 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
                // todo: get type from default value
                fp->uf_va_type = &t_any;
            else
-               fp->uf_va_type = parse_type(&p, &fp->uf_type_list);
+               fp->uf_va_type = parse_type(&p, &fp->uf_type_list, TRUE);
            if (fp->uf_va_type == NULL)
                return FAIL;
        }
@@ -460,17 +460,22 @@ register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
 
 /*
  * Skip over "->" or "=>" after the arguments of a lambda.
+ * If ": type" is found make "ret_type" point to "type".
  * Return NULL if no valid arrow found.
  */
     static char_u *
-skip_arrow(char_u *start, int equal_arrow)
+skip_arrow(char_u *start, int equal_arrow, char_u **ret_type)
 {
     char_u *s = start;
 
     if (equal_arrow)
     {
        if (*s == ':')
-           s = skip_type(skipwhite(s + 1), TRUE);
+       {
+           s = skipwhite(s + 1);
+           *ret_type = s;
+           s = skip_type(s, TRUE);
+       }
        s = skipwhite(s);
        if (*s != '=')
            return NULL;
@@ -503,6 +508,7 @@ get_lambda_tv(
     ufunc_T    *fp = NULL;
     partial_T   *pt = NULL;
     int                varargs;
+    char_u     *ret_type = NULL;
     int                ret;
     char_u     *s;
     char_u     *start, *end;
@@ -517,19 +523,20 @@ get_lambda_tv(
     ga_init(&newargs);
     ga_init(&newlines);
 
-    // First, check if this is a lambda expression. "->" or "=>" must exist.
+    // First, check if this is really a lambda expression. "->" or "=>" must
+    // be found after the arguments.
     s = skipwhite(*arg + 1);
     ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
            types_optional ? &argtypes : NULL, types_optional,
                                                 NULL, NULL, TRUE, NULL, NULL);
-    if (ret == FAIL || skip_arrow(s, equal_arrow) == NULL)
+    if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type) == NULL)
     {
        if (types_optional)
            ga_clear_strings(&argtypes);
        return NOTDONE;
     }
 
-    // Parse the arguments again.
+    // Parse the arguments for real.
     if (evaluate)
        pnewargs = &newargs;
     else
@@ -538,7 +545,8 @@ get_lambda_tv(
     ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
            types_optional ? &argtypes : NULL, types_optional,
                                            &varargs, NULL, FALSE, NULL, NULL);
-    if (ret == FAIL || (*arg = skip_arrow(*arg, equal_arrow)) == NULL)
+    if (ret == FAIL
+                 || (*arg = skip_arrow(*arg, equal_arrow, &ret_type)) == NULL)
     {
        if (types_optional)
            ga_clear_strings(&argtypes);
@@ -551,11 +559,11 @@ get_lambda_tv(
 
     *arg = skipwhite_and_linebreak(*arg, evalarg);
 
-    // Only recognize "{" as the start of a function body when followed by
-    // white space, "{key: val}" is a dict.
-    if (equal_arrow && **arg == '{' && IS_WHITE_OR_NUL((*arg)[1]))
+    // Recognize "{" as the start of a function body.
+    if (equal_arrow && **arg == '{')
     {
        // TODO: process the function body upto the "}".
+       // Return type is required then.
        emsg("Lambda function body not supported yet");
        goto errret;
     }
@@ -619,9 +627,18 @@ get_lambda_tv(
        hash_add(&func_hashtab, UF2HIKEY(fp));
        fp->uf_args = newargs;
        ga_init(&fp->uf_def_args);
-       if (types_optional
-                        && parse_argument_types(fp, &argtypes, FALSE) == FAIL)
-           goto errret;
+       if (types_optional)
+       {
+           if (parse_argument_types(fp, &argtypes, FALSE) == FAIL)
+               goto errret;
+           if (ret_type != NULL)
+           {
+               fp->uf_ret_type = parse_type(&ret_type,
+                                                     &fp->uf_type_list, TRUE);
+               if (fp->uf_ret_type == NULL)
+                   goto errret;
+           }
+       }
 
        fp->uf_lines = newlines;
        if (current_funccal != NULL && eval_lavars)
@@ -3752,7 +3769,7 @@ define_function(exarg_T *eap, char_u *name_arg)
        else
        {
            p = ret_type;
-           fp->uf_ret_type = parse_type(&p, &fp->uf_type_list);
+           fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
        }
        SOURCING_LNUM = lnum_save;
     }
index 50acca18caf78c4452d2e9d5bf7e99ad9cf52381..ba291afe717b2c48c0bb3fd936c3b5ccc6052e58 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2209,
 /**/
     2208,
 /**/
index 3305bbbd6911561687a722ac8e220189c9f3c8a4..fdc805b48e06f54ec8733786ea096800faf19ec6 100644 (file)
@@ -4118,11 +4118,9 @@ compile_expr7t(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
     // Recognize <type>
     if (**arg == '<' && eval_isnamec1((*arg)[1]))
     {
-       int             called_emsg_before = called_emsg;
-
        ++*arg;
-       want_type = parse_type(arg, cctx->ctx_type_list);
-       if (called_emsg != called_emsg_before)
+       want_type = parse_type(arg, cctx->ctx_type_list, TRUE);
+       if (want_type == NULL)
            return FAIL;
 
        if (**arg != '>')
@@ -4809,7 +4807,7 @@ compile_expr0(char_u **arg,  cctx_T *cctx)
  * compile "return [expr]"
  */
     static char_u *
-compile_return(char_u *arg, int set_return_type, cctx_T *cctx)
+compile_return(char_u *arg, int check_return_type, cctx_T *cctx)
 {
     char_u     *p = arg;
     garray_T   *stack = &cctx->ctx_type_stack;
@@ -4824,8 +4822,10 @@ compile_return(char_u *arg, int set_return_type, cctx_T *cctx)
        if (cctx->ctx_skip != SKIP_YES)
        {
            stack_type = ((type_T **)stack->ga_data)[stack->ga_len - 1];
-           if (set_return_type)
+           if (check_return_type && cctx->ctx_ufunc->uf_ret_type == NULL)
+           {
                cctx->ctx_ufunc->uf_ret_type = stack_type;
+           }
            else
            {
                if (cctx->ctx_ufunc->uf_ret_type->tt_type == VAR_VOID
@@ -4843,7 +4843,7 @@ compile_return(char_u *arg, int set_return_type, cctx_T *cctx)
     }
     else
     {
-       // "set_return_type" cannot be TRUE, only used for a lambda which
+       // "check_return_type" cannot be TRUE, only used for a lambda which
        // always has an argument.
        if (cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_VOID
                && cctx->ctx_ufunc->uf_ret_type->tt_type != VAR_UNKNOWN)
@@ -5636,7 +5636,9 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
                    goto theend;
                }
                p = skipwhite(var_end + 1);
-               type = parse_type(&p, cctx->ctx_type_list);
+               type = parse_type(&p, cctx->ctx_type_list, TRUE);
+               if (type == NULL)
+                   goto theend;
                has_type = TRUE;
            }
            else if (lvar != NULL)
@@ -7417,15 +7419,16 @@ add_def_function(ufunc_T *ufunc)
  * After ex_function() has collected all the function lines: parse and compile
  * the lines into instructions.
  * Adds the function to "def_functions".
- * When "set_return_type" is set then set ufunc->uf_ret_type to the type of the
- * return statement (used for lambda).
+ * When "check_return_type" is set then set ufunc->uf_ret_type to the type of
+ * the return statement (used for lambda).  When uf_ret_type is already set
+ * then check that it matches.
  * "outer_cctx" is set for a nested function.
  * This can be used recursively through compile_lambda(), which may reallocate
  * "def_functions".
  * Returns OK or FAIL.
  */
     int
-compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
+compile_def_function(ufunc_T *ufunc, int check_return_type, cctx_T *outer_cctx)
 {
     char_u     *line = NULL;
     char_u     *p;
@@ -7797,7 +7800,7 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
                    goto erret;
 
            case CMD_return:
-                   line = compile_return(p, set_return_type, &cctx);
+                   line = compile_return(p, check_return_type, &cctx);
                    cctx.ctx_had_return = TRUE;
                    break;
 
index fa455ab191604494dac59ff32284f5018b83ccb6..163b8a7f350e42fdd5adac78818a6ebd418af7af 100644 (file)
@@ -511,7 +511,6 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
     char_u         *name;
     scriptitem_T    *si = SCRIPT_ITEM(current_sctx.sc_sid);
     type_T         *type;
-    int                    called_emsg_before = called_emsg;
     typval_T       init_tv;
 
     if (eap->cmdidx == CMD_final || eap->cmdidx == CMD_const)
@@ -548,8 +547,8 @@ vim9_declare_scriptvar(exarg_T *eap, char_u *arg)
 
     // parse type
     p = skipwhite(p + 1);
-    type = parse_type(&p, &si->sn_type_list);
-    if (called_emsg != called_emsg_before)
+    type = parse_type(&p, &si->sn_type_list, TRUE);
+    if (type == NULL)
     {
        vim_free(name);
        return p;
index d655a63db5a6c07dfc8508d9c8176c4e26a0709f..4ea32a7fe1000b7ac4aeec0789b821351fbc3e3c 100644 (file)
@@ -596,28 +596,38 @@ skip_type(char_u *start, int optional)
  * Returns NULL in case of failure.
  */
     static type_T *
-parse_type_member(char_u **arg, type_T *type, garray_T *type_gap)
+parse_type_member(
+       char_u      **arg,
+       type_T      *type,
+       garray_T    *type_gap,
+       int         give_error)
 {
     type_T  *member_type;
     int            prev_called_emsg = called_emsg;
 
     if (**arg != '<')
     {
-       if (*skipwhite(*arg) == '<')
-           semsg(_(e_no_white_space_allowed_before_str), "<");
-       else
-           emsg(_(e_missing_type));
-       return type;
+       if (give_error)
+       {
+           if (*skipwhite(*arg) == '<')
+               semsg(_(e_no_white_space_allowed_before_str), "<");
+           else
+               emsg(_(e_missing_type));
+       }
+       return NULL;
     }
     *arg = skipwhite(*arg + 1);
 
-    member_type = parse_type(arg, type_gap);
+    member_type = parse_type(arg, type_gap, give_error);
+    if (member_type == NULL)
+       return NULL;
 
     *arg = skipwhite(*arg);
     if (**arg != '>' && called_emsg == prev_called_emsg)
     {
-       emsg(_(e_missing_gt_after_type));
-       return type;
+       if (give_error)
+           emsg(_(e_missing_gt_after_type));
+       return NULL;
     }
     ++*arg;
 
@@ -628,10 +638,11 @@ parse_type_member(char_u **arg, type_T *type, garray_T *type_gap)
 
 /*
  * Parse a type at "arg" and advance over it.
- * Return &t_any for failure.
+ * When "give_error" is TRUE give error messages, otherwise be quiet.
+ * Return NULL for failure.
  */
     type_T *
-parse_type(char_u **arg, garray_T *type_gap)
+parse_type(char_u **arg, garray_T *type_gap, int give_error)
 {
     char_u  *p = *arg;
     size_t  len;
@@ -673,7 +684,8 @@ parse_type(char_u **arg, garray_T *type_gap)
            if (len == 4 && STRNCMP(*arg, "dict", len) == 0)
            {
                *arg += len;
-               return parse_type_member(arg, &t_dict_any, type_gap);
+               return parse_type_member(arg, &t_dict_any,
+                                                        type_gap, give_error);
            }
            break;
        case 'f':
@@ -683,8 +695,9 @@ parse_type(char_u **arg, garray_T *type_gap)
                *arg += len;
                return &t_float;
 #else
-               emsg(_(e_this_vim_is_not_compiled_with_float_support));
-               return &t_any;
+               if (give_error)
+                   emsg(_(e_this_vim_is_not_compiled_with_float_support));
+               return NULL;
 #endif
            }
            if (len == 4 && STRNCMP(*arg, "func", len) == 0)
@@ -721,11 +734,15 @@ parse_type(char_u **arg, garray_T *type_gap)
                        }
                        else if (first_optional != -1)
                        {
-                           emsg(_(e_mandatory_argument_after_optional_argument));
-                           return &t_any;
+                           if (give_error)
+                               emsg(_(e_mandatory_argument_after_optional_argument));
+                           return NULL;
                        }
 
-                       arg_type[argcount++] = parse_type(&p, type_gap);
+                       type = parse_type(&p, type_gap, give_error);
+                       if (type == NULL)
+                           return NULL;
+                       arg_type[argcount++] = type;
 
                        // Nothing comes after "...{type}".
                        if (flags & TTFLAG_VARARGS)
@@ -733,31 +750,35 @@ parse_type(char_u **arg, garray_T *type_gap)
 
                        if (*p != ',' && *skipwhite(p) == ',')
                        {
-                           semsg(_(e_no_white_space_allowed_before_str), ",");
-                           return &t_any;
+                           if (give_error)
+                               semsg(_(e_no_white_space_allowed_before_str), ",");
+                           return NULL;
                        }
                        if (*p == ',')
                        {
                            ++p;
                            if (!VIM_ISWHITE(*p))
                            {
-                               semsg(_(e_white_space_required_after_str), ",");
-                               return &t_any;
+                               if (give_error)
+                                   semsg(_(e_white_space_required_after_str), ",");
+                               return NULL;
                            }
                        }
                        p = skipwhite(p);
                        if (argcount == MAX_FUNC_ARGS)
                        {
-                           emsg(_(e_too_many_argument_types));
-                           return &t_any;
+                           if (give_error)
+                               emsg(_(e_too_many_argument_types));
+                           return NULL;
                        }
                    }
 
                    p = skipwhite(p);
                    if (*p != ')')
                    {
-                       emsg(_(e_missing_close));
-                       return &t_any;
+                       if (give_error)
+                           emsg(_(e_missing_close));
+                       return NULL;
                    }
                    *arg = p + 1;
                }
@@ -765,10 +786,12 @@ parse_type(char_u **arg, garray_T *type_gap)
                {
                    // parse return type
                    ++*arg;
-                   if (!VIM_ISWHITE(**arg))
+                   if (!VIM_ISWHITE(**arg) && give_error)
                        semsg(_(e_white_space_required_after_str), ":");
                    *arg = skipwhite(*arg);
-                   ret_type = parse_type(arg, type_gap);
+                   ret_type = parse_type(arg, type_gap, give_error);
+                   if (ret_type == NULL)
+                       return NULL;
                }
                if (flags == 0 && first_optional == -1 && argcount <= 0)
                    type = get_func_type(ret_type, argcount, type_gap);
@@ -783,7 +806,7 @@ parse_type(char_u **arg, garray_T *type_gap)
                                                   ? argcount : first_optional;
                        if (func_type_add_arg_types(type, argcount,
                                                             type_gap) == FAIL)
-                           return &t_any;
+                           return NULL;
                        mch_memmove(type->tt_args, arg_type,
                                                  sizeof(type_T *) * argcount);
                    }
@@ -802,7 +825,8 @@ parse_type(char_u **arg, garray_T *type_gap)
            if (len == 4 && STRNCMP(*arg, "list", len) == 0)
            {
                *arg += len;
-               return parse_type_member(arg, &t_list_any, type_gap);
+               return parse_type_member(arg, &t_list_any,
+                                                        type_gap, give_error);
            }
            break;
        case 'n':
@@ -828,8 +852,9 @@ parse_type(char_u **arg, garray_T *type_gap)
            break;
     }
 
-    semsg(_(e_type_not_recognized_str), *arg);
-    return &t_any;
+    if (give_error)
+       semsg(_(e_type_not_recognized_str), *arg);
+    return NULL;
 }
 
 /*
@@ -1016,9 +1041,12 @@ vartype_name(vartype_T type)
     char *
 type_name(type_T *type, char **tofree)
 {
-    char *name = vartype_name(type->tt_type);
+    char *name;
 
     *tofree = NULL;
+    if (type == NULL)
+       return "[unknown]";
+    name = vartype_name(type->tt_type);
     if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT)
     {
        char *member_free;