]> granicus.if.org Git - vim/commitdiff
patch 9.0.1338: :defcompile and :disassemble can't find class method v9.0.1338
authorBram Moolenaar <Bram@vim.org>
Tue, 21 Feb 2023 19:55:14 +0000 (19:55 +0000)
committerBram Moolenaar <Bram@vim.org>
Tue, 21 Feb 2023 19:55:14 +0000 (19:55 +0000)
Problem:    :defcompile and :disassemble can't find class method. (Ernie Rael)
Solution:   Make a class name and class.method name work. (closes #11984)

src/eval.c
src/proto/userfunc.pro
src/structs.h
src/testdir/test_vim9_class.vim
src/userfunc.c
src/version.c
src/vim.h

index a0bbabfe3c28d07fc5454a56d4cd27c6292c73eb..0d32fa046f9e93b6a5d9c0fb1840ac21196622b1 100644 (file)
@@ -1529,45 +1529,81 @@ get_lval(
            if (cl != NULL)
            {
                lp->ll_valtype = NULL;
-               int count = v_type == VAR_OBJECT ? cl->class_obj_member_count
-                                               : cl->class_class_member_count;
-               ocmember_T *members = v_type == VAR_OBJECT
-                                                    ? cl->class_obj_members
-                                                    : cl->class_class_members;
-               for (int i = 0; i < count; ++i)
+
+               if (flags & GLV_PREFER_FUNC)
                {
-                   ocmember_T *om = members + i;
-                   if (STRNCMP(om->ocm_name, key, p - key) == 0
-                                              && om->ocm_name[p - key] == NUL)
+                   // First look for a function with this name.
+                   // round 1: class functions (skipped for an object)
+                   // round 2: object methods
+                   for (int round = v_type == VAR_OBJECT ? 2 : 1;
+                                                          round <= 2; ++round)
                    {
-                       switch (om->ocm_access)
+                       int count = round == 1
+                                           ? cl->class_class_function_count
+                                           : cl->class_obj_method_count;
+                       ufunc_T **funcs = round == 1
+                                           ? cl->class_class_functions
+                                           : cl->class_obj_methods;
+                       for (int i = 0; i < count; ++i)
                        {
-                           case ACCESS_PRIVATE:
-                                   semsg(_(e_cannot_access_private_member_str),
-                                                                om->ocm_name);
-                                   return NULL;
-                           case ACCESS_READ:
-                                   if (!(flags & GLV_READ_ONLY))
-                                   {
-                                       semsg(_(e_member_is_not_writable_str),
+                           ufunc_T *fp = funcs[i];
+                           char_u *ufname = (char_u *)fp->uf_name;
+                           if (STRNCMP(ufname, key, p - key) == 0
+                                                    && ufname[p - key] == NUL)
+                           {
+                               lp->ll_ufunc = fp;
+                               lp->ll_valtype = fp->uf_func_type;
+                               round = 3;
+                               break;
+                           }
+                       }
+                   }
+               }
+
+               if (lp->ll_valtype == NULL)
+               {
+                   int count = v_type == VAR_OBJECT
+                                           ? cl->class_obj_member_count
+                                           : cl->class_class_member_count;
+                   ocmember_T *members = v_type == VAR_OBJECT
+                                           ? cl->class_obj_members
+                                           : cl->class_class_members;
+                   for (int i = 0; i < count; ++i)
+                   {
+                       ocmember_T *om = members + i;
+                       if (STRNCMP(om->ocm_name, key, p - key) == 0
+                                              && om->ocm_name[p - key] == NUL)
+                       {
+                           switch (om->ocm_access)
+                           {
+                               case ACCESS_PRIVATE:
+                                       semsg(_(e_cannot_access_private_member_str),
                                                                 om->ocm_name);
                                        return NULL;
-                                   }
-                                   break;
-                           case ACCESS_ALL:
-                                   break;
-                       }
+                               case ACCESS_READ:
+                                       if ((flags & GLV_READ_ONLY) == 0)
+                                       {
+                                           semsg(_(e_member_is_not_writable_str),
+                                                                om->ocm_name);
+                                           return NULL;
+                                       }
+                                       break;
+                               case ACCESS_ALL:
+                                       break;
+                           }
 
-                       lp->ll_valtype = om->ocm_type;
+                           lp->ll_valtype = om->ocm_type;
 
-                       if (v_type == VAR_OBJECT)
-                           lp->ll_tv = ((typval_T *)(
+                           if (v_type == VAR_OBJECT)
+                               lp->ll_tv = ((typval_T *)(
                                            lp->ll_tv->vval.v_object + 1)) + i;
-                       else
-                           lp->ll_tv = &cl->class_members_tv[i];
-                       break;
+                           else
+                               lp->ll_tv = &cl->class_members_tv[i];
+                           break;
+                       }
                    }
                }
+
                if (lp->ll_valtype == NULL)
                {
                    if (v_type == VAR_OBJECT)
index cda0389b86d003921a56bc26efac9dd840b287fd..e5d78b66f12bf69138f7094af312dacab78e70e9 100644 (file)
@@ -41,7 +41,8 @@ void user_func_error(funcerror_T error, char_u *name, int found_var);
 int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe);
 int call_simple_func(char_u *funcname, int len, typval_T *rettv);
 char_u *printable_func_name(ufunc_T *fp);
-char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type);
+char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags);
+char_u *trans_function_name_ext(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type, ufunc_T **ufunc);
 char_u *get_scriptlocal_funcname(char_u *funcname);
 char_u *alloc_printable_func_name(char_u *fname);
 char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
index bf00393f8fb3ce2aa358ddf1071c4237edb7019f..655293942263e591b5ff3152af46ba1b5177ff05 100644 (file)
@@ -4521,6 +4521,7 @@ typedef struct lval_S
     char_u     *ll_newkey;     // New key for Dict in alloc. mem or NULL.
     type_T     *ll_valtype;    // type expected for the value or NULL
     blob_T     *ll_blob;       // The Blob or NULL
+    ufunc_T    *ll_ufunc;      // The function or NULL
 } lval_T;
 
 // Structure used to save the current state.  Used when executing Normal mode
index d1f361a91971df2820d766cc2a27d50868d7b62a..c268dde50eea7a1d5be3ea8397e92672c5afb57f 100644 (file)
@@ -842,6 +842,34 @@ def Test_class_function()
   v9.CheckScriptSuccess(lines)
 enddef
 
+def Test_class_defcompile()
+  var lines =<< trim END
+      vim9script
+
+      class C
+          def Fo(i: number): string
+              return i
+          enddef
+      endclass
+
+      defcompile C.Fo
+  END
+  v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number')
+
+  lines =<< trim END
+      vim9script
+
+      class C
+          static def Fc(): number
+            return 'x'
+          enddef
+      endclass
+
+      defcompile C.Fc
+  END
+  v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string')
+enddef
+
 def Test_class_object_to_string()
   var lines =<< trim END
       vim9script
index 2575e7d06055aa389e311d58a8c567746d442c7d..9e24b79587ec8c75a4c76a19d958e5ef8e779dcf 100644 (file)
@@ -1037,8 +1037,7 @@ get_function_body(
                if (*p == '!')
                    p = skipwhite(p + 1);
                p += eval_fname_script(p);
-               vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
-                                                                 NULL, NULL));
+               vim_free(trans_function_name(&p, NULL, TRUE, 0));
                if (*skipwhite(p) == '(')
                {
                    if (nesting == MAX_FUNC_NESTING - 1)
@@ -4038,13 +4037,29 @@ list_func_head(ufunc_T *fp, int indent)
  */
     char_u *
 trans_function_name(
+    char_u     **pp,
+    int                *is_global,
+    int                skip,           // only find the end, don't evaluate
+    int                flags)
+{
+    return trans_function_name_ext(pp, is_global, skip, flags,
+           NULL, NULL, NULL, NULL);
+}
+
+/*
+ * trans_function_name() with extra arguments.
+ * "fdp", "partial", "type" and "ufunc" can be NULL.
+ */
+    char_u *
+trans_function_name_ext(
     char_u     **pp,
     int                *is_global,
     int                skip,           // only find the end, don't evaluate
     int                flags,
     funcdict_T *fdp,           // return: info about dictionary used
     partial_T  **partial,      // return: partial of a FuncRef
-    type_T     **type)         // return: type of funcref if not NULL
+    type_T     **type,         // return: type of funcref
+    ufunc_T    **ufunc)        // return: function
 {
     char_u     *name = NULL;
     char_u     *start;
@@ -4079,7 +4094,8 @@ trans_function_name(
        start += lead;
 
     // Note that TFN_ flags use the same values as GLV_ flags.
-    end = get_lval(start, NULL, &lv, FALSE, skip, flags | GLV_READ_ONLY,
+    end = get_lval(start, NULL, &lv, FALSE, skip,
+                       flags | GLV_READ_ONLY | GLV_PREFER_FUNC,
                                              lead > 2 ? 0 : FNE_CHECK_START);
     if (end == start || (vim9script && end != NULL
                                   && end[-1] == AUTOLOAD_CHAR && *end == '('))
@@ -4105,6 +4121,13 @@ trans_function_name(
        goto theend;
     }
 
+    if (lv.ll_ufunc != NULL)
+    {
+       *ufunc = lv.ll_ufunc;
+       name = vim_strsave(lv.ll_ufunc->uf_name);
+       goto theend;
+    }
+
     if (lv.ll_tv != NULL)
     {
        if (fdp != NULL)
@@ -4455,8 +4478,8 @@ save_function_name(
            CLEAR_POINTER(fudi);
     }
     else
-       saved = trans_function_name(&p, is_global, skip,
-                                                     flags, fudi, NULL, NULL);
+       saved = trans_function_name_ext(&p, is_global, skip,
+                                               flags, fudi, NULL, NULL, NULL);
     *name = p;
     return saved;
 }
@@ -5330,15 +5353,20 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
     }
     else
     {
-       // First try finding a method in a class, find_func_by_name() will give
-       // an error if the function is not found.
+       // First try finding a method in a class, trans_function_name() will
+       // give an error if the function is not found.
        ufunc = find_class_func(&arg);
        if (ufunc != NULL)
            return ufunc;
 
-       fname = trans_function_name(&arg, &is_global, FALSE,
+       fname = trans_function_name_ext(&arg, &is_global, FALSE,
                      TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DECL,
-                     NULL, NULL, NULL);
+                     NULL, NULL, NULL, &ufunc);
+       if (ufunc != NULL)
+       {
+           vim_free(fname);
+           return ufunc;
+       }
     }
     if (fname == NULL)
     {
@@ -5375,13 +5403,10 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
     void
 ex_defcompile(exarg_T *eap)
 {
-    ufunc_T    *ufunc;
-
     if (*eap->arg != NUL)
     {
        compiletype_T compile_type = CT_NONE;
-
-       ufunc = find_func_by_name(eap->arg, &compile_type);
+       ufunc_T *ufunc = find_func_by_name(eap->arg, &compile_type);
        if (ufunc != NULL)
        {
            if (func_needs_compiling(ufunc, compile_type))
@@ -5401,7 +5426,7 @@ ex_defcompile(exarg_T *eap)
            if (!HASHITEM_EMPTY(hi))
            {
                --todo;
-               ufunc = HI2UF(hi);
+               ufunc_T *ufunc = HI2UF(hi);
                if (ufunc->uf_script_ctx.sc_sid == current_sctx.sc_sid
                        && ufunc->uf_def_status == UF_TO_BE_COMPILED
                        && (ufunc->uf_flags & FC_DEAD) == 0)
@@ -5475,7 +5500,7 @@ function_exists(char_u *name, int no_deref)
     flag = TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD;
     if (no_deref)
        flag |= TFN_NO_DEREF;
-    p = trans_function_name(&nm, &is_global, FALSE, flag, NULL, NULL, NULL);
+    p = trans_function_name(&nm, &is_global, FALSE, flag);
     nm = skipwhite(nm);
 
     // Only accept "funcname", "funcname ", "funcname (..." and
@@ -5494,8 +5519,7 @@ get_expanded_name(char_u *name, int check)
     char_u     *p;
     int                is_global = FALSE;
 
-    p = trans_function_name(&nm, &is_global, FALSE,
-                                         TFN_INT|TFN_QUIET, NULL, NULL, NULL);
+    p = trans_function_name(&nm, &is_global, FALSE, TFN_INT|TFN_QUIET);
 
     if (p != NULL && *nm == NUL
                       && (!check || translated_function_exists(p, is_global)))
@@ -5631,8 +5655,8 @@ ex_delfunction(exarg_T *eap)
     int                is_global = FALSE;
 
     p = eap->arg;
-    name = trans_function_name(&p, &is_global, eap->skip, 0, &fudi,
-                                                                  NULL, NULL);
+    name = trans_function_name_ext(&p, &is_global, eap->skip, 0, &fudi,
+                                                            NULL, NULL, NULL);
     vim_free(fudi.fd_newkey);
     if (name == NULL)
     {
@@ -6166,8 +6190,8 @@ ex_call(exarg_T *eap)
        return;
     }
 
-    tofree = trans_function_name(&arg, NULL, eap->skip, TFN_INT,
-                             &fudi, &partial, vim9script ? &type : NULL);
+    tofree = trans_function_name_ext(&arg, NULL, eap->skip, TFN_INT,
+                            &fudi, &partial, vim9script ? &type : NULL, NULL);
     if (fudi.fd_newkey != NULL)
     {
        // Still need to give an error message for missing key.
index a665a2acf0811d95a9441136f6fa1385c53496cc..6b8e2bc761b42705d2e06e2f1c60c41da3dd2720 100644 (file)
@@ -695,6 +695,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1338,
 /**/
     1337,
 /**/
index d1ec26c50ed5bf7c254570f29e2c7eb6cd91d380..5193d598f8ce4bfe5636659daf33c556464d1303 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2722,6 +2722,7 @@ typedef char *(*opt_did_set_cb_T)(optset_T *args);
 #define GLV_NO_DECL    TFN_NO_DECL     // assignment without :var or :let
 #define GLV_COMPILING  TFN_COMPILING   // variable may be defined later
 #define GLV_ASSIGN_WITH_OP TFN_ASSIGN_WITH_OP // assignment with operator
+#define GLV_PREFER_FUNC        0x10000         // prefer function above variable
 
 #define DO_NOT_FREE_CNT 99999  // refcount for dict or list that should not
                                // be freed.