vim-patch:9.2.0265: unnecessary restrictions for defining dictionary function names (#38524)

Problem:  unnecessary restrictions for defining dictionary function
          names
Solution: Allow defining dict function with bracket key that is not a
          valid identifier (thinca)

In Vim script, "function obj.func()" and "function obj['func']()" both
define a dictionary function.  However, the bracket form required the
key to match function naming rules (eval_isnamec), so
"function obj['foo-bar']()" failed with E475.

Assigning and calling already work: "let obj['foo-bar'] = obj.func"
and "call obj['foo-bar']()" are valid.  Only the definition was
incorrectly restricted.

Skip the identifier check when the name comes from fd_newkey (i.e. the
key was given in bracket notation).  Dictionary keys may be any string.

Supported by AI

closes: vim/vim#19833

f89662722d

Co-authored-by: thinca <thinca@gmail.com>
This commit is contained in:
zeertzjq
2026-03-28 18:51:00 +08:00
committed by GitHub
parent 9383a096eb
commit 5a7df03b42
2 changed files with 32 additions and 14 deletions

View File

@@ -2737,21 +2737,27 @@ void ex_function(exarg_T *eap)
}
if (arg != NULL && (fudi.fd_di == NULL || !tv_is_func(fudi.fd_di->di_tv))) {
char *name_base = arg;
if ((uint8_t)(*arg) == K_SPECIAL) {
name_base = vim_strchr(arg, '_');
if (name_base == NULL) {
name_base = arg + 3;
} else {
name_base++;
// When defining a dictionary function with bracket notation
// (e.g. obj['foo-bar']()), the key is a dictionary key and is not
// required to follow function naming rules. Skip the identifier
// check in that case.
if (arg != fudi.fd_newkey) {
if ((uint8_t)(*arg) == K_SPECIAL) {
name_base = vim_strchr(arg, '_');
if (name_base == NULL) {
name_base = arg + 3;
} else {
name_base++;
}
}
int i;
for (i = 0; name_base[i] != NUL && (i == 0
? eval_isnamec1(name_base[i])
: eval_isnamec(name_base[i])); i++) {}
if (name_base[i] != NUL) {
emsg_funcname(e_invarg2, arg);
goto ret_free;
}
}
int i;
for (i = 0; name_base[i] != NUL && (i == 0
? eval_isnamec1(name_base[i])
: eval_isnamec(name_base[i])); i++) {}
if (name_base[i] != NUL) {
emsg_funcname(e_invarg2, arg);
goto ret_free;
}
}
// Disallow using the g: dict.

View File

@@ -540,6 +540,18 @@ func Test_func_dict()
call assert_fails('call mydict.nonexist()', 'E716:')
endfunc
func Test_func_dict_bracket_key()
" Dictionary function can be defined with bracket notation using a key
" that does not follow function naming rules (e.g. containing a hyphen).
let obj = {}
function obj['foo-bar']() dict
return self.value
endfunction
let obj.value = 42
call assert_equal(42, obj['foo-bar']())
call assert_equal(42, call(obj['foo-bar'], []))
endfunc
func Test_func_range()
new
call setline(1, range(1, 8))