]> granicus.if.org Git - vim/commitdiff
patch 8.2.2603: Vim9: no effect if user command is also a function v8.2.2603
authorBram Moolenaar <Bram@vim.org>
Sun, 14 Mar 2021 12:21:35 +0000 (13:21 +0100)
committerBram Moolenaar <Bram@vim.org>
Sun, 14 Mar 2021 12:21:35 +0000 (13:21 +0100)
Problem:    Vim9: no effect if user command is also a function.
Solution:   Check for paren following. (closes #7960)

src/evalvars.c
src/ex_docmd.c
src/proto/evalvars.pro
src/proto/ex_docmd.pro
src/testdir/test_vim9_cmd.vim
src/version.c
src/vim9compile.c

index eefd05f367c1cb3f69d888d1aabb63f59dfe4875..79c7a9c8c4af1992de7ccb840efbf875c6d1837b 100644 (file)
@@ -2805,12 +2805,15 @@ get_script_local_ht(void)
 
 /*
  * Look for "name[len]" in script-local variables and functions.
+ * When "cmd" is TRUE it must look like a command, a function must be followed
+ * by "(" or "->".
  * Return OK when found, FAIL when not found.
  */
     int
 lookup_scriptitem(
        char_u  *name,
        size_t  len,
+       int     cmd,
        cctx_T  *dummy UNUSED)
 {
     hashtab_T  *ht = get_script_local_ht();
@@ -2845,19 +2848,26 @@ lookup_scriptitem(
     if (p != buffer)
        vim_free(p);
 
+    // Find a function, so that a following "->" works.
+    // When used as a command require "(" or "->" to follow, "Cmd" is a user
+    // command while "Cmd()" is a function call.
     if (res != OK)
     {
-       // Find a function, so that a following "->" works.  Skip "g:" before a
-       // function name.
-       // Do not check for an internal function, since it might also be a
-       // valid command, such as ":split" versuse "split()".
-       if (name[0] == 'g' && name[1] == ':')
+       p = skipwhite(name + len);
+
+       if (!cmd || name[len] == '(' || (p[0] == '-' && p[1] == '>'))
        {
-           is_global = TRUE;
-           fname = name + 2;
+           // Do not check for an internal function, since it might also be a
+           // valid command, such as ":split" versus "split()".
+           // Skip "g:" before a function name.
+           if (name[0] == 'g' && name[1] == ':')
+           {
+               is_global = TRUE;
+               fname = name + 2;
+           }
+           if (find_func(fname, is_global, NULL) != NULL)
+               res = OK;
        }
-       if (find_func(fname, is_global, NULL) != NULL)
-           res = OK;
     }
 
     return res;
@@ -3288,7 +3298,7 @@ set_var_const(
     {
        // Item not found, check if a function already exists.
        if (is_script_local && (flags & (ASSIGN_NO_DECL | ASSIGN_DECL)) == 0
-                         && lookup_scriptitem(name, STRLEN(name), NULL) == OK)
+                  && lookup_scriptitem(name, STRLEN(name), FALSE, NULL) == OK)
        {
            semsg(_(e_redefining_script_item_str), name);
            goto failed;
index c7b57ba9924ddb1863ab7242a7272b1e8d8ec6a8..477d140bde79909be0295ceb7362eee4c31cd5bb 100644 (file)
@@ -3311,7 +3311,7 @@ skip_option_env_lead(char_u *start)
 find_ex_command(
        exarg_T *eap,
        int     *full UNUSED,
-       int     (*lookup)(char_u *, size_t, cctx_T *) UNUSED,
+       int     (*lookup)(char_u *, size_t, int cmd, cctx_T *) UNUSED,
        cctx_T  *cctx UNUSED)
 {
     int                len;
@@ -3430,7 +3430,7 @@ find_ex_command(
                        || *eap->cmd == '&'
                        || *eap->cmd == '$'
                        || *eap->cmd == '@'
-                       || lookup(eap->cmd, p - eap->cmd, cctx) == OK)
+                       || lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK)
                {
                    eap->cmdidx = CMD_var;
                    return eap->cmd;
@@ -3449,7 +3449,7 @@ find_ex_command(
        // If it is an ID it might be a variable with an operator on the next
        // line, if the variable exists it can't be an Ex command.
        if (p > eap->cmd && ends_excmd(*skipwhite(p))
-               && (lookup(eap->cmd, p - eap->cmd, cctx) == OK
+               && (lookup(eap->cmd, p - eap->cmd, TRUE, cctx) == OK
                    || (ASCII_ISALPHA(eap->cmd[0]) && eap->cmd[1] == ':')))
        {
            eap->cmdidx = CMD_eval;
index 1094d4563068abfdcba41e22ee8334398e6ea721..c75f80a86378b0f4c7c862efbc02675a92440889 100644 (file)
@@ -61,7 +61,7 @@ void check_vars(char_u *name, int len);
 dictitem_T *find_var(char_u *name, hashtab_T **htp, int no_autoload);
 dictitem_T *find_var_in_ht(hashtab_T *ht, int htname, char_u *varname, int no_autoload);
 hashtab_T *get_script_local_ht(void);
-int lookup_scriptitem(char_u *name, size_t len, cctx_T *dummy);
+int lookup_scriptitem(char_u *name, size_t len, int cmd, cctx_T *dummy);
 hashtab_T *find_var_ht(char_u *name, char_u **varname);
 char_u *get_var_value(char_u *name);
 void new_script_vars(scid_T id);
index dcd9f3940cc122aab7a56d99a01f11a62fede2e2..9ac8eaf527fcd849bf61a3a2e96fe9a2701e2f02 100644 (file)
@@ -13,7 +13,7 @@ void undo_cmdmod(cmdmod_T *cmod);
 int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
 int checkforcmd(char_u **pp, char *cmd, int len);
 char_u *skip_option_env_lead(char_u *start);
-char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, cctx_T *), cctx_T *cctx);
+char_u *find_ex_command(exarg_T *eap, int *full, int (*lookup)(char_u *, size_t, int cmd, cctx_T *), cctx_T *cctx);
 int modifier_len(char_u *cmd);
 int cmd_exists(char_u *name);
 void f_fullcommand(typval_T *argvars, typval_T *rettv);
index dd6e966de1766b6c8bdf29aee1e43bfd71fd1af4..c21e55a477852337407cd301e5c8d8b81efe4a2f 100644 (file)
@@ -364,9 +364,8 @@ def Test_method_call_linebreak()
         return F()
       enddef
       def Test()
-        Foo
-          ->Bar()
-          ->setline(1)
+        Foo  ->Bar()
+             ->setline(1)
       enddef
       Test()
       assert_equal('the text', getline(1))
@@ -401,8 +400,7 @@ def Test_method_call_linebreak()
           return F()
       enddef
 
-      Foo
-         ->Bar()
+      Foo->Bar()
          ->setline(1)
   END
   CheckScriptSuccess(lines)
@@ -424,6 +422,33 @@ def Test_method_call_whitespace()
   CheckDefAndScriptSuccess(lines)
 enddef
 
+def Test_method_and_user_command()
+  var lines =<< trim END
+      vim9script
+      def Cmd()
+        g:didFunc = 1
+      enddef
+      command Cmd g:didCmd = 1
+      Cmd
+      assert_equal(1, g:didCmd)
+      Cmd()
+      assert_equal(1, g:didFunc)
+      unlet g:didFunc
+      unlet g:didCmd
+
+      def InDefFunc()
+        Cmd
+        assert_equal(1, g:didCmd)
+        Cmd()
+        assert_equal(1, g:didFunc)
+        unlet g:didFunc
+        unlet g:didCmd
+      enddef
+      InDefFunc()
+  END
+  CheckScriptSuccess(lines)
+enddef
+
 def Test_skipped_expr_linebreak()
   if 0
     var x = []
index 5f950831b7f6e18a6b98f3004d0798c3cbc38edb..14bf96bba0b0e8702d505a1a7b0c2a41fd0ef169 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2603,
 /**/
     2602,
 /**/
index 1bb30ca0cf1dfbb7cd514e62aa95c0b630f4c0e8..3fef9bd6b0bea3c2db2312075bc973521ca01fd0 100644 (file)
@@ -391,19 +391,29 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
  * imported or function.
  */
     static int
-item_exists(char_u *name, size_t len, cctx_T *cctx)
+item_exists(char_u *name, size_t len, int cmd UNUSED, cctx_T *cctx)
 {
     int            is_global;
+    char_u  *p;
 
     if (variable_exists(name, len, cctx))
        return TRUE;
 
-    // Find a function, so that a following "->" works.  Skip "g:" before a
-    // function name.
-    // Do not check for an internal function, since it might also be a
-    // valid command, such as ":split" versuse "split()".
-    is_global = (name[0] == 'g' && name[1] == ':');
-    return find_func(is_global ? name + 2 : name, is_global, cctx) != NULL;
+    // This is similar to what is in lookup_scriptitem():
+    // Find a function, so that a following "->" works.
+    // Require "(" or "->" to follow, "Cmd" is a user command while "Cmd()" is
+    // a function call.
+    p = skipwhite(name + len);
+
+    if (name[len] == '(' || (p[0] == '-' && p[1] == '>'))
+    {
+       // Do not check for an internal function, since it might also be a
+       // valid command, such as ":split" versuse "split()".
+       // Skip "g:" before a function name.
+       is_global = (name[0] == 'g' && name[1] == ':');
+       return find_func(is_global ? name + 2 : name, is_global, cctx) != NULL;
+    }
+    return FALSE;
 }
 
 /*
@@ -8429,8 +8439,8 @@ compile_def_function(
                }
            }
        }
-       p = find_ex_command(&ea, NULL, starts_with_colon ? NULL
-                   : (int (*)(char_u *, size_t, cctx_T *))item_exists, &cctx);
+       p = find_ex_command(&ea, NULL, starts_with_colon
+                                                 ? NULL : item_exists, &cctx);
 
        if (p == NULL)
        {