# HG changeset patch # User Christian Brabandt # Date 1694802605 -7200 # Node ID aba1fa2b7d1eb3ba4c8ac1e2918ae55ceea8e39c # Parent cf3186a6807ffd383b119d5ea9d3bcef7ab0a653 patch 9.0.1898: Vim9: restrict access to static vars Commit: https://github.com/vim/vim/commit/c30a90d9b2c029f794cea502f6b824f71e4876dd Author: Yegappan Lakshmanan Date: Fri Sep 15 20:14:55 2023 +0200 patch 9.0.1898: Vim9: restrict access to static vars Problem: Vim9: restrict access to static vars and methods Solution: Class members are accesible only from the class where they are defined. Based on the #13004 discussion, the following changes are made: 1) Static variables and methods are accessible only using the class name and inside the class where they are defined. 2) Static variables and methods can be used without the class name in the class where they are defined. 3) Static variables of a super class are not copied to the sub class. 4) A sub class can declare a class variable with the same name as the super class. 5) When a method or member is found during compilation, use more specific error messages. This aligns the Vim9 class variable/method implementation with the Dart implementation. Also while at it, ignore duplicate class and object methods. The access level of an object method can however be changed in a subclass. For the tests, use the new CheckSourceFailure() function instead of the CheckScriptFailure() function in the tests. closes: #13086 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -3411,16 +3411,14 @@ EXTERN char e_public_member_name_cannot_ INIT(= N_("E1332: Public member name cannot start with underscore: %s")); EXTERN char e_cannot_access_private_member_str[] INIT(= N_("E1333: Cannot access private member: %s")); -EXTERN char e_object_member_not_found_str[] - INIT(= N_("E1334: Object member not found: %s")); EXTERN char e_member_is_not_writable_str[] INIT(= N_("E1335: Member is not writable: %s")); #endif EXTERN char e_internal_error_shortmess_too_long[] INIT(= "E1336: Internal error: shortmess too long"); #ifdef FEAT_EVAL -EXTERN char e_class_member_not_found_str[] - INIT(= N_("E1337: Class member not found: %s")); +EXTERN char e_class_member_str_not_found_in_class_str[] + INIT(= N_("E1337: Class member \"%s\" not found in class \"%s\"")); EXTERN char e_member_not_found_on_class_str_str[] INIT(= N_("E1338: Member not found on class \"%s\": %s")); #endif @@ -3488,7 +3486,6 @@ EXTERN char e_cannot_access_private_meth INIT(= N_("E1366: Cannot access private method: %s")); EXTERN char e_member_str_of_interface_str_has_different_access[] INIT(= N_("E1367: Access level of member \"%s\" of interface \"%s\" is different")); - EXTERN char e_static_cannot_be_followed_by_this[] INIT(= N_("E1368: Static cannot be followed by \"this\" in a member name")); EXTERN char e_duplicate_member_str[] @@ -3501,6 +3498,16 @@ EXTERN char e_abstract_method_in_concret INIT(= N_("E1372: Abstract method \"%s\" cannot be defined in a concrete class")); EXTERN char e_abstract_method_str_not_found[] INIT(= N_("E1373: Abstract method \"%s\" is not implemented")); +EXTERN char e_class_member_str_accessible_only_inside_class_str[] + INIT(= N_("E1374: Class member \"%s\" accessible only inside class \"%s\"")); +EXTERN char e_class_member_str_accessible_only_using_class_str[] + INIT(= N_("E1375: Class member \"%s\" accessible only using class \"%s\"")); +EXTERN char e_object_member_str_accessible_only_using_object_str[] + INIT(= N_("E1376: Object member \"%s\" accessible only using class \"%s\" object")); +EXTERN char e_static_member_not_supported_in_interface[] + INIT(= N_("E1377: Static member is not supported in an interface")); +EXTERN char e_method_str_of_class_str_has_different_access[] + INIT(= N_("E1378: Access level of method \"%s\" is different in class \"%s\"")); EXTERN char e_cannot_mix_positional_and_non_positional_str[] INIT(= N_("E1400: Cannot mix positional and non-positional arguments: %s")); EXTERN char e_fmt_arg_nr_unused_str[] @@ -3521,4 +3528,4 @@ EXTERN char e_aptypes_is_null_nr_str[] INIT(= "E1408: Internal error: ap_types or ap_types[idx] is NULL: %d: %s"); EXTERN char e_interface_static_direct_access_str[] INIT(= N_("E1409: Cannot directly access interface \"%s\" static member \"%s\"")); -// E1371 - E1399 unused +// E1376 - E1399 unused diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -1596,10 +1596,7 @@ get_lval( if (lp->ll_valtype == NULL) { - if (v_type == VAR_OBJECT) - semsg(_(e_object_member_not_found_str), key); - else - semsg(_(e_class_member_not_found_str), key); + member_not_found_msg(cl, v_type, key, p - key); return NULL; } } diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro --- a/src/proto/vim9class.pro +++ b/src/proto/vim9class.pro @@ -26,6 +26,8 @@ int set_ref_in_classes(int copyID); void object_created(object_T *obj); void object_cleared(object_T *obj); int object_free_nonref(int copyID); +void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); +void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); void f_instanceof(typval_T *argvars, typval_T *rettv); int class_instance_of(class_T *cl, class_T *other_cl); /* vim: set ft=c : */ diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1791,8 +1791,11 @@ struct ufunc_S def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc. int uf_dfunc_idx; // only valid if uf_def_status is UF_COMPILED - class_T *uf_class; // for object method and constructor; does not - // count for class_refcount + class_T *uf_class; // for class/object method and constructor; + // does not count for class_refcount. + // class of the object which is invoking this + // function. + class_T *uf_defclass; // class where this function is defined. garray_T uf_args; // arguments, including optional arguments garray_T uf_def_args; // default argument expressions diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -8,56 +8,56 @@ def Test_class_basic() class NotWorking endclass END - v9.CheckScriptFailure(lines, 'E1316:') + v9.CheckSourceFailure(lines, 'E1316:') lines =<< trim END vim9script class notWorking endclass END - v9.CheckScriptFailure(lines, 'E1314:') + v9.CheckSourceFailure(lines, 'E1314:') lines =<< trim END vim9script class Not@working endclass END - v9.CheckScriptFailure(lines, 'E1315:') + v9.CheckSourceFailure(lines, 'E1315:') lines =<< trim END vim9script abstract noclass Something endclass END - v9.CheckScriptFailure(lines, 'E475:') + v9.CheckSourceFailure(lines, 'E475:') lines =<< trim END vim9script abstract classy Something endclass END - v9.CheckScriptFailure(lines, 'E475:') + v9.CheckSourceFailure(lines, 'E475:') lines =<< trim END vim9script class Something endcl END - v9.CheckScriptFailure(lines, 'E1065:') + v9.CheckSourceFailure(lines, 'E1065:') lines =<< trim END vim9script class Something endclass school's out END - v9.CheckScriptFailure(lines, 'E488:') + v9.CheckSourceFailure(lines, 'E488:') lines =<< trim END vim9script class Something endclass | echo 'done' END - v9.CheckScriptFailure(lines, 'E488:') + v9.CheckSourceFailure(lines, 'E488:') lines =<< trim END vim9script @@ -65,7 +65,7 @@ def Test_class_basic() this endclass END - v9.CheckScriptFailure(lines, 'E1317:') + v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script @@ -73,7 +73,7 @@ def Test_class_basic() this. endclass END - v9.CheckScriptFailure(lines, 'E1317:') + v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script @@ -81,7 +81,7 @@ def Test_class_basic() this .count endclass END - v9.CheckScriptFailure(lines, 'E1317:') + v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script @@ -89,7 +89,7 @@ def Test_class_basic() this. count endclass END - v9.CheckScriptFailure(lines, 'E1317:') + v9.CheckSourceFailure(lines, 'E1317:') lines =<< trim END vim9script @@ -98,7 +98,7 @@ def Test_class_basic() that.count endclass END - v9.CheckScriptFailure(lines, 'E1318: Not a valid command in a class: that.count') + v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count') lines =<< trim END vim9script @@ -106,7 +106,7 @@ def Test_class_basic() this.count endclass END - v9.CheckScriptFailure(lines, 'E1022:') + v9.CheckSourceFailure(lines, 'E1022:') lines =<< trim END vim9script @@ -117,7 +117,7 @@ def Test_class_basic() endclass var obj = Something.new() END - v9.CheckScriptFailure(lines, 'E1089:') + v9.CheckSourceFailure(lines, 'E1089:') lines =<< trim END vim9script @@ -125,7 +125,7 @@ def Test_class_basic() this.count : number endclass END - v9.CheckScriptFailure(lines, 'E1059:') + v9.CheckSourceFailure(lines, 'E1059:') lines =<< trim END vim9script @@ -133,7 +133,7 @@ def Test_class_basic() this.count:number endclass END - v9.CheckScriptFailure(lines, 'E1069:') + v9.CheckSourceFailure(lines, 'E1069:') # Test for unsupported comment specifier lines =<< trim END @@ -143,7 +143,7 @@ def Test_class_basic() #{ endclass END - v9.CheckScriptFailure(lines, 'E1170:') + v9.CheckSourceFailure(lines, 'E1170:') lines =<< trim END vim9script @@ -171,7 +171,7 @@ def Test_class_basic() assert_equal('class', typename(TextPosition)) assert_equal('object', typename(pos)) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # When referencing object methods, space cannot be used after a "." lines =<< trim END @@ -184,7 +184,7 @@ def Test_class_basic() var a = A.new() var v = a. Foo() END - v9.CheckScriptFailure(lines, 'E1202:') + v9.CheckSourceFailure(lines, 'E1202:') # Using an object without specifying a method or a member variable lines =<< trim END @@ -197,7 +197,7 @@ def Test_class_basic() var a = A.new() var v = a. END - v9.CheckScriptFailure(lines, 'E15:') + v9.CheckSourceFailure(lines, 'E15:') # Error when parsing the arguments of an object method. lines =<< trim END @@ -209,7 +209,7 @@ def Test_class_basic() var a = A.new() var v = a.Foo(,) END - v9.CheckScriptFailure(lines, 'E15:') + v9.CheckSourceFailure(lines, 'E15:') lines =<< trim END vim9script @@ -220,7 +220,7 @@ def Test_class_basic() endclass var a = A.new() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_class_defined_twice() @@ -232,7 +232,7 @@ def Test_class_defined_twice() class There endclass END - v9.CheckScriptFailure(lines, 'E1041: Redefining script item: "There"') + v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "There"') # one class, reload same script twice is OK lines =<< trim END @@ -259,7 +259,7 @@ def Test_returning_null_object() var buffers = BufferList.new() echo buffers.Current() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_using_null_class() @@ -276,7 +276,7 @@ def Test_class_interface_wrong_end() this.member = 'text' endinterface END - v9.CheckScriptFailure(lines, 'E476: Invalid command: endinterface, expected endclass') + v9.CheckSourceFailure(lines, 'E476: Invalid command: endinterface, expected endclass') lines =<< trim END vim9script @@ -284,7 +284,7 @@ def Test_class_interface_wrong_end() this.member: string endclass END - v9.CheckScriptFailure(lines, 'E476: Invalid command: endclass, expected endinterface') + v9.CheckSourceFailure(lines, 'E476: Invalid command: endclass, expected endinterface') enddef def Test_object_not_set() @@ -299,7 +299,7 @@ def Test_object_not_set() var db = {'xyz': 789} echo db[state.value] END - v9.CheckScriptFailure(lines, 'E1360:') + v9.CheckSourceFailure(lines, 'E1360:') lines =<< trim END vim9script @@ -317,7 +317,7 @@ def Test_object_not_set() enddef Func() END - v9.CheckScriptFailure(lines, 'E1360:') + v9.CheckSourceFailure(lines, 'E1360:') lines =<< trim END vim9script @@ -337,7 +337,7 @@ def Test_object_not_set() var bg: Background # UNINITIALIZED echo Colorscheme.new(bg).GetBackground() END - v9.CheckScriptFailure(lines, 'E1360:') + v9.CheckSourceFailure(lines, 'E1360:') # TODO: this should not give an error but be handled at runtime lines =<< trim END @@ -356,7 +356,7 @@ def Test_object_not_set() enddef Func() END - v9.CheckScriptFailure(lines, 'E1363:') + v9.CheckSourceFailure(lines, 'E1363:') enddef def Test_null_object_assign_compare() @@ -396,7 +396,7 @@ def Test_null_object_assign_compare() o2 = null_object assert_true(o2 == null) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_class_member_initializer() @@ -432,7 +432,7 @@ def Test_class_member_initializer() '\d\+ RETURN object.*', instr) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_member_any_used_as_object() @@ -456,7 +456,7 @@ def Test_member_any_used_as_object() F(outer_obj) assert_equal(1, inner_obj.value) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -482,7 +482,51 @@ def Test_member_any_used_as_object() Test_assign_to_nested_typed_member() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) + + # Try modifying a private variable using an "any" object + lines =<< trim END + vim9script + + class Inner + this._value: string = '' + endclass + + class Outer + this.inner: any + endclass + + def F(outer: Outer) + outer.inner._value = 'b' + enddef + + var inner_obj = Inner.new('a') + var outer_obj = Outer.new(inner_obj) + F(outer_obj) + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _value') + + # Try modifying a non-existing variable using an "any" object + lines =<< trim END + vim9script + + class Inner + this.value: string = '' + endclass + + class Outer + this.inner: any + endclass + + def F(outer: Outer) + outer.inner.someval = 'b' + enddef + + var inner_obj = Inner.new('a') + var outer_obj = Outer.new(inner_obj) + F(outer_obj) + END + v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Inner": someval') enddef def Test_assignment_with_operator() @@ -508,7 +552,7 @@ def Test_assignment_with_operator() AddToFoo(f) assert_equal(23, f.x) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # do the same thing, but through an interface lines =<< trim END @@ -538,7 +582,7 @@ def Test_assignment_with_operator() AddToFoo(f) assert_equal(23, f.x) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_list_of_objects() @@ -559,7 +603,7 @@ def Test_list_of_objects() var l: list = [Foo.new()] ProcessList(l) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_expr_after_using_object() @@ -578,7 +622,7 @@ def Test_expr_after_using_object() Foo() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_class_default_new() @@ -606,7 +650,7 @@ def Test_class_default_new() assert_equal(1, pos.lnum) assert_equal(33, pos.col) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -629,7 +673,7 @@ def Test_class_default_new() assert_equal(4, chris.age) assert_equal("none", chris.education) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -644,7 +688,7 @@ def Test_class_default_new() var missing = Person.new() END - v9.CheckScriptFailure(lines, 'E119:') + v9.CheckSourceFailure(lines, 'E119:') enddef @@ -677,7 +721,7 @@ def Test_class_new_with_object_member() Check() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -699,7 +743,7 @@ def Test_class_new_with_object_member() Check() END - v9.CheckScriptFailure(lines, 'E1013:') + v9.CheckSourceFailure(lines, 'E1013:') lines =<< trim END vim9script @@ -721,7 +765,7 @@ def Test_class_new_with_object_member() Check() END - v9.CheckScriptFailure(lines, 'E1013:') + v9.CheckSourceFailure(lines, 'E1013:') enddef def Test_class_object_member_inits() @@ -738,7 +782,7 @@ def Test_class_object_member_inits() assert_equal(1, pos.col) assert_equal(2, pos.addcol) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -747,7 +791,7 @@ def Test_class_object_member_inits() this.col = 1 endclass END - v9.CheckScriptFailure(lines, 'E1022:') + v9.CheckSourceFailure(lines, 'E1022:') # If the type is not specified for a member, then it should be set during # object creation and not when defining the class. @@ -770,7 +814,7 @@ def Test_class_object_member_inits() var a = A.new() assert_equal(init_count, 2) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Test for initializing an object member with an unknown variable/type lines =<< trim END @@ -780,10 +824,11 @@ def Test_class_object_member_inits() endclass var a = A.new() END - v9.CheckScriptFailure(lines, 'E1001:') + v9.CheckSourceFailure(lines, 'E1001:') enddef -def Test_class_object_member_access() +" Test for instance variable access +def Test_instance_variable_access() var lines =<< trim END vim9script class Triple @@ -807,9 +852,9 @@ def Test_class_object_member_access() trip.three = 33 assert_equal(33, trip.three) - assert_fails('trip.four = 4', 'E1334') - END - v9.CheckScriptSuccess(lines) + assert_fails('trip.four = 4', 'E1326') + END + v9.CheckSourceSuccess(lines) # Test for a public member variable name beginning with an underscore lines =<< trim END @@ -818,7 +863,7 @@ def Test_class_object_member_access() public this._val = 10 endclass END - v9.CheckScriptFailure(lines, 'E1332:') + v9.CheckSourceFailure(lines, 'E1332:') lines =<< trim END vim9script @@ -854,7 +899,7 @@ def Test_class_object_member_access() enddef CheckCar() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -870,7 +915,7 @@ def Test_class_object_member_access() var c = MyCar.new("abc") var c = MyCar.new("def") END - v9.CheckScriptFailure(lines, 'E1041:') + v9.CheckSourceFailure(lines, 'E1041:') lines =<< trim END vim9script @@ -896,7 +941,7 @@ def Test_class_object_member_access() .Add(2) .x END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Test for "public" cannot be abbreviated lines =<< trim END @@ -905,7 +950,7 @@ def Test_class_object_member_access() pub this.val = 1 endclass END - v9.CheckScriptFailure(lines, 'E1065:') + v9.CheckSourceFailure(lines, 'E1065:') # Test for "public" keyword must be followed by "this" or "static". lines =<< trim END @@ -914,25 +959,53 @@ def Test_class_object_member_access() public val = 1 endclass END - v9.CheckScriptFailure(lines, 'E1331:') - - # Test for "static" cannot be abbreviated + v9.CheckSourceFailure(lines, 'E1331:') + + # Modify a instance variable using the class name in the script context + lines =<< trim END + vim9script + class A + public this.val = 1 + endclass + A.val = 1 + END + v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') + + # Read a instance variable using the class name in the script context lines =<< trim END vim9script - class Something - stat this.val = 1 - endclass - END - v9.CheckScriptFailure(lines, 'E1065:') - - # Test for "static" cannot be followed by "this". + class A + public this.val = 1 + endclass + var i = A.val + END + v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') + + # Modify a instance variable using the class name in a def function lines =<< trim END vim9script - class Something - static this.val = 1 - endclass - END - v9.CheckScriptFailure(lines, 'E1368: Static cannot be followed by "this" in a member name') + class A + public this.val = 1 + endclass + def T() + A.val = 1 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') + + # Read a instance variable using the class name in a def function + lines =<< trim END + vim9script + class A + public this.val = 1 + endclass + def T() + var i = A.val + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1376: Object member "val" accessible only using class "A" object') # Access from child class extending a class: lines =<< trim END @@ -941,10 +1014,6 @@ def Test_class_object_member_access() this.ro_obj_var = 10 public this.rw_obj_var = 20 this._priv_obj_var = 30 - - static ro_class_var = 40 - public static rw_class_var = 50 - static _priv_class_var = 60 endclass class B extends A @@ -956,19 +1025,89 @@ def Test_class_object_member_access() this.rw_obj_var = 0 x = this._priv_obj_var this._priv_obj_var = 0 - - x = ro_class_var - ro_class_var = 0 - x = rw_class_var - rw_class_var = 0 - x = _priv_class_var - _priv_class_var = 0 - - x = A.ro_class_var - A.ro_class_var = 0 - x = A.rw_class_var - A.rw_class_var = 0 - x = A._priv_class_var + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for class variable access +def Test_class_variable_access() + # Test for "static" cannot be abbreviated + var lines =<< trim END + vim9script + class Something + stat this.val = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1065:') + + # Test for "static" cannot be followed by "this". + lines =<< trim END + vim9script + class Something + static this.val = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1368: Static cannot be followed by "this" in a member name') + + # Test for "static" cannot be followed by "public". + lines =<< trim END + vim9script + class Something + static public val = 1 + endclass + END + v9.CheckSourceFailure(lines, 'E1022: Type or initialization required') + + # A readonly class variable cannot be modified from a child class + lines =<< trim END + vim9script + class A + static ro_class_var = 40 + endclass + + class B extends A + def Foo() + A.ro_class_var = 50 + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "ro_class_var"') + + # A private class variable cannot be accessed from a child class + lines =<< trim END + vim9script + class A + static _priv_class_var = 60 + endclass + + class B extends A + def Foo() + var i = A._priv_class_var + enddef + endclass + + var b = B.new() + b.Foo() + END + v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _priv_class_var') + + # A private class variable cannot be modified from a child class + lines =<< trim END + vim9script + class A + static _priv_class_var = 60 + endclass + + class B extends A + def Foo() A._priv_class_var = 0 enddef endclass @@ -976,7 +1115,37 @@ def Test_class_object_member_access() var b = B.new() b.Foo() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _priv_class_var') + + # Access from child class extending a class and from script context + lines =<< trim END + vim9script + class A + static ro_class_var = 10 + public static rw_class_var = 20 + static _priv_class_var = 30 + endclass + + class B extends A + def Foo() + var x: number + x = A.ro_class_var + assert_equal(10, x) + x = A.rw_class_var + assert_equal(25, x) + A.rw_class_var = 20 + assert_equal(20, A.rw_class_var) + enddef + endclass + + assert_equal(10, A.ro_class_var) + assert_equal(20, A.rw_class_var) + A.rw_class_var = 25 + assert_equal(25, A.rw_class_var) + var b = B.new() + b.Foo() + END + v9.CheckSourceSuccess(lines) enddef def Test_class_object_compare() @@ -1005,8 +1174,8 @@ def Test_class_object_compare() assert_notequal(i1, io2) END - v9.CheckScriptSuccess(class_lines + test_lines) - v9.CheckScriptSuccess( + v9.CheckSourceSuccess(class_lines + test_lines) + v9.CheckSourceSuccess( class_lines + ['def Test()'] + test_lines + ['enddef', 'Test()']) for op in ['>', '>=', '<', '<=', '=~', '!~'] @@ -1015,8 +1184,8 @@ def Test_class_object_compare() 'var i2 = Item.new()', 'echo i1 ' .. op .. ' i2', ] - v9.CheckScriptFailure(class_lines + op_lines, 'E1153: Invalid operation for object') - v9.CheckScriptFailure(class_lines + v9.CheckSourceFailure(class_lines + op_lines, 'E1153: Invalid operation for object') + v9.CheckSourceFailure(class_lines + ['def Test()'] + op_lines + ['enddef', 'Test()'], 'E1153: Invalid operation for object') endfor enddef @@ -1042,7 +1211,7 @@ def Test_object_type() t = m END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -1056,7 +1225,7 @@ def Test_object_type() var o: One = Two.new() END - v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object but got object') + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got object') lines =<< trim END vim9script @@ -1074,7 +1243,7 @@ def Test_object_type() var o: One = Two.new(5) assert_equal(5, o.GetMember()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -1094,7 +1263,7 @@ def Test_object_type() echo Fn(Num.new(4)) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_class_member() @@ -1116,7 +1285,7 @@ def Test_class_member() assert_equal(0, TextPos.counter) TextPos.AddToCounter(3) assert_equal(3, TextPos.counter) - assert_fails('echo TextPos.noSuchMember', 'E1338:') + assert_fails('echo TextPos.noSuchMember', 'E1337:') def GetCounter(): number return TextPos.counter @@ -1136,7 +1305,7 @@ def Test_class_member() TextPos.anybody += 5 assert_equal(17, TextPos.anybody) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # example in the help lines =<< trim END @@ -1155,7 +1324,7 @@ def Test_class_member() var to7 = OtherThing.new(7) assert_equal(10, OtherThing.totalSize) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # using static class member twice lines =<< trim END @@ -1172,7 +1341,7 @@ def Test_class_member() assert_equal('some text', HTML.MacroSubstitute('some text')) assert_equal('some text', HTML.MacroSubstitute('some text')) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # access private member in lambda lines =<< trim END @@ -1190,7 +1359,7 @@ def Test_class_member() var foo = Foo.new() assert_equal(5, foo.Add(5)) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # access private member in lambda body lines =<< trim END @@ -1211,7 +1380,7 @@ def Test_class_member() var foo = Foo.new() assert_equal(13, foo.Add(7)) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # check shadowing lines =<< trim END @@ -1227,7 +1396,7 @@ def Test_class_member() var s = Some.new() s.Method(7) END - v9.CheckScriptFailure(lines, 'E1340: Argument already declared in the class: count') + v9.CheckSourceFailure(lines, 'E1340: Argument already declared in the class: count') lines =<< trim END vim9script @@ -1243,7 +1412,7 @@ def Test_class_member() var s = Some.new() s.Method(7) END - v9.CheckScriptFailure(lines, 'E1341: Variable already declared in the class: count') + v9.CheckSourceFailure(lines, 'E1341: Variable already declared in the class: count') # Test for using an invalid type for a member variable lines =<< trim END @@ -1252,7 +1421,7 @@ def Test_class_member() this.val: xxx endclass END - v9.CheckScriptFailure(lines, 'E1010:') + v9.CheckSourceFailure(lines, 'E1010:') # Test for setting a member on a null object lines =<< trim END @@ -1267,7 +1436,7 @@ def Test_class_member() enddef F() END - v9.CheckScriptFailure(lines, 'E1360: Using a null object') + v9.CheckSourceFailure(lines, 'E1360: Using a null object') # Test for accessing a member on a null object lines =<< trim END @@ -1282,7 +1451,7 @@ def Test_class_member() enddef F() END - v9.CheckScriptFailure(lines, 'E1360: Using a null object') + v9.CheckSourceFailure(lines, 'E1360: Using a null object') # Test for setting a member on a null object, at script level lines =<< trim END @@ -1295,7 +1464,7 @@ def Test_class_member() obj.val = "" END # FIXME(in source): this should give E1360 as well! - v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object but got string') + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got string') # Test for accessing a member on a null object, at script level lines =<< trim END @@ -1307,7 +1476,7 @@ def Test_class_member() var obj: A echo obj.val END - v9.CheckScriptFailure(lines, 'E1360: Using a null object') + v9.CheckSourceFailure(lines, 'E1360: Using a null object') # Test for no space before or after the '=' when initializing a member # variable @@ -1317,14 +1486,14 @@ def Test_class_member() this.val: number= 10 endclass END - v9.CheckScriptFailure(lines, 'E1004:') + v9.CheckSourceFailure(lines, 'E1004:') lines =<< trim END vim9script class A this.val: number =10 endclass END - v9.CheckScriptFailure(lines, 'E1004:') + v9.CheckSourceFailure(lines, 'E1004:') # Access a non-existing member lines =<< trim END @@ -1334,7 +1503,7 @@ def Test_class_member() var a = A.new() var v = a.bar END - v9.CheckScriptFailure(lines, 'E1326:') + v9.CheckSourceFailure(lines, 'E1326: Member not found on object "A": bar') enddef func Test_class_garbagecollect() @@ -1351,7 +1520,7 @@ func Test_class_garbagecollect() call test_garbagecollect_now() echo Point.pl Point.pd END - call v9.CheckScriptSuccess(lines) + call v9.CheckSourceSuccess(lines) let lines =<< trim END vim9script @@ -1378,7 +1547,7 @@ func Test_class_garbagecollect() view = MyView.new() test_garbagecollect_now() END - call v9.CheckScriptSuccess(lines) + call v9.CheckSourceSuccess(lines) endfunc " Test interface garbage collection @@ -1432,7 +1601,7 @@ func Test_interface_garbagecollect() assert_equal(60, A.ClassFoo()) assert_equal(150, o.ObjFoo()) END - call v9.CheckScriptSuccess(lines) + call v9.CheckSourceSuccess(lines) endfunc def Test_class_function() @@ -1458,7 +1627,7 @@ def Test_class_function() var v2 = Value.new(7) assert_equal(2, Value.GetCount()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Test for cleaning up after a class definition failure when using class # functions. @@ -1470,7 +1639,7 @@ def Test_class_function() aaa endclass END - v9.CheckScriptFailure(lines, 'E1318:') + v9.CheckSourceFailure(lines, 'E1318:') enddef def Test_class_defcompile() @@ -1485,7 +1654,7 @@ def Test_class_defcompile() defcompile C.Fo END - v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected string but got number') + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected string but got number') lines =<< trim END vim9script @@ -1498,7 +1667,7 @@ def Test_class_defcompile() defcompile C.Fc END - v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected number but got string') + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected number but got string') lines =<< trim END vim9script @@ -1510,14 +1679,14 @@ def Test_class_defcompile() defcompile C.new END - v9.CheckScriptFailure(lines, 'E1370: Cannot define a "new" function as static') + v9.CheckSourceFailure(lines, 'E1370: Cannot define a "new" function as static') # Trying to compile a function using a non-existing class variable lines =<< trim END vim9script defcompile x.Foo() END - v9.CheckScriptFailure(lines, 'E475:') + v9.CheckSourceFailure(lines, 'E475:') # Trying to compile a function using a variable which is not a class lines =<< trim END @@ -1525,7 +1694,7 @@ def Test_class_defcompile() var x: number defcompile x.Foo() END - v9.CheckScriptFailure(lines, 'E475:') + v9.CheckSourceFailure(lines, 'E475:') # Trying to compile a function without specifying the name lines =<< trim END @@ -1534,7 +1703,7 @@ def Test_class_defcompile() endclass defcompile A. END - v9.CheckScriptFailure(lines, 'E475:') + v9.CheckSourceFailure(lines, 'E475:') # Trying to compile a non-existing class object member function lines =<< trim END @@ -1544,7 +1713,7 @@ def Test_class_defcompile() var a = A.new() defcompile a.Foo() END - v9.CheckScriptFailureList(lines, ['E1334:', 'E475:']) + v9.CheckSourceFailureList(lines, ['E1326:', 'E475:']) enddef def Test_class_object_to_string() @@ -1560,7 +1729,7 @@ def Test_class_object_to_string() var pos = TextPosition.new() assert_equal("object of TextPosition {lnum: 1, col: 22}", string(pos)) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_interface_basics() @@ -1572,14 +1741,14 @@ def Test_interface_basics() def GetCount(): number endinterface END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END interface SomethingWrong static count = 7 endinterface END - v9.CheckScriptFailure(lines, 'E1342:') + v9.CheckSourceFailure(lines, 'E1342:') lines =<< trim END vim9script @@ -1589,7 +1758,7 @@ def Test_interface_basics() def Method(count: number) endinterface END - v9.CheckScriptFailure(lines, 'E1340: Argument already declared in the class: count', 5) + v9.CheckSourceFailure(lines, 'E1340: Argument already declared in the class: count', 5) lines =<< trim END vim9script @@ -1601,7 +1770,7 @@ def Test_interface_basics() END # The argument name and the object member name are the same, but this is not a # problem because object members are always accessed with the "this." prefix. - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -1609,7 +1778,7 @@ def Test_interface_basics() static count = 7 endinterface END - v9.CheckScriptFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong') + v9.CheckSourceFailure(lines, 'E1343: Interface name must start with an uppercase letter: somethingWrong') lines =<< trim END vim9script @@ -1619,7 +1788,7 @@ def Test_interface_basics() def GetCount(): number endinterface END - v9.CheckScriptFailure(lines, 'E1344:') + v9.CheckSourceFailure(lines, 'E1344:') lines =<< trim END vim9script @@ -1631,7 +1800,7 @@ def Test_interface_basics() enddef endinterface END - v9.CheckScriptFailure(lines, 'E1345: Not a valid command in an interface: return 5') + v9.CheckSourceFailure(lines, 'E1345: Not a valid command in an interface: return 5') lines =<< trim END vim9script @@ -1699,7 +1868,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -1712,7 +1881,7 @@ def Test_class_implements_interface() static count: number endclass END - v9.CheckScriptFailure(lines, 'E1350:') + v9.CheckSourceFailure(lines, 'E1350:') lines =<< trim END vim9script @@ -1725,7 +1894,7 @@ def Test_class_implements_interface() static count: number endclass END - v9.CheckScriptFailure(lines, 'E1351: Duplicate interface after "implements": Some') + v9.CheckSourceFailure(lines, 'E1351: Duplicate interface after "implements": Some') lines =<< trim END vim9script @@ -1742,7 +1911,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckScriptFailure(lines, 'E1348: Member "counter" of interface "Some" not implemented') + v9.CheckSourceFailure(lines, 'E1348: Member "counter" of interface "Some" not implemented') lines =<< trim END vim9script @@ -1759,7 +1928,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckScriptFailure(lines, 'E1349: Function "Methods" of interface "Some" not implemented') + v9.CheckSourceFailure(lines, 'E1349: Function "Methods" of interface "Some" not implemented') # Check different order of members in class and interface works. lines =<< trim END @@ -1789,7 +1958,7 @@ def Test_class_implements_interface() Test() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Interface name after "extends" doesn't end in a space or NUL character lines =<< trim END @@ -1799,7 +1968,7 @@ def Test_class_implements_interface() class B extends A" endclass END - v9.CheckScriptFailure(lines, 'E1315:') + v9.CheckSourceFailure(lines, 'E1315:') # Trailing characters after a class name lines =<< trim END @@ -1807,7 +1976,7 @@ def Test_class_implements_interface() class A bbb endclass END - v9.CheckScriptFailure(lines, 'E488:') + v9.CheckSourceFailure(lines, 'E488:') # using "implements" with a non-existing class lines =<< trim END @@ -1815,7 +1984,7 @@ def Test_class_implements_interface() class A implements B endclass END - v9.CheckScriptFailure(lines, 'E1346:') + v9.CheckSourceFailure(lines, 'E1346:') # using "implements" with a regular class lines =<< trim END @@ -1825,7 +1994,7 @@ def Test_class_implements_interface() class B implements A endclass END - v9.CheckScriptFailure(lines, 'E1347:') + v9.CheckSourceFailure(lines, 'E1347:') # using "implements" with a variable lines =<< trim END @@ -1834,7 +2003,7 @@ def Test_class_implements_interface() class A implements T endclass END - v9.CheckScriptFailure(lines, 'E1347:') + v9.CheckSourceFailure(lines, 'E1347:') # all the class methods in an "interface" should be implemented lines =<< trim END @@ -1845,7 +2014,7 @@ def Test_class_implements_interface() class B implements A endclass END - v9.CheckScriptFailure(lines, 'E1349:') + v9.CheckSourceFailure(lines, 'E1349:') # implements should be followed by a white space lines =<< trim END @@ -1855,7 +2024,7 @@ def Test_class_implements_interface() class B implements A; endclass END - v9.CheckScriptFailure(lines, 'E1315:') + v9.CheckSourceFailure(lines, 'E1315:') lines =<< trim END vim9script @@ -1871,7 +2040,7 @@ def Test_class_implements_interface() static matching: bool endclass END - v9.CheckScriptFailure(lines, 'E1406: Member "not_matching": type mismatch, expected number but got string') + v9.CheckSourceFailure(lines, 'E1406: Member "not_matching": type mismatch, expected number but got string') lines =<< trim END vim9script @@ -1884,7 +2053,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckScriptFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string') + v9.CheckSourceFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(number): string') lines =<< trim END vim9script @@ -1897,7 +2066,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckScriptFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool') + v9.CheckSourceFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(bool): bool') lines =<< trim END vim9script @@ -1910,7 +2079,7 @@ def Test_class_implements_interface() enddef endclass END - v9.CheckScriptFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list): bool') + v9.CheckSourceFailure(lines, 'E1407: Method "IsEven": type mismatch, expected func(number): bool but got func(number, ...list): bool') # access superclass interface members from subclass, mix variable order lines =<< trim END @@ -1939,8 +2108,6 @@ def Test_class_implements_interface() class B extends A def new() - svar1 = 21 - svar2 = 22 this.mvar1 = 121 this.mvar2 = 122 enddef @@ -1948,8 +2115,6 @@ def Test_class_implements_interface() class C extends B def new() - svar1 = 31 - svar2 = 32 this.mvar1 = 131 this.mvar2 = 132 enddef @@ -1967,7 +2132,7 @@ def Test_class_implements_interface() assert_equal([121, 122], F2(ob)) assert_equal([131, 132], F2(oc)) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Access superclass interface members from subclass, mix variable order. # Two interfaces, one on A, one on B; each has both kinds of variables @@ -2007,8 +2172,6 @@ def Test_class_implements_interface() public this.mvar3: number public this.mvar4: number def new() - svar1 = 21 - svar2 = 22 svar3 = 23 svar4 = 24 this.mvar1 = 121 @@ -2021,10 +2184,6 @@ def Test_class_implements_interface() class C extends B public static svar5: number def new() - svar1 = 31 - svar2 = 32 - svar3 = 33 - svar4 = 34 svar5 = 1001 this.mvar1 = 131 this.mvar2 = 132 @@ -2049,7 +2208,7 @@ def Test_class_implements_interface() assert_equal([[121, 122], [123, 124]], [F2(ob), F4(ob)]) assert_equal([[131, 132], [133, 134]], [F2(oc), F4(oc)]) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_call_interface_method() @@ -2074,7 +2233,7 @@ def Test_call_interface_method() assert_equal('child', g:result) unlet g:result END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2099,7 +2258,7 @@ def Test_call_interface_method() assert_equal('child', g:result) unlet g:result END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # method of interface returns a value lines =<< trim END @@ -2125,7 +2284,7 @@ def Test_call_interface_method() assert_equal('child/resource', g:result) unlet g:result END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2152,7 +2311,7 @@ def Test_call_interface_method() assert_equal('child/resource', g:result) unlet g:result END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # No class that implements the interface. @@ -2170,7 +2329,7 @@ def Test_call_interface_method() defcompile END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_class_used_as_type() @@ -2187,7 +2346,7 @@ def Test_class_used_as_type() assert_equal(2, p.x) assert_equal(33, p.y) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2206,7 +2365,7 @@ def Test_class_used_as_type() var hx = p assert_equal(2, hx.x) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2219,7 +2378,7 @@ def Test_class_used_as_type() var p: Point p = 'text' END - v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object but got string') + v9.CheckSourceFailure(lines, 'E1012: Type mismatch; expected object but got string') enddef def Test_class_extends() @@ -2243,7 +2402,7 @@ def Test_class_extends() assert_equal(1, o.GetOne()) assert_equal(3, o.GetTotal()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2257,7 +2416,7 @@ def Test_class_extends() assert_equal(3, o.one) assert_equal(44, o.two) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2268,7 +2427,7 @@ def Test_class_extends() this.two = 2 endclass END - v9.CheckScriptFailure(lines, 'E1352: Duplicate "extends"') + v9.CheckSourceFailure(lines, 'E1352: Duplicate "extends"') lines =<< trim END vim9script @@ -2276,7 +2435,7 @@ def Test_class_extends() this.two = 2 endclass END - v9.CheckScriptFailure(lines, 'E1353: Class name not found: BaseClass') + v9.CheckSourceFailure(lines, 'E1353: Class name not found: BaseClass') lines =<< trim END vim9script @@ -2285,7 +2444,7 @@ def Test_class_extends() this.two = 2 endclass END - v9.CheckScriptFailure(lines, 'E1354: Cannot extend SomeVar') + v9.CheckSourceFailure(lines, 'E1354: Cannot extend SomeVar') lines =<< trim END vim9script @@ -2306,7 +2465,7 @@ def Test_class_extends() var o = Child.new('John', 42) assert_equal('John: 42', o.ToString()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2320,7 +2479,7 @@ def Test_class_extends() enddef endclass END - v9.CheckScriptFailure(lines, 'E1355: Duplicate function: ToString') + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: ToString') lines =<< trim END vim9script @@ -2333,7 +2492,7 @@ def Test_class_extends() var o = Child.new(42) echo o.ToString() END - v9.CheckScriptFailure(lines, 'E1356:') + v9.CheckSourceFailure(lines, 'E1356:') lines =<< trim END vim9script @@ -2350,7 +2509,7 @@ def Test_class_extends() enddef echo ToString() END - v9.CheckScriptFailure(lines, 'E1357:') + v9.CheckSourceFailure(lines, 'E1357:') lines =<< trim END vim9script @@ -2363,7 +2522,7 @@ def Test_class_extends() var o = Child.new(42) echo o.ToString() END - v9.CheckScriptFailure(lines, 'E1358:') + v9.CheckSourceFailure(lines, 'E1358:') lines =<< trim END vim9script @@ -2384,7 +2543,7 @@ def Test_class_extends() var o = Child.new('John', 42) assert_equal('Base class: 42', o.ToString()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2401,7 +2560,7 @@ def Test_class_extends() endclass var c = Child.new() END - v9.CheckScriptFailure(lines, 'E1325: Method not found on class "Child": new(') + v9.CheckSourceFailure(lines, 'E1375: Class member "new" accessible only using class "Child"') # base class with more than one object member lines =<< trim END @@ -2421,7 +2580,7 @@ def Test_class_extends() var v = Success.new('asdf') assert_equal("object of Success {success: true, value: 'asdf'}", string(v)) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # class name after "extends" doesn't end in a space or NUL character lines =<< trim END @@ -2431,7 +2590,7 @@ def Test_class_extends() class B extends A" endclass END - v9.CheckScriptFailure(lines, 'E1315:') + v9.CheckSourceFailure(lines, 'E1315:') enddef def Test_using_base_class() @@ -2470,7 +2629,7 @@ def Test_using_base_class() With(ChildEE.new()) assert_equal('42/finally/exit', g:result) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) unlet g:result # Using super, Child invokes Base method which has optional arg. #12471 @@ -2493,7 +2652,7 @@ def Test_using_base_class() var obj = Child.new() assert_equal(true, obj.success) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef @@ -2536,7 +2695,7 @@ def Test_abstract_class() assert_equal('Peter', p.name) assert_equal(42, p.age) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -2548,14 +2707,14 @@ def Test_abstract_class() endclass var p = Base.new('Peter') END - v9.CheckScriptFailure(lines, 'E1325: Method not found on class "Base": new(') + v9.CheckSourceFailure(lines, 'E1325: Method not found on class "Base": new') lines =<< trim END abstract class Base this.name: string endclass END - v9.CheckScriptFailure(lines, 'E1316:') + v9.CheckSourceFailure(lines, 'E1316:') # Abstract class cannot have a "new" function lines =<< trim END @@ -2565,7 +2724,7 @@ def Test_abstract_class() enddef endclass END - v9.CheckScriptFailure(lines, 'E1359:') + v9.CheckSourceFailure(lines, 'E1359:') enddef def Test_closure_in_class() @@ -2583,7 +2742,7 @@ def Test_closure_in_class() Foo.new() assert_equal(['A'], g:result) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_call_constructor_from_legacy() @@ -2606,7 +2765,7 @@ def Test_call_constructor_from_legacy() legacy call p.new() assert_equal('true', newCalled) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_defer_with_object() @@ -2635,7 +2794,7 @@ def Test_defer_with_object() }) assert_equal('entered/called/exited', g:result) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) unlet g:result lines =<< trim END @@ -2672,7 +2831,7 @@ def Test_defer_with_object() }) assert_equal('entered-child/called/exited-child', g:result) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) unlet g:result enddef @@ -2712,7 +2871,7 @@ def Test_extends_method_crashes_vim() p.Set(true) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for calling a method in a class that is extended @@ -2747,7 +2906,7 @@ def Test_call_method_in_extended_class() assert_true(prop_init_called) assert_true(prop_register_called) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_instanceof() @@ -2794,7 +2953,7 @@ def Test_instanceof() enddef Foo() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for calling a method in the parent class that is extended partially. @@ -2832,7 +2991,7 @@ def Test_call_method_in_parent_class() var foo2 = Foo.new() var l = Stack(foo1, foo2) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for calling methods from three levels of classes @@ -2906,7 +3065,7 @@ def Test_multi_level_method_call() assert_equal(0, B_func3) assert_equal(3, C_func3) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for using members from three levels of classes @@ -2949,7 +3108,7 @@ def Test_multi_level_member_access() assert_equal(2, cobj.val2) assert_equal(1, cobj.val3) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test expansion of with class methods. @@ -2973,7 +3132,7 @@ def Test_stack_expansion_with_methods() F() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test the return type of the new() constructor @@ -3006,7 +3165,7 @@ def Test_new_return_type() enddef F() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # new() uses the default return type and an empty 'return' statement lines =<< trim END @@ -3037,7 +3196,7 @@ def Test_new_return_type() enddef F() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # new() uses "any" return type and returns "this" lines =<< trim END @@ -3054,7 +3213,7 @@ def Test_new_return_type() enddef endclass END - v9.CheckScriptFailure(lines, 'E1365:') + v9.CheckSourceFailure(lines, 'E1365:') # new() uses 'Dict' return type and returns a Dict lines =<< trim END @@ -3072,7 +3231,7 @@ def Test_new_return_type() var c = C.new() assert_equal('object', typename(c)) END - v9.CheckScriptFailure(lines, 'E1365:') + v9.CheckSourceFailure(lines, 'E1365:') enddef " Test for checking a member initialization type at run time. @@ -3098,7 +3257,7 @@ def Test_runtime_type_check_for_member_i var c1 = C.new() var c2 = C.new() END - v9.CheckScriptFailure(lines, 'E1012:') + v9.CheckSourceFailure(lines, 'E1012:') enddef " Test for locking a variable referring to an object and reassigning to another @@ -3135,7 +3294,7 @@ def Test_object_lockvar() G() assert_equal(2, current.val) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for a private object method @@ -3152,7 +3311,7 @@ def Test_private_object_method() var a = A.new() a._Foo() END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Try calling a private method using an object (from a def function) lines =<< trim END @@ -3169,7 +3328,7 @@ def Test_private_object_method() enddef T() END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Use a private method from another object method (in script context) lines =<< trim END @@ -3186,7 +3345,7 @@ def Test_private_object_method() var a = A.new() assert_equal(1234, a.Bar()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Use a private method from another object method (def function context) lines =<< trim END @@ -3206,7 +3365,7 @@ def Test_private_object_method() enddef T() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Try calling a private method without the "this" prefix lines =<< trim END @@ -3223,7 +3382,7 @@ def Test_private_object_method() var a = A.new() a.Bar() END - v9.CheckScriptFailure(lines, 'E117: Unknown function: _Foo') + v9.CheckSourceFailure(lines, 'E117: Unknown function: _Foo') # Try calling a private method using the class name lines =<< trim END @@ -3236,7 +3395,7 @@ def Test_private_object_method() endclass A._Foo() END - v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo') # Try to use "public" keyword when defining a private method lines =<< trim END @@ -3249,7 +3408,7 @@ def Test_private_object_method() var a = A.new() a._Foo() END - v9.CheckScriptFailure(lines, 'E1331: Public must be followed by "this" or "static"') + v9.CheckSourceFailure(lines, 'E1331: Public must be followed by "this" or "static"') # Define two private methods with the same name lines =<< trim END @@ -3263,7 +3422,7 @@ def Test_private_object_method() endclass var a = A.new() END - v9.CheckScriptFailure(lines, 'E1355: Duplicate function: _Foo') + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') # Define a private method and a object method with the same name lines =<< trim END @@ -3277,7 +3436,7 @@ def Test_private_object_method() endclass var a = A.new() END - v9.CheckScriptFailure(lines, 'E1355: Duplicate function: Foo') + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo') # Define an object method and a private method with the same name lines =<< trim END @@ -3291,7 +3450,7 @@ def Test_private_object_method() endclass var a = A.new() END - v9.CheckScriptFailure(lines, 'E1355: Duplicate function: _Foo') + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') # Call a public method and a private method from a private method lines =<< trim END @@ -3315,7 +3474,7 @@ def Test_private_object_method() var a = A.new() a.T() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Try calling a private method from another class lines =<< trim END @@ -3335,7 +3494,7 @@ def Test_private_object_method() var b = B.new() b.Foo() END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Call a private object method from a child class object method lines =<< trim END @@ -3357,7 +3516,7 @@ def Test_private_object_method() var c = C.new() assert_equal(1234, c.Baz()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Call a private object method from a child class object lines =<< trim END @@ -3378,7 +3537,7 @@ def Test_private_object_method() var c = C.new() assert_equal(1234, c._Foo()) END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Using "_" prefix in a method name should fail outside of a class lines =<< trim END @@ -3388,7 +3547,7 @@ def Test_private_object_method() enddef var a = _Foo() END - v9.CheckScriptFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number') + v9.CheckSourceFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number') enddef " Test for an private class method @@ -3404,7 +3563,7 @@ def Test_private_class_method() endclass A._Foo() END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Try calling a class private method (from a def function) lines =<< trim END @@ -3420,7 +3579,7 @@ def Test_private_class_method() enddef T() END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Try calling a class private method using an object (at the script level) lines =<< trim END @@ -3434,7 +3593,7 @@ def Test_private_class_method() var a = A.new() a._Foo() END - v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo') # Try calling a class private method using an object (from a def function) lines =<< trim END @@ -3451,7 +3610,7 @@ def Test_private_class_method() enddef T() END - v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo') # Use a class private method from an object method lines =<< trim END @@ -3468,7 +3627,7 @@ def Test_private_class_method() var a = A.new() a.Bar() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Use a class private method from another class private method lines =<< trim END @@ -3488,7 +3647,7 @@ def Test_private_class_method() var a = A.new() a.Bar() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Declare a class method and a class private method with the same name lines =<< trim END @@ -3502,7 +3661,7 @@ def Test_private_class_method() endclass var a = A.new() END - v9.CheckScriptFailure(lines, 'E1355: Duplicate function: Foo') + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo') # Try calling a class private method from another class lines =<< trim END @@ -3521,7 +3680,7 @@ def Test_private_class_method() var b = B.new() assert_equal(1234, b.Foo()) END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Call a private class method from a child class object method lines =<< trim END @@ -3543,7 +3702,7 @@ def Test_private_class_method() var c = C.new() assert_equal(1234, c.Baz()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Call a private class method from a child class private class method lines =<< trim END @@ -3564,7 +3723,7 @@ def Test_private_class_method() endclass assert_equal(1234, C.Baz()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Call a private class method from a child class object lines =<< trim END @@ -3585,7 +3744,7 @@ def Test_private_class_method() var c = C.new() assert_equal(1234, C._Foo()) END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1325: Method not found on class "C": _Foo') enddef " Test for an interface private object_method @@ -3607,7 +3766,7 @@ def Test_interface_private_object_method var a = A.new() assert_equal(1234, a.Bar()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Call an interface private class method (script context) lines =<< trim END @@ -3623,7 +3782,7 @@ def Test_interface_private_object_method var a = A.new() assert_equal(1234, a._Foo()) END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Call an interface private class method (def context) lines =<< trim END @@ -3642,7 +3801,7 @@ def Test_interface_private_object_method enddef T() END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()') # Implement an interface private object method as a private class method lines =<< trim END @@ -3656,7 +3815,7 @@ def Test_interface_private_object_method enddef endclass END - v9.CheckScriptFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented') + v9.CheckSourceFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented') enddef " Test for an interface private class method @@ -3678,7 +3837,7 @@ def Test_interface_private_class_method( var a = A.new() assert_equal(1234, a.Bar()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Call an interface private class method (script context) lines =<< trim END @@ -3693,7 +3852,7 @@ def Test_interface_private_class_method( endclass assert_equal(1234, A._Foo()) END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo())') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo())') # Call an interface private class method (def context) lines =<< trim END @@ -3711,7 +3870,7 @@ def Test_interface_private_class_method( enddef T() END - v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo())') + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo())') # Implement an interface private class method as a private object method lines =<< trim END @@ -3725,7 +3884,7 @@ def Test_interface_private_class_method( enddef endclass END - v9.CheckScriptFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented') + v9.CheckSourceFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented') enddef " Test for using the return value of a class/object method as a function @@ -3751,7 +3910,7 @@ def Test_objmethod_funcarg() var t = C.new() Baz(t) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) lines =<< trim END vim9script @@ -3772,7 +3931,7 @@ def Test_objmethod_funcarg() Baz() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_static_inheritence() @@ -3797,21 +3956,17 @@ def Test_static_inheritence() class B extends A def new() - _svar = 2 this._mvar = 102 enddef endclass class C extends B def new() - _svar = 3 this._mvar = 103 enddef def AccessPrivateStaticThroughClassName(): number assert_equal(1, A._svar) - assert_equal(2, B._svar) - assert_equal(3, C._svar) return 444 enddef endclass @@ -3823,14 +3978,14 @@ def Test_static_inheritence() assert_equal(102, ob.AccessObject()) assert_equal(103, oc.AccessObject()) - assert_equal(444, oc.AccessPrivateStaticThroughClassName()) + assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access private member: _svar') # verify object properly resolves to correct static assert_equal(1, oa.AccessStaticThroughObject()) - assert_equal(2, ob.AccessStaticThroughObject()) - assert_equal(3, oc.AccessStaticThroughObject()) - END - v9.CheckScriptSuccess(lines) + assert_equal(1, ob.AccessStaticThroughObject()) + assert_equal(1, oc.AccessStaticThroughObject()) + END + v9.CheckSourceSuccess(lines) enddef " Test for declaring duplicate object and class members @@ -3843,7 +3998,7 @@ def Test_dup_member_variable() this.val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate private member variable lines =<< trim END @@ -3853,7 +4008,7 @@ def Test_dup_member_variable() this._val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate public member variable lines =<< trim END @@ -3863,7 +4018,7 @@ def Test_dup_member_variable() public this.val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate private member variable lines =<< trim END @@ -3873,7 +4028,7 @@ def Test_dup_member_variable() this._val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate public and private member variable lines =<< trim END @@ -3883,7 +4038,7 @@ def Test_dup_member_variable() public this.val = 10 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate class member variable lines =<< trim END @@ -3893,7 +4048,7 @@ def Test_dup_member_variable() static _s: string = "def" endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _s') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _s') # Duplicate public and private class member variable lines =<< trim END @@ -3903,7 +4058,7 @@ def Test_dup_member_variable() static _s: string = "def" endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _s') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _s') # Duplicate class and object member variable lines =<< trim END @@ -3918,7 +4073,7 @@ def Test_dup_member_variable() assert_equal(10, C.val) assert_equal(20, c.val) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Duplicate object member variable in a derived class lines =<< trim END @@ -3932,7 +4087,7 @@ def Test_dup_member_variable() this.val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Duplicate object private member variable in a derived class lines =<< trim END @@ -3946,7 +4101,7 @@ def Test_dup_member_variable() this._val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate object private member variable in a derived class lines =<< trim END @@ -3960,7 +4115,7 @@ def Test_dup_member_variable() this._val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: _val') # Duplicate object member variable in a derived class lines =<< trim END @@ -3974,63 +4129,7 @@ def Test_dup_member_variable() this.val = 20 endclass END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') - - # Duplicate class member variable in a derived class - lines =<< trim END - vim9script - class A - static val = 10 - endclass - class B extends A - endclass - class C extends B - static val = 20 - endclass - END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') - - # Duplicate private class member variable in a derived class - lines =<< trim END - vim9script - class A - static _val = 10 - endclass - class B extends A - endclass - class C extends B - static _val = 20 - endclass - END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') - - # Duplicate private class member variable in a derived class - lines =<< trim END - vim9script - class A - static val = 10 - endclass - class B extends A - endclass - class C extends B - static _val = 20 - endclass - END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: _val') - - # Duplicate class member variable in a derived class - lines =<< trim END - vim9script - class A - static _val = 10 - endclass - class B extends A - endclass - class C extends B - static val = 20 - endclass - END - v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val') + v9.CheckSourceFailure(lines, 'E1369: Duplicate member: val') # Two member variables with a common prefix lines =<< trim END @@ -4040,7 +4139,7 @@ def Test_dup_member_variable() public static svar: number endclass END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef def Test_interface_static_member_access() @@ -4058,7 +4157,7 @@ def Test_interface_static_member_access( endclass C.new().F() END - v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') + v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') # In a class cannot write to interface static lines =<< trim END @@ -4074,7 +4173,7 @@ def Test_interface_static_member_access( endclass C.new().F() END - v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') + v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') # In a def cannot read from interface static lines =<< trim END @@ -4087,7 +4186,7 @@ def Test_interface_static_member_access( enddef F() END - v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') + v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') # In a def cannot write to interface static lines =<< trim END @@ -4100,7 +4199,7 @@ def Test_interface_static_member_access( enddef F() END - v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') + v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "num"') # script level cannot read interface static lines =<< trim END @@ -4111,7 +4210,7 @@ def Test_interface_static_member_access( var x = I.s_var1 END - v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" static member "s_var1"') + v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "s_var1"') # script level cannot write interface static lines =<< trim END @@ -4122,7 +4221,7 @@ def Test_interface_static_member_access( I.s_var1 = 3 END - v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" static member "I.s_var1 = 3"') + v9.CheckSourceFailure(lines, 'E1409: Cannot directly access interface "I" static member "I.s_var1 = 3"') enddef @@ -4158,7 +4257,7 @@ def Test_static_member_access_outside_cl assert_equal(11, F1()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for accessing a private member outside a class in a def function @@ -4178,7 +4277,7 @@ def Test_private_member_access_outside_c enddef T() END - v9.CheckScriptFailure(lines, 'E1333: Cannot access private member: _val') + v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val') # access a non-existing private object member variable lines =<< trim END @@ -4192,7 +4291,7 @@ def Test_private_member_access_outside_c enddef T() END - v9.CheckScriptFailure(lines, 'E1089: Unknown variable: _a = 1') + v9.CheckSourceFailure(lines, 'E1089: Unknown variable: _a') # private static member variable lines =<< trim END @@ -4206,7 +4305,7 @@ def Test_private_member_access_outside_c enddef T() END - v9.CheckScriptFailure(lines, 'E1326: Member not found on object "A": _val') + v9.CheckSourceFailure(lines, 'E1375: Class member "_val" accessible only using class "A"') # private static member variable lines =<< trim END @@ -4220,8 +4319,7 @@ def Test_private_member_access_outside_c enddef T() END - # TODO: wrong error, should be about private member - v9.CheckScriptFailure(lines, 'E1089: Unknown variable') + v9.CheckSourceFailure(lines, 'E1374: Class member "_val" accessible only inside class "A"') # private static class variable lines =<< trim END @@ -4234,7 +4332,7 @@ def Test_private_member_access_outside_c enddef T() END - v9.CheckScriptFailure(lines, 'E1333: Cannot access private member: _val') + v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val') # private static class variable lines =<< trim END @@ -4247,7 +4345,7 @@ def Test_private_member_access_outside_c enddef T() END - v9.CheckScriptFailure(lines, 'E1333: Cannot access private member: _val') + v9.CheckSourceFailure(lines, 'E1333: Cannot access private member: _val') enddef " Test for changing the member access of an interface in a implementation class @@ -4261,7 +4359,7 @@ def Test_change_interface_member_access( this.val = 10 endclass END - v9.CheckScriptFailure(lines, 'E1367: Access level of member "val" of interface "A" is different') + v9.CheckSourceFailure(lines, 'E1367: Access level of member "val" of interface "A" is different') lines =<< trim END vim9script @@ -4272,7 +4370,7 @@ def Test_change_interface_member_access( public this.val = 10 endclass END - v9.CheckScriptFailure(lines, 'E1367: Access level of member "val" of interface "A" is different') + v9.CheckSourceFailure(lines, 'E1367: Access level of member "val" of interface "A" is different') enddef " Test for trying to change a readonly member from a def function @@ -4288,7 +4386,7 @@ def Test_readonly_member_change_in_def_f enddef T() END - v9.CheckScriptFailure(lines, 'E46: Cannot change read-only variable "val"') + v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "val"') enddef " Test for reading and writing a class member from a def function @@ -4312,7 +4410,7 @@ def Test_modify_class_member_from_def_fu enddef T() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for accessing a class member variable using an object @@ -4337,7 +4435,7 @@ def Test_class_variable_access_using_obj enddef Foo() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Cannot read from a class variable using an object in script context lines =<< trim END @@ -4350,7 +4448,7 @@ def Test_class_variable_access_using_obj var a = A.new() echo a.svar2 END - v9.CheckScriptFailure(lines, 'E1326: Member not found on object "A": svar2') + v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"') # Cannot write to a class variable using an object in script context lines =<< trim END @@ -4363,7 +4461,7 @@ def Test_class_variable_access_using_obj var a = A.new() a.svar2 = [2] END - v9.CheckScriptFailure(lines, 'E1334: Object member not found: svar2 = [2]') + v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"') # Cannot read from a class variable using an object in def method context lines =<< trim END @@ -4379,7 +4477,7 @@ def Test_class_variable_access_using_obj enddef T() END - v9.CheckScriptFailure(lines, 'E1326: Member not found on object "A": svar2') + v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"') # Cannot write to a class variable using an object in def method context lines =<< trim END @@ -4395,7 +4493,7 @@ def Test_class_variable_access_using_obj enddef T() END - v9.CheckScriptFailure(lines, 'E1089: Unknown variable: svar2 = [2]') + v9.CheckSourceFailure(lines, 'E1374: Class member "svar2" accessible only inside class "A"') enddef " Test for using a interface method using a child object @@ -4431,7 +4529,7 @@ def Test_interface_method_from_child() T1(c) T2(c) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) enddef " Test for using an interface method using a child object when it is overridden @@ -4472,7 +4570,7 @@ enddef " T1(c) " T2(c) " END -" v9.CheckScriptSuccess(lines) +" v9.CheckSourceSuccess(lines) " enddef " Test for abstract methods @@ -4498,7 +4596,7 @@ def Test_abstract_method() var b = B.new() assert_equal([10, 20, 30], [b.M1(), b.M2(), b.M3()]) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Don't define an abstract method lines =<< trim END @@ -4509,7 +4607,7 @@ def Test_abstract_method() class B extends A endclass END - v9.CheckScriptFailure(lines, 'E1373: Abstract method "Foo" is not implemented') + v9.CheckSourceFailure(lines, 'E1373: Abstract method "Foo" is not implemented') # Use abstract method in a concrete class lines =<< trim END @@ -4520,7 +4618,7 @@ def Test_abstract_method() class B extends A endclass END - v9.CheckScriptFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class') + v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class') # Use abstract method in an interface lines =<< trim END @@ -4531,7 +4629,7 @@ def Test_abstract_method() class B implements A endclass END - v9.CheckScriptFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class') + v9.CheckSourceFailure(lines, 'E1372: Abstract method "abstract def Foo()" cannot be defined in a concrete class') # Abbreviate the "abstract" keyword lines =<< trim END @@ -4540,7 +4638,7 @@ def Test_abstract_method() abs def Foo() endclass END - v9.CheckScriptFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()') + v9.CheckSourceFailure(lines, 'E1065: Command cannot be shortened: abs def Foo()') # Use "abstract" with a member variable lines =<< trim END @@ -4549,7 +4647,7 @@ def Test_abstract_method() abstract this.val = 10 endclass END - v9.CheckScriptFailure(lines, 'E1371: Abstract must be followed by "def" or "static"') + v9.CheckSourceFailure(lines, 'E1371: Abstract must be followed by "def" or "static"') # Use a static abstract method lines =<< trim END @@ -4564,7 +4662,7 @@ def Test_abstract_method() endclass assert_equal(4, B.Foo()) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Type mismatch between abstract method and concrete method lines =<< trim END @@ -4578,7 +4676,7 @@ def Test_abstract_method() enddef endclass END - v9.CheckScriptFailure(lines, 'E1407: Method "Foo": type mismatch, expected func(string, number): list but got func(number, string): list') + v9.CheckSourceFailure(lines, 'E1407: Method "Foo": type mismatch, expected func(string, number): list but got func(number, string): list') # Use an abstract class to invoke an abstract method # FIXME: This should fail @@ -4589,7 +4687,7 @@ def Test_abstract_method() endclass A.Foo() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # Invoke an abstract method from a def function lines =<< trim END @@ -4608,7 +4706,31 @@ def Test_abstract_method() var b = B.new() Bar(b) END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) +enddef + +" Test for calling a class method from a subclass +def Test_class_method_call_from_subclass() + # class method call from a subclass + var lines =<< trim END + vim9script + + class A + static def Foo() + echo "foo" + enddef + endclass + + class B extends A + def Bar() + Foo() + enddef + endclass + + var b = B.new() + b.Bar() + END + v9.CheckSourceFailure(lines, 'E1374: Class member "Foo" accessible only inside class "A"') enddef " Test for calling a class method using an object in a def function context and @@ -4638,7 +4760,7 @@ def Test_class_method_call_using_object( a.Bar() T() END - v9.CheckScriptSuccess(lines) + v9.CheckSourceSuccess(lines) # script context lines =<< trim END @@ -4652,7 +4774,7 @@ def Test_class_method_call_using_object( var a = A.new() assert_equal('foo', a.Foo()) END - v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": Foo()') + v9.CheckSourceFailure(lines, 'E1375: Class member "Foo" accessible only using class "A"') # def function context lines =<< trim END @@ -4669,7 +4791,407 @@ def Test_class_method_call_using_object( enddef T() END - v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": Foo()') + v9.CheckSourceFailure(lines, 'E1375: Class member "Foo" accessible only using class "A"') +enddef + +def Test_class_variable() + var lines =<< trim END + vim9script + + class A + public static val: number = 10 + static def ClassFunc() + assert_equal(10, val) + enddef + def ObjFunc() + assert_equal(10, val) + enddef + endclass + + class B extends A + endclass + + assert_equal(10, A.val) + A.ClassFunc() + var a = A.new() + a.ObjFunc() + var b = B.new() + b.ObjFunc() + + def T1(a1: A) + a1.ObjFunc() + A.ClassFunc() + enddef + T1(b) + + A.val = 20 + assert_equal(20, A.val) + END + v9.CheckSourceSuccess(lines) + + # Modifying a parent class variable from a child class method + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + + class B extends A + static def ClassFunc() + val = 20 + enddef + endclass + B.ClassFunc() + END + v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') + + # Reading a parent class variable from a child class method + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + + class B extends A + static def ClassFunc() + var i = val + enddef + endclass + B.ClassFunc() + END + v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') + + # Modifying a parent class variable from a child object method + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + + class B extends A + def ObjFunc() + val = 20 + enddef + endclass + var b = B.new() + b.ObjFunc() + END + v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') + + # Reading a parent class variable from a child object method + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + + class B extends A + def ObjFunc() + var i = val + enddef + endclass + var b = B.new() + b.ObjFunc() + END + v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') + + # Modifying a class variable using an object at script level + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + var a = A.new() + a.val = 20 + END + v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"') + + # Reading a class variable using an object at script level + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + var a = A.new() + var i = a.val + END + v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"') + + # Modifying a class variable using an object at function level + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + + def T() + var a = A.new() + a.val = 20 + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') + + # Reading a class variable using an object at function level + lines =<< trim END + vim9script + + class A + static val: number = 10 + endclass + def T() + var a = A.new() + var i = a.val + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"') +enddef + +" Test for using a duplicate class method and class variable in a child class +def Test_dup_class_member() + # duplicate class variable, class method and overridden object method + var lines =<< trim END + vim9script + class A + static sval = 100 + static def Check() + assert_equal(100, sval) + enddef + def GetVal(): number + return sval + enddef + endclass + + class B extends A + static sval = 200 + static def Check() + assert_equal(200, sval) + enddef + def GetVal(): number + return sval + enddef + endclass + + def T1(aa: A): number + return aa.GetVal() + enddef + + def T2(bb: B): number + return bb.GetVal() + enddef + + assert_equal(100, A.sval) + assert_equal(200, B.sval) + var a = A.new() + assert_equal(100, a.GetVal()) + var b = B.new() + assert_equal(200, b.GetVal()) + assert_equal(200, T1(b)) + assert_equal(200, T2(b)) + END + v9.CheckSourceSuccess(lines) + + # duplicate class variable and class method + lines =<< trim END + vim9script + class A + static sval = 100 + static def Check() + assert_equal(100, sval) + enddef + def GetVal(): number + return sval + enddef + endclass + + class B extends A + static sval = 200 + static def Check() + assert_equal(200, sval) + enddef + endclass + + def T1(aa: A): number + return aa.GetVal() + enddef + + def T2(bb: B): number + return bb.GetVal() + enddef + + assert_equal(100, A.sval) + assert_equal(200, B.sval) + var a = A.new() + assert_equal(100, a.GetVal()) + var b = B.new() + assert_equal(100, b.GetVal()) + assert_equal(100, T1(b)) + assert_equal(100, T2(b)) + END + v9.CheckSourceSuccess(lines) +enddef + +" Test for calling an instance method using the class +def Test_instance_method_call_using_class() + # Invoke an object method using a class in script context + var lines =<< trim END + vim9script + class A + def Foo() + echo "foo" + enddef + endclass + A.Foo() + END + v9.CheckSourceFailure(lines, 'E1376: Object member "Foo" accessible only using class "A" object') + + # Invoke an object method using a class in def function context + lines =<< trim END + vim9script + class A + def Foo() + echo "foo" + enddef + endclass + def T() + A.Foo() + enddef + T() + END + v9.CheckSourceFailure(lines, 'E1376: Object member "Foo" accessible only using class "A" object') +enddef + +" Test for duplicate class method and instance method +def Test_dup_classmethod_objmethod() + # Duplicate instance method + var lines =<< trim END + vim9script + class A + static def Foo() + enddef + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo') + + # Duplicate private instance method + lines =<< trim END + vim9script + class A + static def Foo() + enddef + def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') + + # Duplicate class method + lines =<< trim END + vim9script + class A + def Foo() + enddef + static def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: Foo') + + # Duplicate private class method + lines =<< trim END + vim9script + class A + def Foo() + enddef + static def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') + + # Duplicate private class and object method + lines =<< trim END + vim9script + class A + def _Foo() + enddef + static def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1355: Duplicate function: _Foo') +enddef + +" Test for an instance method access level comparison with parent instance +" methods. +def Test_instance_method_access_level() + # Private method in subclass + var lines =<< trim END + vim9script + class A + def Foo() + enddef + endclass + class B extends A + endclass + class C extends B + def _Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1378: Access level of method "_Foo" is different in class "A"') + + # Public method in subclass + lines =<< trim END + vim9script + class A + def _Foo() + enddef + endclass + class B extends A + endclass + class C extends B + def Foo() + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1378: Access level of method "Foo" is different in class "A"') +enddef + +def Test_extend_empty_class() + var lines =<< trim END + vim9script + class A + endclass + class B extends A + endclass + class C extends B + public static rw_class_var = 1 + public this.rw_obj_var = 2 + static def ClassMethod(): number + return 3 + enddef + def ObjMethod(): number + return 4 + enddef + endclass + assert_equal(1, C.rw_class_var) + assert_equal(3, C.ClassMethod()) + var c = C.new() + assert_equal(2, c.rw_obj_var) + assert_equal(4, c.ObjMethod()) + END + v9.CheckSourceSuccess(lines) enddef " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/testdir/vim9.vim b/src/testdir/vim9.vim --- a/src/testdir/vim9.vim +++ b/src/testdir/vim9.vim @@ -110,6 +110,40 @@ export def CheckScriptSuccess(lines: lis endtry enddef +# :source a list of "lines" and check whether it fails with "error" +export def CheckSourceFailure(lines: list, error: string, lnum = -3) + new + setline(1, lines) + try + assert_fails('source', error, lines, lnum) + finally + bw! + endtry +enddef + +# :source a list of "lines" and check whether it fails with the list of +# "errors" +export def CheckSourceFailureList(lines: list, errors: list, lnum = -3) + new + setline(1, lines) + try + assert_fails('source', errors, lines, lnum) + finally + bw! + endtry +enddef + +# :source a list of "lines" and check whether it succeeds +export def CheckSourceSuccess(lines: list) + new + setline(1, lines) + try + :source + finally + bw! + endtry +enddef + export def CheckDefAndScriptSuccess(lines: list) CheckDefSuccess(lines) CheckScriptSuccess(['vim9script'] + lines) diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -700,6 +700,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1898, +/**/ 1897, /**/ 1896, diff --git a/src/vim9class.c b/src/vim9class.c --- a/src/vim9class.c +++ b/src/vim9class.c @@ -322,63 +322,109 @@ validate_extends_class(char_u *extends_n } /* - * Check whether a class/object member variable in "classmembers_gap" / - * "objmembers_gap" is a duplicate of a member in any of the extended parent - * class lineage. Returns TRUE if there are no duplicates. + * Check method names in the parent class lineage to make sure the access is + * the same for overridden methods. + */ + static int +validate_extends_methods( + garray_T *objmethods_gap, + class_T *extends_cl) +{ + class_T *super = extends_cl; + int method_count = objmethods_gap->ga_len; + ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data); + + while (super != NULL) + { + int extends_method_count = super->class_obj_method_count_child; + if (extends_method_count == 0) + { + super = super->class_extends; + continue; + } + + ufunc_T **extends_methods = super->class_obj_methods; + + for (int i = 0; i < extends_method_count; i++) + { + char_u *pstr = extends_methods[i]->uf_name; + int extends_private = (*pstr == '_'); + if (extends_private) + pstr++; + + for (int j = 0; j < method_count; j++) + { + char_u *qstr = cl_fp[j]->uf_name; + int priv_method = (*qstr == '_'); + if (priv_method) + qstr++; + if (STRCMP(pstr, qstr) == 0 && priv_method != extends_private) + { + // Method access is different between the super class and + // the subclass + semsg(_(e_method_str_of_class_str_has_different_access), + cl_fp[j]->uf_name, super->class_name); + return FALSE; + } + } + } + super = super->class_extends; + } + + return TRUE; +} + +/* + * Check whether a object member variable in "objmembers_gap" is a duplicate of + * a member in any of the extended parent class lineage. Returns TRUE if there + * are no duplicates. */ static int validate_extends_members( - garray_T *classmembers_gap, garray_T *objmembers_gap, class_T *extends_cl) { - for (int loop = 1; loop <= 2; ++loop) + // loop == 1: check class members + // loop == 2: check object members + int member_count = objmembers_gap->ga_len; + if (member_count == 0) + return TRUE; + + ocmember_T *members = (ocmember_T *)(objmembers_gap->ga_data); + + // Validate each member variable + for (int c_i = 0; c_i < member_count; c_i++) { - // loop == 1: check class members - // loop == 2: check object members - int member_count = loop == 1 ? classmembers_gap->ga_len - : objmembers_gap->ga_len; - if (member_count == 0) - continue; - ocmember_T *members = (ocmember_T *)(loop == 1 - ? classmembers_gap->ga_data - : objmembers_gap->ga_data); - - // Validate each member variable - for (int c_i = 0; c_i < member_count; c_i++) - { - class_T *p_cl = extends_cl; - ocmember_T *c_m = members + c_i; - char_u *pstr = (*c_m->ocm_name == '_') + class_T *p_cl = extends_cl; + ocmember_T *c_m = members + c_i; + char_u *pstr = (*c_m->ocm_name == '_') ? c_m->ocm_name + 1 : c_m->ocm_name; - // Check in all the parent classes in the lineage - while (p_cl != NULL) + // Check in all the parent classes in the lineage + while (p_cl != NULL) + { + int p_member_count = p_cl->class_obj_member_count; + if (p_member_count == 0) { - int p_member_count = loop == 1 - ? p_cl->class_class_member_count - : p_cl->class_obj_member_count; - if (p_member_count == 0) - continue; - ocmember_T *p_members = (loop == 1 - ? p_cl->class_class_members - : p_cl->class_obj_members); + p_cl = p_cl->class_extends; + continue; + } + ocmember_T *p_members = p_cl->class_obj_members; - // Compare against all the members in the parent class - for (int p_i = 0; p_i < p_member_count; p_i++) + // Compare against all the members in the parent class + for (int p_i = 0; p_i < p_member_count; p_i++) + { + ocmember_T *p_m = p_members + p_i; + char_u *qstr = (*p_m->ocm_name == '_') + ? p_m->ocm_name + 1 : p_m->ocm_name; + if (STRCMP(pstr, qstr) == 0) { - ocmember_T *p_m = p_members + p_i; - char_u *qstr = (*p_m->ocm_name == '_') - ? p_m->ocm_name + 1 : p_m->ocm_name; - if (STRCMP(pstr, qstr) == 0) - { - semsg(_(e_duplicate_member_str), c_m->ocm_name); - return FALSE; - } + semsg(_(e_duplicate_member_str), c_m->ocm_name); + return FALSE; } + } - p_cl = p_cl->class_extends; - } + p_cl = p_cl->class_extends; } } @@ -391,7 +437,7 @@ validate_extends_members( * implemented. */ static int -validate_extends_methods( +validate_abstract_class_methods( garray_T *classmethods_gap, garray_T *objmethods_gap, class_T *extends_cl) @@ -707,18 +753,26 @@ is_duplicate_member(garray_T *mgap, char * Returns TRUE if the method "name" is already defined. */ static int -is_duplicate_method(garray_T *fgap, char_u *name) +is_duplicate_method( + garray_T *classmethods_gap, + garray_T *objmethods_gap, + char_u *name) { char_u *pstr = (*name == '_') ? name + 1 : name; - for (int i = 0; i < fgap->ga_len; ++i) + // loop 1: class methods, loop 2: object methods + for (int loop = 1; loop <= 2; loop++) { - char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name; - char_u *qstr = *n == '_' ? n + 1 : n; - if (STRCMP(pstr, qstr) == 0) + garray_T *fgap = (loop == 1) ? classmethods_gap : objmethods_gap; + for (int i = 0; i < fgap->ga_len; ++i) { - semsg(_(e_duplicate_function_str), name); - return TRUE; + char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name; + char_u *qstr = *n == '_' ? n + 1 : n; + if (STRCMP(pstr, qstr) == 0) + { + semsg(_(e_duplicate_function_str), name); + return TRUE; + } } } @@ -882,7 +936,7 @@ add_lookup_tables(class_T *cl, class_T * return FAIL; } - // Update the lookup table for the extended class, if nay + // Update the lookup table for the extended class, if any if (extends_cl != NULL) { class_T *pclass = extends_cl; @@ -1017,9 +1071,10 @@ add_classfuncs_objmethods( int parent_count = 0; if (extends_cl != NULL) - // Include functions from the parent. + // Include object methods from the parent. + // Don't include the parent class methods. parent_count = loop == 1 - ? extends_cl->class_class_function_count + ? 0 : extends_cl->class_obj_method_count; *fcount = parent_count + gap->ga_len; @@ -1087,6 +1142,8 @@ add_classfuncs_objmethods( { ufunc_T *fp = (*fup)[i]; fp->uf_class = cl; + if (i < gap->ga_len) + fp->uf_defclass = cl; if (loop == 2) fp->uf_flags |= FC_OBJECT; } @@ -1443,16 +1500,16 @@ early_ret: break; } - garray_T *fgap = has_static || is_new - ? &classfunctions : &objmethods; // Check the name isn't used already. - if (is_duplicate_method(fgap, name)) + if (is_duplicate_method(&classfunctions, &objmethods, name)) { success = FALSE; func_clear_free(uf, FALSE); break; } + garray_T *fgap = has_static || is_new + ? &classfunctions : &objmethods; if (ga_grow(fgap, 1) == OK) { if (is_new) @@ -1517,19 +1574,23 @@ early_ret: success = validate_extends_class(extends, &extends_cl); VIM_CLEAR(extends); - // Check the new class members and object members are not duplicates of the - // members in the extended class lineage. + // Check the new object methods to make sure their access (public or + // private) is the same as that in the extended class lineage. if (success && extends_cl != NULL) - success = validate_extends_members(&classmembers, &objmembers, - extends_cl); + success = validate_extends_methods(&objmethods, extends_cl); + + // Check the new class and object variables are not duplicates of the + // variables in the extended class lineage. + if (success && extends_cl != NULL) + success = validate_extends_members(&objmembers, extends_cl); // When extending an abstract class, make sure all the abstract methods in // the parent class are implemented. If the current class is an abstract // class, then there is no need for this check. if (success && !is_abstract && extends_cl != NULL && (extends_cl->class_flags & CLASS_ABSTRACT)) - success = validate_extends_methods(&classfunctions, &objmethods, - extends_cl); + success = validate_abstract_class_methods(&classfunctions, + &objmethods, extends_cl); class_T **intf_classes = NULL; @@ -1572,12 +1633,10 @@ early_ret: extends_cl->class_flags |= CLASS_EXTENDED; } - // Add class and object members to "cl". + // Add class and object variables to "cl". if (add_members_to_class(&classmembers, - extends_cl == NULL ? NULL - : extends_cl->class_class_members, - extends_cl == NULL ? 0 - : extends_cl->class_class_member_count, + NULL, + 0, &cl->class_class_members, &cl->class_class_member_count) == FAIL || add_members_to_class(&objmembers, @@ -1756,7 +1815,21 @@ class_member_type( member_idx); if (m == NULL) { - semsg(_(e_unknown_variable_str), name); + char_u *varname = vim_strnsave(name, len); + if (varname != NULL) + { + if (is_object && class_member_idx(cl, name, len) >= 0) + // A class variable with this name is present + semsg(_(e_class_member_str_accessible_only_inside_class_str), + varname, cl->class_name); + else if (!is_object && object_member_idx(cl, name, len) >= 0) + // An instance variable with this name is present + semsg(_(e_object_member_str_accessible_only_using_object_str), + varname, cl->class_name); + else + semsg(_(e_unknown_variable_str), varname); + } + vim_free(varname); return &t_any; } @@ -1887,8 +1960,7 @@ class_object_index( fp = method_lookup(cl, rettv->v_type, name, len, NULL); if (fp == NULL) { - semsg(_(e_method_not_found_on_class_str_str), cl->class_name, - name); + method_not_found_msg(cl, rettv->v_type, name, len); return FAIL; } @@ -1951,7 +2023,7 @@ class_object_index( return OK; } - semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name); + member_not_found_msg(cl, VAR_OBJECT, name, len); } else if (rettv->v_type == VAR_CLASS) @@ -1962,7 +2034,7 @@ class_object_index( ocmember_T *m = class_member_lookup(cl, name, len, &m_idx); if (m == NULL) { - semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name); + member_not_found_msg(cl, VAR_CLASS, name, len); return FAIL; } @@ -2029,7 +2101,7 @@ fail_after_eval: } /* - * Returns the index of class member variable "name" in the class "cl". + * Returns the index of class variable "name" in the class "cl". * Returns -1, if the variable is not found. * If "namelen" is zero, then it is assumed that "name" is NUL terminated. */ @@ -2498,6 +2570,68 @@ object_free_nonref(int copyID) } /* + * Echo a class or object method not found message. + */ + void +method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len) +{ + char_u *method_name = vim_strnsave(name, len); + if ((v_type == VAR_OBJECT) + && (class_method_idx(cl, name, len) >= 0)) + { + // If this is a class method, then give a different error + if (*name == '_') + semsg(_(e_cannot_access_private_method_str), method_name); + else + semsg(_(e_class_member_str_accessible_only_using_class_str), + method_name, cl->class_name); + } + else if ((v_type == VAR_CLASS) + && (object_method_idx(cl, name, len) >= 0)) + { + // If this is an object method, then give a different error + if (*name == '_') + semsg(_(e_cannot_access_private_method_str), method_name); + else + semsg(_(e_object_member_str_accessible_only_using_object_str), + method_name, cl->class_name); + } + else + semsg(_(e_method_not_found_on_class_str_str), cl->class_name, + method_name); + vim_free(method_name); +} + +/* + * Echo a class or object member not found message. + */ + void +member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len) +{ + char_u *varname = len ? vim_strnsave(name, len) : vim_strsave(name); + + if (v_type == VAR_OBJECT) + { + if (class_member_idx(cl, name, len) >= 0) + semsg(_(e_class_member_str_accessible_only_using_class_str), + varname, cl->class_name); + else + semsg(_(e_member_not_found_on_object_str_str), cl->class_name, + varname); + } + else + { + if (object_member_idx(cl, name, len) >= 0) + semsg(_(e_object_member_str_accessible_only_using_object_str), + varname, cl->class_name); + else + semsg(_(e_class_member_str_not_found_in_class_str), + varname, cl->class_name); + } + vim_free(varname); +} + +/* * Return TRUE when the class "cl", its base class or one of the implemented * interfaces matches the class "other_cl". */ diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -332,24 +332,42 @@ script_var_exists(char_u *name, size_t l } /* - * If "name[len]" is a class method in cctx->ctx_ufunc->uf_class return the - * class method index. + * Returns the index of a class method or class variable with name "name" + * accessible in the currently compiled function. * If "cl_ret" is not NULL set it to the class. * Otherwise return -1. */ - int -cctx_class_method_idx( + static int +cctx_class_midx( cctx_T *cctx, + int is_method, char_u *name, size_t len, class_T **cl_ret) { if (cctx == NULL || cctx->ctx_ufunc == NULL - || cctx->ctx_ufunc->uf_class == NULL) + || cctx->ctx_ufunc->uf_class == NULL + || cctx->ctx_ufunc->uf_defclass == NULL) return -1; - class_T *cl = cctx->ctx_ufunc->uf_class; - int m_idx = class_method_idx(cl, name, len); + // Search for the class method or variable in the class where the calling + // function is defined. + class_T *cl = cctx->ctx_ufunc->uf_defclass; + int m_idx = is_method ? class_method_idx(cl, name, len) + : class_member_idx(cl, name, len); + if (m_idx < 0) + { + cl = cl->class_extends; + while (cl != NULL) + { + m_idx = is_method ? class_method_idx(cl, name, len) + : class_member_idx(cl, name, len); + if (m_idx >= 0) + break; + cl = cl->class_extends; + } + } + if (m_idx >= 0) { if (cl_ret != NULL) @@ -360,10 +378,24 @@ cctx_class_method_idx( } /* - * If "name[len]" is a class member in cctx->ctx_ufunc->uf_class return the - * class member variable index. - * If "cl_ret" is not NULL set it to the class. - * Otherwise return -1; + * Returns the index of a class method with name "name" accessible in the + * currently compiled function. Returns -1 if not found. The class where the + * method is defined is returned in "cl_ret". + */ + int +cctx_class_method_idx( + cctx_T *cctx, + char_u *name, + size_t len, + class_T **cl_ret) +{ + return cctx_class_midx(cctx, TRUE, name, len, cl_ret); +} + +/* + * Returns the index of a class variable with name "name" accessible in the + * currently compiled function. Returns -1 if not found. The class where the + * variable is defined is returned in "cl_ret". */ int cctx_class_member_idx( @@ -372,19 +404,7 @@ cctx_class_member_idx( size_t len, class_T **cl_ret) { - if (cctx == NULL || cctx->ctx_ufunc == NULL - || cctx->ctx_ufunc->uf_class == NULL) - return -1; - - class_T *cl = cctx->ctx_ufunc->uf_class; - int m_idx = class_member_idx(cl, name, len); - if (m_idx >= 0) - { - if (cl_ret != NULL) - *cl_ret = cl; - } - - return m_idx; + return cctx_class_midx(cctx, FALSE, name, len, cl_ret); } /* @@ -1639,6 +1659,8 @@ compile_lhs( } else { + class_T *defcl; + // No specific kind of variable recognized, just a name. if (check_reserved_name(lhs->lhs_name, lhs->lhs_has_index && *var_end == '.') == FAIL) @@ -1681,8 +1703,16 @@ compile_lhs( } } else if ((lhs->lhs_classmember_idx = cctx_class_member_idx( - cctx, var_start, lhs->lhs_varlen, NULL)) >= 0) + cctx, var_start, lhs->lhs_varlen, &defcl)) >= 0) { + if (cctx->ctx_ufunc->uf_defclass != defcl) + { + // A class variable can be accessed without the class name + // only inside a class. + semsg(_(e_class_member_str_accessible_only_inside_class_str), + lhs->lhs_name, defcl->class_name); + return FAIL; + } if (is_decl) { semsg(_(e_variable_already_declared_in_class_str), @@ -1918,10 +1948,10 @@ compile_lhs( int use_class = lhs->lhs_type != NULL && (lhs->lhs_type->tt_type == VAR_CLASS - || lhs->lhs_type->tt_type == VAR_OBJECT); + || lhs->lhs_type->tt_type == VAR_OBJECT); if (lhs->lhs_type == NULL || (use_class ? lhs->lhs_type->tt_class == NULL - : lhs->lhs_type->tt_member == NULL)) + : lhs->lhs_type->tt_member == NULL)) { lhs->lhs_member_type = &t_any; } @@ -1930,9 +1960,10 @@ compile_lhs( // for an object or class member get the type of the member class_T *cl = lhs->lhs_type->tt_class; ocmember_T *m; + int is_object = lhs->lhs_type->tt_type == VAR_OBJECT; lhs->lhs_member_type = class_member_type(cl, - lhs->lhs_type->tt_type == VAR_OBJECT, + is_object, after + 1, lhs->lhs_end, &lhs->lhs_member_idx, &m); if (lhs->lhs_member_idx < 0) @@ -1946,7 +1977,11 @@ compile_lhs( } // If it is private member variable, then accessing it outside the // class is not allowed. - if ((m->ocm_access != VIM_ACCESS_ALL) && !inside_class(cctx, cl)) + // If it is a read only class variable, then it can be modified + // only inside the class where it is defined. + if ((m->ocm_access != VIM_ACCESS_ALL) && + ((is_object && !inside_class(cctx, cl)) + || (!is_object && cctx->ctx_ufunc->uf_class != cl))) { char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE) ? e_cannot_access_private_member_str diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2172,8 +2172,7 @@ execute_storeindex(isn_T *iptr, ectx_T * { if (*member == '_') { - semsg(_(e_cannot_access_private_member_str), - m->ocm_name); + semsg(_(e_cannot_access_private_member_str), m->ocm_name); status = FAIL; } @@ -2181,8 +2180,7 @@ execute_storeindex(isn_T *iptr, ectx_T * } else { - semsg(_(e_member_not_found_on_object_str_str), - cl->class_name, member); + member_not_found_msg(cl, VAR_OBJECT, member, 0); status = FAIL; } } diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -367,12 +367,19 @@ compile_class_object_index(cctx_T *cctx, } if (ufunc == NULL) { - // TODO: different error for object method? - semsg(_(e_method_not_found_on_class_str_str), cl->class_name, name); + method_not_found_msg(cl, type->tt_type, name, len); return FAIL; } - if (*ufunc->uf_name == '_' && !inside_class_hierarchy(cctx, cl)) + // A private object method can be used only inside the class where it + // is defined or in one of the child classes. + // A private class method can be used only in the class where it is + // defined. + if (*ufunc->uf_name == '_' && + ((type->tt_type == VAR_OBJECT + && !inside_class_hierarchy(cctx, cl)) + || (type->tt_type == VAR_CLASS + && cctx->ctx_ufunc->uf_class != cl))) { semsg(_(e_cannot_access_private_method_str), name); return FAIL; @@ -422,7 +429,7 @@ compile_class_object_index(cctx_T *cctx, return generate_FUNCREF(cctx, fp, NULL, 0, NULL); } - semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name); + member_not_found_msg(cl, VAR_OBJECT, name, len); } else { @@ -438,7 +445,9 @@ compile_class_object_index(cctx_T *cctx, cl->class_name, m->ocm_name); return FAIL; } - if (*name == '_' && !inside_class(cctx, cl)) + // A private class variable can be accessed only in the class where + // it is defined. + if (*name == '_' && cctx->ctx_ufunc->uf_class != cl) { semsg(_(e_cannot_access_private_member_str), m->ocm_name); return FAIL; @@ -447,7 +456,7 @@ compile_class_object_index(cctx_T *cctx, *arg = name_end; return generate_CLASSMEMBER(cctx, TRUE, cl, idx); } - semsg(_(e_class_member_not_found_str), name); + member_not_found_msg(cl, VAR_CLASS, name, len); } return FAIL; @@ -762,9 +771,17 @@ compile_load( } else if ((idx = cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0) { - // Referencing a class member without the class name. Infer - // the class from the def function context. - res = generate_CLASSMEMBER(cctx, TRUE, cl, idx); + // Referencing a class variable without the class name. + // A class variable can be referenced without the class name + // only in the class where the function is defined. + if (cctx->ctx_ufunc->uf_defclass == cl) + res = generate_CLASSMEMBER(cctx, TRUE, cl, idx); + else + { + semsg(_(e_class_member_str_accessible_only_inside_class_str), + name, cl->class_name); + res = FAIL; + } } else { @@ -1130,12 +1147,23 @@ compile_call( } else if ((mi = cctx_class_method_idx(cctx, name, varlen, &cl)) >= 0) { - // Class method invocation without the class name. The - // generate_CALL() function expects the class type at the top of - // the stack. So push the class type to the stack. - push_type_stack(cctx, &t_class); - res = generate_CALL(cctx, cl->class_class_functions[mi], NULL, 0, - type, argcount); + // Class method invocation without the class name. + // A class method can be referenced without the class name only in + // the class where the function is defined. + if (cctx->ctx_ufunc->uf_defclass == cl) + { + // The generate_CALL() function expects the class type at the + // top of the stack. So push the class type to the stack. + push_type_stack(cctx, &t_class); + res = generate_CALL(cctx, cl->class_class_functions[mi], NULL, + 0, type, argcount); + } + else + { + semsg(_(e_class_member_str_accessible_only_inside_class_str), + name, cl->class_name); + res = FAIL; + } goto theend; } }