From 5eaa4d98f79dfda65673db5c7b7f84be82ffec02 Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Sun, 21 Apr 2024 14:48:57 +0200 Subject: patch 9.1.0361: Vim9: vim9type.c is too complicated Problem: Vim9: vim9type.c is too complicated Solution: Refactor a few functions in vim9type.c (Yegappan Lakshmanan) closes: #14605 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/version.c | 2 + src/vim9type.c | 779 ++++++++++++++++++++++++++++++++------------------------- 2 files changed, 438 insertions(+), 343 deletions(-) diff --git a/src/version.c b/src/version.c index d418b602b2..402ab52665 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 361, /**/ 360, /**/ diff --git a/src/vim9type.c b/src/vim9type.c index 55de0de27b..228df6e2fa 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -208,6 +208,59 @@ set_tv_type_recurse(type_T *type) && type->tt_member->tt_member != &t_unknown; } +/* + * Set the type of Dict "d" to "type" + */ + static void +set_tv_type_dict(dict_T *d, type_T *type) +{ + if (d->dv_type == type) + return; + + free_type(d->dv_type); + d->dv_type = alloc_type(type); + + // Need to recursively set the type of dict items? + if (!set_tv_type_recurse(type)) + return; + + int todo = (int)d->dv_hashtab.ht_used; + hashitem_T *hi; + dictitem_T *di; + + FOR_ALL_HASHTAB_ITEMS(&d->dv_hashtab, hi, todo) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + di = HI2DI(hi); + set_tv_type(&di->di_tv, type->tt_member); + } + } +} + +/* + * Set the type of List "l" to "type" + */ + static void +set_tv_type_list(list_T *l, type_T *type) +{ + if (l->lv_type == type) + return; + + free_type(l->lv_type); + l->lv_type = alloc_type(type); + + // Need to recursively set the type of list items? + if (l->lv_first == &range_list_item || !set_tv_type_recurse(type)) + return; + + listitem_T *li; + + FOR_ALL_LIST_ITEMS(l, li) + set_tv_type(&li->li_tv, type->tt_member); +} + /* * Set the type of "tv" to "type" if it is a list or dict. */ @@ -218,49 +271,11 @@ set_tv_type(typval_T *tv, type_T *type) // If the variable type is "any", then keep the value type. // e.g. var x: any = [1, 2] or var y: any = {v: 1} return; - if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) - { - dict_T *d = tv->vval.v_dict; - - if (d->dv_type != type) - { - free_type(d->dv_type); - d->dv_type = alloc_type(type); - if (set_tv_type_recurse(type)) - { - int todo = (int)d->dv_hashtab.ht_used; - hashitem_T *hi; - dictitem_T *di; - FOR_ALL_HASHTAB_ITEMS(&d->dv_hashtab, hi, todo) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - di = HI2DI(hi); - set_tv_type(&di->di_tv, type->tt_member); - } - } - } - } - } + if (tv->v_type == VAR_DICT && tv->vval.v_dict != NULL) + set_tv_type_dict(tv->vval.v_dict, type); else if (tv->v_type == VAR_LIST && tv->vval.v_list != NULL) - { - list_T *l = tv->vval.v_list; - - if (l->lv_type != type) - { - free_type(l->lv_type); - l->lv_type = alloc_type(type); - if (l->lv_first != &range_list_item && set_tv_type_recurse(type)) - { - listitem_T *li; - - FOR_ALL_LIST_ITEMS(l, li) - set_tv_type(&li->li_tv, type->tt_member); - } - } - } + set_tv_type_list(tv->vval.v_list, type); } type_T * @@ -414,7 +429,7 @@ func_type_add_arg_types( type_any_or_unknown(type_T *type) { return type == NULL || type->tt_type == VAR_ANY - || type->tt_type == VAR_UNKNOWN; + || type->tt_type == VAR_UNKNOWN; } /* @@ -1268,6 +1283,194 @@ parse_type_member( return get_dict_type(member_type, type_gap); } +/* + * Parse a "func" type at "*arg" and advance over it. + * When "give_error" is TRUE give error messages, otherwise be quiet. + * Return NULL for failure. + */ + static type_T * +parse_type_func(char_u **arg, size_t len, garray_T *type_gap, int give_error) +{ + char_u *p; + type_T *type; + type_T *ret_type = &t_unknown; + int argcount = -1; + int flags = 0; + int first_optional = -1; + type_T *arg_type[MAX_FUNC_ARGS + 1]; + + // func({type}, ...{type}): {type} + *arg += len; + if (**arg == '(') + { + // "func" may or may not return a value, "func()" does + // not return a value. + ret_type = &t_void; + + p = ++*arg; + argcount = 0; + while (*p != NUL && *p != ')') + { + if (*p == '?') + { + if (first_optional == -1) + first_optional = argcount; + ++p; + } + else if (STRNCMP(p, "...", 3) == 0) + { + flags |= TTFLAG_VARARGS; + p += 3; + } + else if (first_optional != -1) + { + if (give_error) + emsg(_(e_mandatory_argument_after_optional_argument)); + return NULL; + } + + type = parse_type(&p, type_gap, give_error); + if (type == NULL) + return NULL; + if ((flags & TTFLAG_VARARGS) != 0 && type->tt_type != VAR_LIST) + { + char *tofree; + semsg(_(e_variable_arguments_type_must_be_list_str), + type_name(type, &tofree)); + vim_free(tofree); + return NULL; + } + arg_type[argcount++] = type; + + // Nothing comes after "...{type}". + if (flags & TTFLAG_VARARGS) + break; + + if (*p != ',' && *skipwhite(p) == ',') + { + if (give_error) + semsg(_(e_no_white_space_allowed_before_str_str), + ",", p); + return NULL; + } + if (*p == ',') + { + ++p; + if (!VIM_ISWHITE(*p)) + { + if (give_error) + semsg(_(e_white_space_required_after_str_str), + ",", p - 1); + return NULL; + } + } + p = skipwhite(p); + if (argcount == MAX_FUNC_ARGS) + { + if (give_error) + emsg(_(e_too_many_argument_types)); + return NULL; + } + } + + p = skipwhite(p); + if (*p != ')') + { + if (give_error) + emsg(_(e_missing_closing_paren)); + return NULL; + } + *arg = p + 1; + } + if (**arg == ':') + { + // parse return type + ++*arg; + if (!VIM_ISWHITE(**arg) && give_error) + semsg(_(e_white_space_required_after_str_str), ":", *arg - 1); + *arg = skipwhite(*arg); + 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); + else + { + type = alloc_func_type(ret_type, argcount, type_gap); + type->tt_flags = flags; + if (argcount > 0) + { + type->tt_argcount = argcount; + type->tt_min_argcount = first_optional == -1 + ? argcount : first_optional; + if (func_type_add_arg_types(type, argcount, type_gap) == FAIL) + return NULL; + mch_memmove(type->tt_args, arg_type, sizeof(type_T *) * argcount); + } + } + + return type; +} + +/* + * Parse a user defined type at "*arg" and advance over it. + * It can be a class or an interface or a typealias name, possibly imported. + * Return NULL if a type is not found. + */ + static type_T * +parse_type_user_defined( + char_u **arg, + size_t len, + garray_T *type_gap, + int give_error) +{ + int did_emsg_before = did_emsg; + typval_T tv; + + tv.v_type = VAR_UNKNOWN; + if (eval_variable_import(*arg, &tv) == OK) + { + if (tv.v_type == VAR_CLASS && tv.vval.v_class != NULL) + { + type_T *type = get_type_ptr(type_gap); + if (type != NULL) + { + // Although the name is that of a class or interface, the type + // uses will be an object. + type->tt_type = VAR_OBJECT; + type->tt_class = tv.vval.v_class; + clear_tv(&tv); + + *arg += len; + // Skip over ".ClassName". + while (ASCII_ISALNUM(**arg) || **arg == '_' || **arg == '.') + ++*arg; + + return type; + } + } + else if (tv.v_type == VAR_TYPEALIAS) + { + // user defined type + type_T *type = copy_type(tv.vval.v_typealias->ta_type, type_gap); + *arg += len; + clear_tv(&tv); + // Skip over ".TypeName". + while (ASCII_ISALNUM(**arg) || **arg == '_' || **arg == '.') + ++*arg; + return type; + } + + clear_tv(&tv); + } + + if (give_error && (did_emsg == did_emsg_before)) + semsg(_(e_type_not_recognized_str), *arg); + + return NULL; +} + /* * Parse a type at "arg" and advance over it. * When "give_error" is TRUE give error messages, otherwise be quiet. @@ -1327,130 +1530,7 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error) return &t_float; } if (len == 4 && STRNCMP(*arg, "func", len) == 0) - { - type_T *type; - type_T *ret_type = &t_unknown; - int argcount = -1; - int flags = 0; - int first_optional = -1; - type_T *arg_type[MAX_FUNC_ARGS + 1]; - - // func({type}, ...{type}): {type} - *arg += len; - if (**arg == '(') - { - // "func" may or may not return a value, "func()" does - // not return a value. - ret_type = &t_void; - - p = ++*arg; - argcount = 0; - while (*p != NUL && *p != ')') - { - if (*p == '?') - { - if (first_optional == -1) - first_optional = argcount; - ++p; - } - else if (STRNCMP(p, "...", 3) == 0) - { - flags |= TTFLAG_VARARGS; - p += 3; - } - else if (first_optional != -1) - { - if (give_error) - emsg(_(e_mandatory_argument_after_optional_argument)); - return NULL; - } - - type = parse_type(&p, type_gap, give_error); - if (type == NULL) - return NULL; - if ((flags & TTFLAG_VARARGS) != 0 - && type->tt_type != VAR_LIST) - { - char *tofree; - semsg(_(e_variable_arguments_type_must_be_list_str), - type_name(type, &tofree)); - vim_free(tofree); - return NULL; - } - arg_type[argcount++] = type; - - // Nothing comes after "...{type}". - if (flags & TTFLAG_VARARGS) - break; - - if (*p != ',' && *skipwhite(p) == ',') - { - if (give_error) - semsg(_(e_no_white_space_allowed_before_str_str), - ",", p); - return NULL; - } - if (*p == ',') - { - ++p; - if (!VIM_ISWHITE(*p)) - { - if (give_error) - semsg(_(e_white_space_required_after_str_str), - ",", p - 1); - return NULL; - } - } - p = skipwhite(p); - if (argcount == MAX_FUNC_ARGS) - { - if (give_error) - emsg(_(e_too_many_argument_types)); - return NULL; - } - } - - p = skipwhite(p); - if (*p != ')') - { - if (give_error) - emsg(_(e_missing_closing_paren)); - return NULL; - } - *arg = p + 1; - } - if (**arg == ':') - { - // parse return type - ++*arg; - if (!VIM_ISWHITE(**arg) && give_error) - semsg(_(e_white_space_required_after_str_str), - ":", *arg - 1); - *arg = skipwhite(*arg); - 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); - else - { - type = alloc_func_type(ret_type, argcount, type_gap); - type->tt_flags = flags; - if (argcount > 0) - { - type->tt_argcount = argcount; - type->tt_min_argcount = first_optional == -1 - ? argcount : first_optional; - if (func_type_add_arg_types(type, argcount, - type_gap) == FAIL) - return NULL; - mch_memmove(type->tt_args, arg_type, - sizeof(type_T *) * argcount); - } - } - return type; - } + return parse_type_func(arg, len, type_gap, give_error); break; case 'j': if (len == 3 && STRNCMP(*arg, "job", len) == 0) @@ -1490,50 +1570,8 @@ parse_type(char_u **arg, garray_T *type_gap, int give_error) break; } - // It can be a class or interface name, possibly imported. - int did_emsg_before = did_emsg; - typval_T tv; - - tv.v_type = VAR_UNKNOWN; - if (eval_variable_import(*arg, &tv) == OK) - { - if (tv.v_type == VAR_CLASS && tv.vval.v_class != NULL) - { - type_T *type = get_type_ptr(type_gap); - if (type != NULL) - { - // Although the name is that of a class or interface, the type - // uses will be an object. - type->tt_type = VAR_OBJECT; - type->tt_class = tv.vval.v_class; - clear_tv(&tv); - - *arg += len; - // Skip over ".ClassName". - while (ASCII_ISALNUM(**arg) || **arg == '_' || **arg == '.') - ++*arg; - - return type; - } - } - else if (tv.v_type == VAR_TYPEALIAS) - { - // user defined type - type_T *type = copy_type(tv.vval.v_typealias->ta_type, type_gap); - *arg += len; - clear_tv(&tv); - // Skip over ".TypeName". - while (ASCII_ISALNUM(**arg) || **arg == '_' || **arg == '.') - ++*arg; - return type; - } - - clear_tv(&tv); - } - - if (give_error && (did_emsg == did_emsg_before)) - semsg(_(e_type_not_recognized_str), *arg); - return NULL; + // User defined type + return parse_type_user_defined(arg, len, type_gap, give_error); } /* @@ -1589,6 +1627,60 @@ equal_type(type_T *type1, type_T *type2, int flags) return TRUE; } +/* + * Find the common type of "type1" (VAR_FUNC) and "type2" (VAR_FUNC) and put it + * in "dest". "type2" and "dest" may be the same. + */ + static void +common_type_var_func( + type_T *type1, + type_T *type2, + type_T **dest, + garray_T *type_gap) +{ + type_T *common; + + // When one of the types is t_func_unknown return the other one. + // Useful if a list or dict item is null_func. + if (type1 == &t_func_unknown) + { + *dest = type2; + return; + } + if (type2 == &t_func_unknown) + { + *dest = type1; + return; + } + + common_type(type1->tt_member, type2->tt_member, &common, type_gap); + if (type1->tt_argcount == type2->tt_argcount + && type1->tt_argcount >= 0) + { + int argcount = type1->tt_argcount; + int i; + + *dest = alloc_func_type(common, argcount, type_gap); + if (type1->tt_args != NULL && type2->tt_args != NULL) + { + if (func_type_add_arg_types(*dest, argcount, + type_gap) == OK) + for (i = 0; i < argcount; ++i) + common_type(type1->tt_args[i], type2->tt_args[i], + &(*dest)->tt_args[i], type_gap); + } + } + else + // Use -1 for "tt_argcount" to indicate an unknown number of + // arguments. + *dest = alloc_func_type(common, -1, type_gap); + + // Use the minimum of min_argcount. + (*dest)->tt_min_argcount = + type1->tt_min_argcount < type2->tt_min_argcount + ? type1->tt_min_argcount : type2->tt_min_argcount; +} + /* * Find the common type of "type1" and "type2" and put it in "dest". * "type2" and "dest" may be the same. @@ -1628,49 +1720,10 @@ common_type(type_T *type1, type_T *type2, type_T **dest, garray_T *type_gap) *dest = get_dict_type(common, type_gap); return; } + if (type1->tt_type == VAR_FUNC) { - type_T *common; - - // When one of the types is t_func_unknown return the other one. - // Useful if a list or dict item is null_func. - if (type1 == &t_func_unknown) - { - *dest = type2; - return; - } - if (type2 == &t_func_unknown) - { - *dest = type1; - return; - } - - common_type(type1->tt_member, type2->tt_member, &common, type_gap); - if (type1->tt_argcount == type2->tt_argcount - && type1->tt_argcount >= 0) - { - int argcount = type1->tt_argcount; - int i; - - *dest = alloc_func_type(common, argcount, type_gap); - if (type1->tt_args != NULL && type2->tt_args != NULL) - { - if (func_type_add_arg_types(*dest, argcount, - type_gap) == OK) - for (i = 0; i < argcount; ++i) - common_type(type1->tt_args[i], type2->tt_args[i], - &(*dest)->tt_args[i], type_gap); - } - } - else - // Use -1 for "tt_argcount" to indicate an unknown number of - // arguments. - *dest = alloc_func_type(common, -1, type_gap); - - // Use the minimum of min_argcount. - (*dest)->tt_min_argcount = - type1->tt_min_argcount < type2->tt_min_argcount - ? type1->tt_min_argcount : type2->tt_min_argcount; + common_type_var_func(type1, type2, dest, type_gap); return; } } @@ -1825,120 +1878,160 @@ vartype_name(vartype_T type) } /* - * Return the name of a type. + * Return the type name of a List (list) or Dict (dict). * The result may be in allocated memory, in which case "tofree" is set. */ - char * -type_name(type_T *type, char **tofree) + static char * +type_name_list_or_dict(char *name, type_T *type, char **tofree) { - char *name; - char *arg_free = NULL; + char *member_free; + char *member_name; - *tofree = NULL; - if (type == NULL) - return "[unknown]"; - name = vartype_name(type->tt_type); + if (type->tt_member->tt_type == VAR_UNKNOWN) + member_name = type_name(&t_any, &member_free); + else + member_name = type_name(type->tt_member, &member_free); - if (type->tt_type == VAR_LIST || type->tt_type == VAR_DICT) + size_t len = STRLEN(name) + STRLEN(member_name) + 3; + *tofree = alloc(len); + if (*tofree != NULL) { - char *member_free; - char *member_name; - if (type->tt_member->tt_type == VAR_UNKNOWN) - member_name = type_name(&t_any, &member_free); - else - member_name = type_name(type->tt_member, &member_free); - size_t len = STRLEN(name) + STRLEN(member_name) + 3; - *tofree = alloc(len); - if (*tofree != NULL) - { - vim_snprintf(*tofree, len, "%s<%s>", name, member_name); - vim_free(member_free); - return *tofree; - } + vim_snprintf(*tofree, len, "%s<%s>", name, member_name); + vim_free(member_free); + return *tofree; } - if (type->tt_type == VAR_OBJECT || type->tt_type == VAR_CLASS) + return name; +} + +/* + * Return the type name of a Class (class) or Object (object). + * The result may be in allocated memory, in which case "tofree" is set. + */ + static char * +type_name_class_or_obj(char *name, type_T *type, char **tofree) +{ + char_u *class_name; + + if (type->tt_class != NULL) { - char_u *class_name; - if (type->tt_class != NULL) - { - class_name = type->tt_class->class_name; - if (IS_ENUM(type->tt_class)) - name = "enum"; - } - else - class_name = (char_u *)"Unknown"; - size_t len = STRLEN(name) + STRLEN(class_name) + 3; - *tofree = alloc(len); - if (*tofree != NULL) - { - vim_snprintf(*tofree, len, "%s<%s>", name, class_name); - return *tofree; - } + class_name = type->tt_class->class_name; + if (IS_ENUM(type->tt_class)) + name = "enum"; } + else + class_name = (char_u *)"Unknown"; - if (type->tt_type == VAR_FUNC) + size_t len = STRLEN(name) + STRLEN(class_name) + 3; + *tofree = alloc(len); + if (*tofree != NULL) { - garray_T ga; - int i; - int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0; + vim_snprintf(*tofree, len, "%s<%s>", name, class_name); + return *tofree; + } - ga_init2(&ga, 1, 100); - if (ga_grow(&ga, 20) == FAIL) - goto failed; - STRCPY(ga.ga_data, "func("); - ga.ga_len += 5; + return name; +} - for (i = 0; i < type->tt_argcount; ++i) - { - char *arg_type; - int len; +/* + * Return the type name of a functio. + * The result may be in allocated memory, in which case "tofree" is set. + */ + static char * +type_name_func(type_T *type, char **tofree) +{ + garray_T ga; + int i; + int varargs = (type->tt_flags & TTFLAG_VARARGS) ? 1 : 0; + char *arg_free = NULL; - if (type->tt_args == NULL) - arg_type = "[unknown]"; - else - arg_type = type_name(type->tt_args[i], &arg_free); - if (i > 0) - { - STRCPY((char *)ga.ga_data + ga.ga_len, ", "); - ga.ga_len += 2; - } - len = (int)STRLEN(arg_type); - if (ga_grow(&ga, len + 8) == FAIL) - goto failed; - if (varargs && i == type->tt_argcount - 1) - ga_concat(&ga, (char_u *)"..."); - else if (i >= type->tt_min_argcount) - *((char *)ga.ga_data + ga.ga_len++) = '?'; - ga_concat(&ga, (char_u *)arg_type); - VIM_CLEAR(arg_free); - } - if (type->tt_argcount < 0) - // any number of arguments - ga_concat(&ga, (char_u *)"..."); + ga_init2(&ga, 1, 100); + if (ga_grow(&ga, 20) == FAIL) + goto failed; + STRCPY(ga.ga_data, "func("); + ga.ga_len += 5; - if (type->tt_member == &t_void) - STRCPY((char *)ga.ga_data + ga.ga_len, ")"); + for (i = 0; i < type->tt_argcount; ++i) + { + char *arg_type; + int len; + + if (type->tt_args == NULL) + arg_type = "[unknown]"; else + arg_type = type_name(type->tt_args[i], &arg_free); + if (i > 0) { - char *ret_free; - char *ret_name = type_name(type->tt_member, &ret_free); - int len; - - len = (int)STRLEN(ret_name) + 4; - if (ga_grow(&ga, len) == FAIL) - goto failed; - STRCPY((char *)ga.ga_data + ga.ga_len, "): "); - STRCPY((char *)ga.ga_data + ga.ga_len + 3, ret_name); - vim_free(ret_free); + STRCPY((char *)ga.ga_data + ga.ga_len, ", "); + ga.ga_len += 2; } - *tofree = ga.ga_data; - return ga.ga_data; + len = (int)STRLEN(arg_type); + if (ga_grow(&ga, len + 8) == FAIL) + goto failed; + if (varargs && i == type->tt_argcount - 1) + ga_concat(&ga, (char_u *)"..."); + else if (i >= type->tt_min_argcount) + *((char *)ga.ga_data + ga.ga_len++) = '?'; + ga_concat(&ga, (char_u *)arg_type); + VIM_CLEAR(arg_free); + } + if (type->tt_argcount < 0) + // any number of arguments + ga_concat(&ga, (char_u *)"..."); + + if (type->tt_member == &t_void) + STRCPY((char *)ga.ga_data + ga.ga_len, ")"); + else + { + char *ret_free; + char *ret_name = type_name(type->tt_member, &ret_free); + int len; + + len = (int)STRLEN(ret_name) + 4; + if (ga_grow(&ga, len) == FAIL) + goto failed; + STRCPY((char *)ga.ga_data + ga.ga_len, "): "); + STRCPY((char *)ga.ga_data + ga.ga_len + 3, ret_name); + vim_free(ret_free); + } + *tofree = ga.ga_data; + return ga.ga_data; failed: - vim_free(arg_free); - ga_clear(&ga); + vim_free(arg_free); + ga_clear(&ga); + return "[unknown]"; +} + +/* + * Return the name of a type. + * The result may be in allocated memory, in which case "tofree" is set. + */ + char * +type_name(type_T *type, char **tofree) +{ + char *name; + + *tofree = NULL; + if (type == NULL) return "[unknown]"; + name = vartype_name(type->tt_type); + + switch (type->tt_type) + { + case VAR_LIST: + case VAR_DICT: + return type_name_list_or_dict(name, type, tofree); + + case VAR_CLASS: + case VAR_OBJECT: + return type_name_class_or_obj(name, type, tofree); + + case VAR_FUNC: + return type_name_func(type, tofree); + + default: + break; } return name; -- cgit v1.2.3