0
0
mirror of https://github.com/vim/vim.git synced 2025-09-25 03:54:15 -04:00

patch 9.0.1909: Vim9: problem calling class method from other class

Problem:  Vim9: problem calling class method from other class
Solution: Fix this problem, fix readonly object access, update error
          messages.

Calling a class method from another method without the class name prefix
doesn't work properly.

A readonly object variable is modifiable outside the class using a
nested object assignment.

Remove the unused E1338 error message.

Update error messages.

closes: #13116

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
Yegappan Lakshmanan
2023-09-18 19:56:49 +02:00
committed by Christian Brabandt
parent d25021cf03
commit 00cd18222e
11 changed files with 317 additions and 159 deletions

View File

@@ -4418,13 +4418,11 @@ E1325 vim9class.txt /*E1325*
E1326 vim9class.txt /*E1326* E1326 vim9class.txt /*E1326*
E1327 vim9class.txt /*E1327* E1327 vim9class.txt /*E1327*
E1328 vim9class.txt /*E1328* E1328 vim9class.txt /*E1328*
E1329 vim9class.txt /*E1329*
E133 userfunc.txt /*E133* E133 userfunc.txt /*E133*
E1330 vim9class.txt /*E1330* E1330 vim9class.txt /*E1330*
E1331 vim9class.txt /*E1331* E1331 vim9class.txt /*E1331*
E1332 vim9class.txt /*E1332* E1332 vim9class.txt /*E1332*
E1333 vim9class.txt /*E1333* E1333 vim9class.txt /*E1333*
E1334 vim9class.txt /*E1334*
E1335 vim9class.txt /*E1335* E1335 vim9class.txt /*E1335*
E1336 options.txt /*E1336* E1336 options.txt /*E1336*
E1337 vim9class.txt /*E1337* E1337 vim9class.txt /*E1337*
@@ -4458,9 +4456,27 @@ E1361 syntax.txt /*E1361*
E1362 vim9class.txt /*E1362* E1362 vim9class.txt /*E1362*
E1363 vim9class.txt /*E1363* E1363 vim9class.txt /*E1363*
E1364 recover.txt /*E1364* E1364 recover.txt /*E1364*
E1365 vim9class.txt /*E1365*
E1366 vim9class.txt /*E1366*
E1367 vim9class.txt /*E1367*
E1368 vim9class.txt /*E1368*
E1369 vim9class.txt /*E1369*
E137 starting.txt /*E137* E137 starting.txt /*E137*
E1370 vim9class.txt /*E1370* E1370 vim9class.txt /*E1370*
E1371 vim9class.txt /*E1371*
E1372 vim9class.txt /*E1372*
E1373 vim9class.txt /*E1373*
E1374 vim9class.txt /*E1374*
E1375 vim9class.txt /*E1375*
E1376 vim9class.txt /*E1376*
E1377 vim9class.txt /*E1377*
E1378 vim9class.txt /*E1378*
E1379 vim9class.txt /*E1379*
E138 starting.txt /*E138* E138 starting.txt /*E138*
E1380 vim9class.txt /*E1380*
E1381 vim9class.txt /*E1381*
E1382 vim9class.txt /*E1382*
E1383 vim9class.txt /*E1383*
E139 message.txt /*E139* E139 message.txt /*E139*
E140 message.txt /*E140* E140 message.txt /*E140*
E1400 builtin.txt /*E1400* E1400 builtin.txt /*E1400*
@@ -6367,7 +6383,7 @@ cino-{ indent.txt /*cino-{*
cino-} indent.txt /*cino-}* cino-} indent.txt /*cino-}*
cinoptions-values indent.txt /*cinoptions-values* cinoptions-values indent.txt /*cinoptions-values*
class vim9class.txt /*class* class vim9class.txt /*class*
class-function vim9class.txt /*class-function* class-method vim9class.txt /*class-method*
clear-undo undo.txt /*clear-undo* clear-undo undo.txt /*clear-undo*
clearmatches() builtin.txt /*clearmatches()* clearmatches() builtin.txt /*clearmatches()*
client-server remote.txt /*client-server* client-server remote.txt /*client-server*

View File

@@ -1,4 +1,4 @@
*vim9class.txt* For Vim version 9.0. Last change: 2023 Mar 22 *vim9class.txt* For Vim version 9.0. Last change: 2023 Sep 18
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -11,7 +11,7 @@ Vim9 classes, objects, interfaces, types and enums.
1. Overview |Vim9-class-overview| 1. Overview |Vim9-class-overview|
2. A simple class |Vim9-simple-class| 2. A simple class |Vim9-simple-class|
3. Class members and functions |Vim9-class-member| 3. Class variables and methods |Vim9-class-member|
4. Using an abstract class |Vim9-abstract-class| 4. Using an abstract class |Vim9-abstract-class|
5. Using an interface |Vim9-using-interface| 5. Using an interface |Vim9-using-interface|
6. More class details |Vim9-class| 6. More class details |Vim9-class|
@@ -139,11 +139,13 @@ changed at any time, you can make it public: >
Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting Now you don't need the SetLnum(), SetCol() and SetPosition() methods, setting
"pos.lnum" directly above will no longer give an error. "pos.lnum" directly above will no longer give an error.
*E1334* *E1326*
If you try to set an object member that doesn't exist you get an error: > If you try to set an object member that doesn't exist you get an error: >
pos.other = 9 pos.other = 9
< E1334: Object member not found: other ~ < E1326: Member not found on object "TextPosition": other ~
*E1376*
A object member cannot be accessed using the class name.
Private members ~ Private members ~
*E1332* *E1333* *E1332* *E1333*
@@ -176,9 +178,9 @@ number to the total number of lines: >
endif endif
return this._lnum return this._lnum
enddef enddef
<
Private methods ~ Private methods ~
*E1366*
If you want object methods to be accessible only from other methods of the If you want object methods to be accessible only from other methods of the
same class and not used from outside the class, then you can make them same class and not used from outside the class, then you can make them
private. This is done by prefixing the method name with an underscore: > private. This is done by prefixing the method name with an underscore: >
@@ -252,16 +254,17 @@ If the class extends a parent class, the same thing happens. In the second
step the members of the parent class are done first. There is no need to call step the members of the parent class are done first. There is no need to call
"super()" or "new()" on the parent. "super()" or "new()" on the parent.
*E1365*
When defining the new() method the return type should not be specified. It When defining the new() method the return type should not be specified. It
always returns an object of the class. always returns an object of the class.
============================================================================== ==============================================================================
3. class members and functions *Vim9-class-member* 3. Class Variables and Methods *Vim9-class-member*
*:static* *E1337* *E1338* *:static* *E1337* *E1338* *E1368*
Class members are declared with "static". They are used by the name without a Class members are declared with "static". They are used by the name without a
prefix: > prefix in the class where they are defined: >
class OtherThing class OtherThing
this.size: number this.size: number
@@ -275,6 +278,10 @@ prefix: >
Since the name is used as-is, shadowing the name by a function argument name Since the name is used as-is, shadowing the name by a function argument name
or local variable name is not allowed. or local variable name is not allowed.
*E1374* *E1375*
To access a class member outside of the class where it is defined, the class
name prefix must be used. A class member cannot be accessed using an object.
Just like object members the access can be made private by using an underscore Just like object members the access can be made private by using an underscore
as the first character in the name, and it can be made public by prefixing as the first character in the name, and it can be made public by prefixing
"public": > "public": >
@@ -285,10 +292,11 @@ as the first character in the name, and it can be made public by prefixing
public static result: number # anybody can read and write public static result: number # anybody can read and write
endclass endclass
< <
*class-function* *class-method*
Class functions are also declared with "static". They have no access to Class methods are also declared with "static". They can use the class
object members, they cannot use the "this" keyword. > variables but they have no access to the object variables, they cannot use the
"this" keyword.
>
class OtherThing class OtherThing
this.size: number this.size: number
static totalSize: number static totalSize: number
@@ -301,8 +309,9 @@ object members, they cannot use the "this" keyword. >
enddef enddef
endclass endclass
Inside the class the function can be called by name directly, outside the Inside the class the class method can be called by name directly, outside the
class the class name must be prefixed: `OtherThing.ClearTotalSize()`. class the class name must be prefixed: `OtherThing.ClearTotalSize()`. To use
a super class method in a child class, the class name must be prefixed.
Just like object methods the access can be made private by using an underscore Just like object methods the access can be made private by using an underscore
as the first character in the method name: > as the first character in the method name: >
@@ -312,7 +321,7 @@ as the first character in the method name: >
echo "Foo" echo "Foo"
enddef enddef
def Bar() def Bar()
OtherThing._Foo() _Foo()
enddef enddef
endclass endclass
< <
@@ -320,6 +329,31 @@ as the first character in the method name: >
Note that constructors cannot be declared as "static", because they always Note that constructors cannot be declared as "static", because they always
are. are.
To access the class methods and class variables of a super class in an
extended class, the class name prefix should be used just as from anywhere
outside of the defining class: >
vim9script
class Vehicle
static nextID: number = 1000
static def GetID(): number
nextID += 1
return nextID
enddef
endclass
class Car extends Vehicle
this.myID: number
def new()
this.myID = Vehicle.GetID()
enddef
endclass
<
Class variables and methods are not inherited by a child class. A child class
can declare a static variable or a method with the same name as the one in the
super class. Depending on the class where the member is used the
corresponding class member will be used. The type of the class member in a
child class can be different from that in the super class.
============================================================================== ==============================================================================
4. Using an abstract class *Vim9-abstract-class* 4. Using an abstract class *Vim9-abstract-class*
@@ -358,16 +392,19 @@ class, for which objects can be created. Example: >
An abstract class is defined the same way as a normal class, except that it An abstract class is defined the same way as a normal class, except that it
does not have any new() method. *E1359* does not have any new() method. *E1359*
*abstract-method* *abstract-method* *E1371* *E1372*
An abstract method can be defined in an abstract class by using the "abstract" An abstract method can be defined in an abstract class by using the "abstract"
prefix when defining the function: > prefix when defining the function: >
abstract class Shape abstract class Shape
abstract def Draw() abstract def Draw()
abstract static def SetColor()
endclass endclass
<
*E1373*
A class extending the abstract class must implement all the abstract methods. A class extending the abstract class must implement all the abstract methods.
Class methods in an abstract class can also be abstract methods. The signature (arguments, argument types and return type) must be exactly the
same. Class methods in an abstract class can also be abstract methods.
============================================================================== ==============================================================================
@@ -409,9 +446,10 @@ a number. This example extends the one above: >
return this.base * this.height / 2 return this.base * this.height / 2
enddef enddef
endclass endclass
<
*E1348* *E1349* *E1367* *E1382* *E1383*
If a class declares to implement an interface, all the items specified in the If a class declares to implement an interface, all the items specified in the
interface must appear in the class, with the same types. *E1348* *E1349* interface must appear in the class, with the same types.
The interface name can be used as a type: > The interface name can be used as a type: >
@@ -422,7 +460,14 @@ The interface name can be used as a type: >
for shape in shapes for shape in shapes
echo $'the surface is {shape.Surface()}' echo $'the surface is {shape.Surface()}'
endfor endfor
<
*E1378* *E1379* *E1380*
An interface can have only instance variables (read-only and read-write
access) and methods. An interface cannot contain private variables, private
methods, class variables and class methods.
An interface can extend another interface using "extends". The sub-interface
inherits all the instance variables and methods from the super interface.
============================================================================== ==============================================================================
@@ -464,9 +509,12 @@ once. They can appear in any order, although this order is recommended: >
extends ClassName extends ClassName
implements InterfaceName, OtherInterface implements InterfaceName, OtherInterface
specifies SomeInterface specifies SomeInterface
< *E1355* < *E1355* *E1369*
Each member and function name can be used only once. It is not possible to Each member and function name can be used only once. It is not possible to
define a function with the same name and different type of arguments. define a function with the same name and different type of arguments. It is
not possible to use a public and private member variable with the same name.
A object variable name used in a super class cannot be reused in a child
class.
Member Initialization ~ Member Initialization ~
@@ -491,6 +539,10 @@ Object methods of the base class can be overruled. The signature (arguments,
argument types and return type) must be exactly the same. The method of the argument types and return type) must be exactly the same. The method of the
base class can be called by prefixing "super.". base class can be called by prefixing "super.".
*E1377*
The access level of a method (public or private) in a child class should be
the same as the super class.
Other object methods of the base class are taken over by the child class. Other object methods of the base class are taken over by the child class.
Class functions, including functions starting with "new", can be overruled, Class functions, including functions starting with "new", can be overruled,
@@ -523,18 +575,26 @@ interface, which is often done in many languages, especially Java.
Items in a class ~ Items in a class ~
*E1318* *E1325* *E1326* *E1318* *E1325*
Inside a class, in between `:class` and `:endclass`, these items can appear: Inside a class, in between `:class` and `:endclass`, these items can appear:
- An object member declaration: > - An object member declaration: >
this._memberName: memberType this._privateMemberName: memberType
this.memberName: memberType this.readonlyMemberName: memberType
public this.memberName: memberType public this.readwriteMemberName: memberType
- A class member declaration: >
static this._privateMemberName: memberType
static this.readonlyMemberName: memberType
static public this.readwriteMemberName: memberType
- A constructor method: > - A constructor method: >
def new(arguments) def new(arguments)
def newName(arguments) def newName(arguments)
- A class method: >
static def SomeMethod(arguments)
static def _PrivateMethod(arguments)
- An object method: > - An object method: >
def SomeMethod(arguments) def SomeMethod(arguments)
< *E1329* def _PrivateMethod(arguments)
For the object member the type must be specified. The best way is to do this For the object member the type must be specified. The best way is to do this
explicitly with ": {type}". For simple types you can also use an initializer, explicitly with ": {type}". For simple types you can also use an initializer,
such as "= 123", and Vim will see that the type is a number. Avoid doing this such as "= 123", and Vim will see that the type is a number. Avoid doing this
@@ -573,6 +633,8 @@ An interface name must start with an uppercase letter. *E1343*
The "Has" prefix can be used to make it easier to guess this is an interface The "Has" prefix can be used to make it easier to guess this is an interface
name, with a hint about what it provides. name, with a hint about what it provides.
An interface can only be defined in a |Vim9| script file. *E1342* An interface can only be defined in a |Vim9| script file. *E1342*
An interface cannot "implement" another interface but it can "extend" another
interface. *E1381*
null object ~ null object ~

View File

@@ -3418,8 +3418,7 @@ EXTERN char e_internal_error_shortmess_too_long[]
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
EXTERN char e_class_member_str_not_found_in_class_str[] EXTERN char e_class_member_str_not_found_in_class_str[]
INIT(= N_("E1337: Class member \"%s\" not found in class \"%s\"")); INIT(= N_("E1337: Class member \"%s\" not found in class \"%s\""));
EXTERN char e_interface_static_direct_access_str[] // E1338 unused
INIT(= N_("E1338: Cannot directly access interface \"%s\" static member \"%s\""));
#endif #endif
#ifdef FEAT_PROP_POPUP #ifdef FEAT_PROP_POPUP
EXTERN char e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[] EXTERN char e_cannot_add_textprop_with_text_after_using_textprop_with_negative_id[]
@@ -3506,11 +3505,11 @@ EXTERN char e_object_member_str_accessible_only_using_object_str[]
INIT(= N_("E1376: Object member \"%s\" accessible only using class \"%s\" object")); INIT(= N_("E1376: Object member \"%s\" accessible only using class \"%s\" object"));
EXTERN char e_method_str_of_class_str_has_different_access[] EXTERN char e_method_str_of_class_str_has_different_access[]
INIT(= N_("E1377: Access level of method \"%s\" is different in class \"%s\"")); INIT(= N_("E1377: Access level of method \"%s\" is different in class \"%s\""));
EXTERN char e_static_cannot_be_used_in_interface[] EXTERN char e_static_member_not_supported_in_interface[]
INIT(= N_("E1378: Static cannot be used in an interface")); INIT(= N_("E1378: Static member not supported in an interface"));
EXTERN char e_private_variable_str_in_interface[] EXTERN char e_private_variable_not_supported_in_interface[]
INIT(= N_("E1379: Private variable not supported in an interface")); INIT(= N_("E1379: Private variable not supported in an interface"));
EXTERN char e_private_method_str_in_interface[] EXTERN char e_private_method_not_supported_in_interface[]
INIT(= N_("E1380: Private method not supported in an interface")); INIT(= N_("E1380: Private method not supported in an interface"));
EXTERN char e_interface_cannot_use_implements[] EXTERN char e_interface_cannot_use_implements[]
INIT(= N_("E1381: Interface cannot use \"implements\"")); INIT(= N_("E1381: Interface cannot use \"implements\""));

View File

@@ -1180,14 +1180,6 @@ get_lval(
return NULL; return NULL;
lp->ll_tv = &v->di_tv; lp->ll_tv = &v->di_tv;
} }
if (vim9script && writing && lp->ll_tv->v_type == VAR_CLASS
&& (lp->ll_tv->vval.v_class->class_flags & CLASS_INTERFACE) != 0)
{
if (!quiet)
semsg(_(e_interface_static_direct_access_str),
lp->ll_tv->vval.v_class->class_name, lp->ll_name);
return NULL;
}
if (vim9script && (flags & GLV_NO_DECL) == 0) if (vim9script && (flags & GLV_NO_DECL) == 0)
{ {

View File

@@ -1,7 +1,7 @@
/* vim9class.c */ /* vim9class.c */
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl, int is_static); int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl, int is_static);
void ex_class(exarg_T *eap); void ex_class(exarg_T *eap);
type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx, ocmember_T **m); type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u *name_end, int *member_idx);
void ex_enum(exarg_T *eap); void ex_enum(exarg_T *eap);
void ex_type(exarg_T *eap); void ex_type(exarg_T *eap);
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose); int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);

View File

@@ -4,12 +4,14 @@ source check.vim
import './vim9.vim' as v9 import './vim9.vim' as v9
def Test_class_basic() def Test_class_basic()
# Class supported only in "vim9script"
var lines =<< trim END var lines =<< trim END
class NotWorking class NotWorking
endclass endclass
END END
v9.CheckSourceFailure(lines, 'E1316:') v9.CheckSourceFailure(lines, 'E1316:')
# First character in a class name should be capitalized.
lines =<< trim END lines =<< trim END
vim9script vim9script
class notWorking class notWorking
@@ -17,6 +19,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1314:') v9.CheckSourceFailure(lines, 'E1314:')
# Only alphanumeric characters are supported in a class name
lines =<< trim END lines =<< trim END
vim9script vim9script
class Not@working class Not@working
@@ -24,6 +27,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1315:') v9.CheckSourceFailure(lines, 'E1315:')
# Unsupported keyword (instead of class)
lines =<< trim END lines =<< trim END
vim9script vim9script
abstract noclass Something abstract noclass Something
@@ -31,6 +35,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E475:') v9.CheckSourceFailure(lines, 'E475:')
# Only the completed word "class" should be recognized
lines =<< trim END lines =<< trim END
vim9script vim9script
abstract classy Something abstract classy Something
@@ -38,6 +43,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E475:') v9.CheckSourceFailure(lines, 'E475:')
# The complete "endclass" should be specified.
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -45,6 +51,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1065:') v9.CheckSourceFailure(lines, 'E1065:')
# Additional words after "endclass"
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -52,6 +59,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E488:') v9.CheckSourceFailure(lines, 'E488:')
# Additional commands after "endclass"
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -59,6 +67,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E488:') v9.CheckSourceFailure(lines, 'E488:')
# Use "this" without any member variable name
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -67,6 +76,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1317:') v9.CheckSourceFailure(lines, 'E1317:')
# Use "this." without any member variable name
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -75,6 +85,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1317:') v9.CheckSourceFailure(lines, 'E1317:')
# Space between "this" and ".<variable>"
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -83,6 +94,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1317:') v9.CheckSourceFailure(lines, 'E1317:')
# Space between "this." and the member variable name
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -91,6 +103,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1317:') v9.CheckSourceFailure(lines, 'E1317:')
# Use "that" instead of "this"
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -100,6 +113,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count') v9.CheckSourceFailure(lines, 'E1318: Not a valid command in a class: that.count')
# Member variable without a type or initialization
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -108,6 +122,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1022:') v9.CheckSourceFailure(lines, 'E1022:')
# Use a non-existing member variable in new()
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -117,8 +132,9 @@ def Test_class_basic()
endclass endclass
var obj = Something.new() var obj = Something.new()
END END
v9.CheckSourceFailure(lines, 'E1089:') v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Something": state')
# Space before ":" in a member variable declaration
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -127,6 +143,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1059:') v9.CheckSourceFailure(lines, 'E1059:')
# No space after ":" in a member variable declaration
lines =<< trim END lines =<< trim END
vim9script vim9script
class Something class Something
@@ -204,6 +221,8 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E1324: Using an object as a String') v9.CheckSourceFailure(lines, 'E1324: Using an object as a String')
# Test creating a class with member variables and methods, calling a object
# method. Check for using type() and typename() with a class and an object.
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -270,6 +289,7 @@ def Test_class_basic()
END END
v9.CheckSourceFailure(lines, 'E15:') v9.CheckSourceFailure(lines, 'E15:')
# Use a multi-line initialization for a member variable
lines =<< trim END lines =<< trim END
vim9script vim9script
class A class A
@@ -347,6 +367,7 @@ def Test_class_interface_wrong_end()
enddef enddef
def Test_object_not_set() def Test_object_not_set()
# Use an uninitialized object in script context
var lines =<< trim END var lines =<< trim END
vim9script vim9script
@@ -360,6 +381,7 @@ def Test_object_not_set()
END END
v9.CheckSourceFailure(lines, 'E1360:') v9.CheckSourceFailure(lines, 'E1360:')
# Use an uninitialized object from a def function
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -378,6 +400,8 @@ def Test_object_not_set()
END END
v9.CheckSourceFailure(lines, 'E1360:') v9.CheckSourceFailure(lines, 'E1360:')
# Pass an uninitialized object variable to a "new" function and try to call an
# object method.
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -418,6 +442,7 @@ def Test_object_not_set()
v9.CheckSourceFailure(lines, 'E1363:') v9.CheckSourceFailure(lines, 'E1363:')
enddef enddef
" Null object assignment and comparison
def Test_null_object_assign_compare() def Test_null_object_assign_compare()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
@@ -458,6 +483,7 @@ def Test_null_object_assign_compare()
v9.CheckSourceSuccess(lines) v9.CheckSourceSuccess(lines)
enddef enddef
" Test for object member initialization and disassembly
def Test_class_member_initializer() def Test_class_member_initializer()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
@@ -517,32 +543,6 @@ def Test_member_any_used_as_object()
END END
v9.CheckSourceSuccess(lines) v9.CheckSourceSuccess(lines)
lines =<< trim END
vim9script
class Inner
this.value: number = 0
endclass
class Outer
this.inner: Inner
endclass
def F(outer: Outer)
outer.inner.value = 1
enddef
def Test_assign_to_nested_typed_member()
var inner = Inner.new(0)
var outer = Outer.new(inner)
F(outer)
assert_equal(1, inner.value)
enddef
Test_assign_to_nested_typed_member()
END
v9.CheckSourceSuccess(lines)
# Try modifying a private variable using an "any" object # Try modifying a private variable using an "any" object
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -588,7 +588,37 @@ def Test_member_any_used_as_object()
v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Inner": someval') v9.CheckSourceFailure(lines, 'E1326: Member not found on object "Inner": someval')
enddef enddef
" Nested assignment to a object variable which is of another class type
def Test_assignment_nested_type()
var lines =<< trim END
vim9script
class Inner
public this.value: number = 0
endclass
class Outer
this.inner: Inner
endclass
def F(outer: Outer)
outer.inner.value = 1
enddef
def Test_assign_to_nested_typed_member()
var inner = Inner.new(0)
var outer = Outer.new(inner)
F(outer)
assert_equal(1, inner.value)
enddef
Test_assign_to_nested_typed_member()
END
v9.CheckSourceSuccess(lines)
enddef
def Test_assignment_with_operator() def Test_assignment_with_operator()
# Use "+=" to assign to a object variable
var lines =<< trim END var lines =<< trim END
vim9script vim9script
@@ -762,7 +792,6 @@ def Test_class_default_new()
v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'") v9.CheckSourceFailure(lines, "E1328: Constructor default value must be v:none: = 'a'")
enddef enddef
def Test_class_new_with_object_member() def Test_class_new_with_object_member()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
@@ -1677,7 +1706,7 @@ func Test_interface_garbagecollect()
call v9.CheckSourceSuccess(lines) call v9.CheckSourceSuccess(lines)
endfunc endfunc
def Test_class_function() def Test_class_method()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
class Value class Value
@@ -1713,6 +1742,29 @@ def Test_class_function()
endclass endclass
END END
v9.CheckSourceFailure(lines, 'E1318:') v9.CheckSourceFailure(lines, 'E1318:')
# Test for calling a class method from another class method without the class
# name prefix.
lines =<< trim END
vim9script
class A
static myList: list<number> = [1]
static def Foo(n: number)
myList->add(n)
enddef
static def Bar()
Foo(2)
enddef
def Baz()
Foo(3)
enddef
endclass
A.Bar()
var a = A.new()
a.Baz()
assert_equal([1, 2, 3], A.myList)
END
v9.CheckSourceSuccess(lines)
enddef enddef
def Test_class_defcompile() def Test_class_defcompile()
@@ -2343,7 +2395,6 @@ def Test_call_interface_method()
END END
v9.CheckSourceSuccess(lines) v9.CheckSourceSuccess(lines)
# No class that implements the interface. # No class that implements the interface.
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -2685,7 +2736,6 @@ def Test_using_base_class()
v9.CheckSourceSuccess(lines) v9.CheckSourceSuccess(lines)
enddef enddef
def Test_class_import() def Test_class_import()
var lines =<< trim END var lines =<< trim END
vim9script vim9script
@@ -3651,7 +3701,7 @@ def Test_private_class_method()
return 1234 return 1234
enddef enddef
def Bar() def Bar()
assert_equal(1234, A._Foo()) assert_equal(1234, _Foo())
enddef enddef
endclass endclass
var a = A.new() var a = A.new()
@@ -3659,7 +3709,8 @@ def Test_private_class_method()
END END
v9.CheckSourceSuccess(lines) v9.CheckSourceSuccess(lines)
# Use a class private method from another class private method # Use a class private method from another class private method without the
# class name prefix.
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -3668,10 +3719,10 @@ def Test_private_class_method()
return 1234 return 1234
enddef enddef
static def _Foo2() static def _Foo2()
assert_equal(1234, A._Foo1()) assert_equal(1234, _Foo1())
enddef enddef
def Bar() def Bar()
A._Foo2() _Foo2()
enddef enddef
endclass endclass
var a = A.new() var a = A.new()
@@ -4063,7 +4114,7 @@ def Test_private_member_access_outside_class()
enddef enddef
T() T()
END END
v9.CheckSourceFailure(lines, 'E1089: Unknown variable: _a') v9.CheckSourceFailure(lines, 'E1326: Member not found on object "A": _a')
# private static member variable # private static member variable
lines =<< trim END lines =<< trim END
@@ -4091,7 +4142,7 @@ def Test_private_member_access_outside_class()
enddef enddef
T() T()
END END
v9.CheckSourceFailure(lines, 'E1374: Class member "_val" accessible only inside class "A"') v9.CheckSourceFailure(lines, 'E1375: Class member "_val" accessible only using class "A"')
# private static class variable # private static class variable
lines =<< trim END lines =<< trim END
@@ -4265,7 +4316,7 @@ def Test_class_variable_access_using_object()
enddef enddef
T() T()
END END
v9.CheckSourceFailure(lines, 'E1374: Class member "svar2" accessible only inside class "A"') v9.CheckSourceFailure(lines, 'E1375: Class member "svar2" accessible only using class "A"')
enddef enddef
" Test for using a interface method using a child object " Test for using a interface method using a child object
@@ -4711,7 +4762,7 @@ def Test_class_variable()
enddef enddef
T() T()
END END
v9.CheckSourceFailure(lines, 'E1374: Class member "val" accessible only inside class "A"') v9.CheckSourceFailure(lines, 'E1375: Class member "val" accessible only using class "A"')
# Reading a class variable using an object at function level # Reading a class variable using an object at function level
lines =<< trim END lines =<< trim END
@@ -4977,7 +5028,7 @@ def Test_interface_with_unsupported_members()
static num: number static num: number
endinterface endinterface
END END
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -4985,7 +5036,7 @@ def Test_interface_with_unsupported_members()
static _num: number static _num: number
endinterface endinterface
END END
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -4993,7 +5044,7 @@ def Test_interface_with_unsupported_members()
public static num: number public static num: number
endinterface endinterface
END END
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -5001,7 +5052,7 @@ def Test_interface_with_unsupported_members()
public static _num: number public static _num: number
endinterface endinterface
END END
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -5009,7 +5060,7 @@ def Test_interface_with_unsupported_members()
static def Foo(d: dict<any>): list<string> static def Foo(d: dict<any>): list<string>
endinterface endinterface
END END
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -5017,7 +5068,7 @@ def Test_interface_with_unsupported_members()
static def _Foo(d: dict<any>): list<string> static def _Foo(d: dict<any>): list<string>
endinterface endinterface
END END
v9.CheckSourceFailure(lines, 'E1378: Static cannot be used in an interface') v9.CheckSourceFailure(lines, 'E1378: Static member not supported in an interface')
lines =<< trim END lines =<< trim END
vim9script vim9script
@@ -5402,4 +5453,35 @@ def Test_implements_using_var_type_any()
v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected list<dict<string>> but got dict<number>') v9.CheckSourceFailure(lines, 'E1382: Member "val": type mismatch, expected list<dict<string>> but got dict<number>')
enddef enddef
" Test for assigning to a member variable in a nested class
def Test_nested_object_assignment()
var lines =<< trim END
vim9script
class A
this.value: number
endclass
class B
this.a: A = A.new()
endclass
class C
this.b: B = B.new()
endclass
class D
this.c: C = C.new()
endclass
def T(da: D)
da.c.b.a.value = 10
enddef
var d = D.new()
T(d)
END
v9.CheckSourceFailure(lines, 'E46: Cannot change read-only variable "value"')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -699,6 +699,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1909,
/**/ /**/
1908, 1908,
/**/ /**/

View File

@@ -1592,7 +1592,7 @@ early_ret:
if (!is_class) if (!is_class)
{ {
emsg(_(e_static_cannot_be_used_in_interface)); emsg(_(e_static_member_not_supported_in_interface));
break; break;
} }
has_static = TRUE; has_static = TRUE;
@@ -1623,7 +1623,8 @@ early_ret:
if (!is_class && *varname == '_') if (!is_class && *varname == '_')
{ {
// private variables are not supported in an interface // private variables are not supported in an interface
semsg(_(e_private_variable_str_in_interface), varname); semsg(_(e_private_variable_not_supported_in_interface),
varname);
break; break;
} }
@@ -1689,7 +1690,8 @@ early_ret:
if (!is_class && *name == '_') if (!is_class && *name == '_')
{ {
// private variables are not supported in an interface // private variables are not supported in an interface
semsg(_(e_private_method_str_in_interface), name); semsg(_(e_private_method_not_supported_in_interface),
name);
func_clear_free(uf, FALSE); func_clear_free(uf, FALSE);
break; break;
} }
@@ -2010,8 +2012,7 @@ class_member_type(
int is_object, int is_object,
char_u *name, char_u *name,
char_u *name_end, char_u *name_end,
int *member_idx, int *member_idx)
ocmember_T **p_m)
{ {
size_t len = name_end - name; size_t len = name_end - name;
ocmember_T *m; ocmember_T *m;
@@ -2022,27 +2023,11 @@ class_member_type(
member_idx); member_idx);
if (m == NULL) if (m == NULL)
{ {
char_u *varname = vim_strnsave(name, len); member_not_found_msg(cl, is_object ? VAR_OBJECT : VAR_CLASS, name,
if (varname != NULL) len);
{
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; return &t_any;
} }
if (p_m != NULL)
*p_m = m;
return m->ocm_type; return m->ocm_type;
} }
@@ -2250,12 +2235,6 @@ class_object_index(
semsg(_(e_cannot_access_private_member_str), m->ocm_name); semsg(_(e_cannot_access_private_member_str), m->ocm_name);
return FAIL; return FAIL;
} }
if ((cl->class_flags & CLASS_INTERFACE) != 0)
{
semsg(_(e_interface_static_direct_access_str),
cl->class_name, m->ocm_name);
return FAIL;
}
typval_T *tv = &cl->class_members_tv[m_idx]; typval_T *tv = &cl->class_members_tv[m_idx];
copy_tv(tv, rettv); copy_tv(tv, rettv);

View File

@@ -1578,6 +1578,47 @@ is_decl_command(cmdidx_T cmdidx)
|| cmdidx == CMD_final || cmdidx == CMD_const; || cmdidx == CMD_final || cmdidx == CMD_const;
} }
/*
* Returns TRUE if the class or object variable in "lhs" is modifiable.
* "var_start" points to the start of the variable name and "lhs->lhs_varlen"
* has the total length. Note that the "lhs" can be nested an object reference
* (e.g. a.b.c.d.var).
*/
static int
lhs_class_member_modifiable(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
{
size_t varlen = lhs->lhs_varlen;
class_T *cl = lhs->lhs_type->tt_class;
int is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
char_u *name = var_start + varlen + 1;
size_t namelen = lhs->lhs_end - var_start - varlen - 1;
ocmember_T *m;
m = member_lookup(cl, lhs->lhs_type->tt_type, name, namelen, NULL);
if (m == NULL)
{
member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen);
return FALSE;
}
// If it is private member variable, then accessing it outside the
// class is not allowed.
// 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
: e_cannot_change_readonly_variable_str;
semsg(_(msg), m->ocm_name);
return FALSE;
}
return TRUE;
}
/* /*
* Figure out the LHS type and other properties for an assignment or one item * Figure out the LHS type and other properties for an assignment or one item
* of ":unlet" with an index. * of ":unlet" with an index.
@@ -1691,9 +1732,9 @@ compile_lhs(
{ {
if (is_decl) if (is_decl)
{ {
// if we come here with what looks like an assignment like .= // if we come here with what looks like an assignment like
// but which has been reject by assignment_len() from may_compile_assignment // .= but which has been reject by assignment_len() from
// give a better error message // may_compile_assignment give a better error message
char_u *p = skipwhite(lhs->lhs_end); char_u *p = skipwhite(lhs->lhs_end);
if (p[0] == '.' && p[1] == '=') if (p[0] == '.' && p[1] == '=')
emsg(_(e_dot_equal_not_supported_with_script_version_two)); emsg(_(e_dot_equal_not_supported_with_script_version_two));
@@ -1959,36 +2000,17 @@ compile_lhs(
{ {
// for an object or class member get the type of the member // for an object or class member get the type of the member
class_T *cl = lhs->lhs_type->tt_class; class_T *cl = lhs->lhs_type->tt_class;
ocmember_T *m;
int is_object = lhs->lhs_type->tt_type == VAR_OBJECT; int is_object = lhs->lhs_type->tt_type == VAR_OBJECT;
if (!lhs_class_member_modifiable(lhs, var_start, cctx))
return FAIL;
lhs->lhs_member_type = class_member_type(cl, lhs->lhs_member_type = class_member_type(cl,
is_object, is_object,
after + 1, lhs->lhs_end, after + 1, lhs->lhs_end,
&lhs->lhs_member_idx, &m); &lhs->lhs_member_idx);
if (lhs->lhs_member_idx < 0) if (lhs->lhs_member_idx < 0)
return FAIL; return FAIL;
if ((cl->class_flags & CLASS_INTERFACE) != 0
&& lhs->lhs_type->tt_type == VAR_CLASS)
{
semsg(_(e_interface_static_direct_access_str),
cl->class_name, m->ocm_name);
return FAIL;
}
// If it is private member variable, then accessing it outside the
// class is not allowed.
// 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
: e_cannot_change_readonly_variable_str;
semsg(_(msg), m->ocm_name);
return FAIL;
}
} }
else else
{ {
@@ -2163,6 +2185,14 @@ compile_load_lhs(
lhs->lhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void lhs->lhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
: get_type_on_stack(cctx, 0); : get_type_on_stack(cctx, 0);
if (lhs->lhs_type->tt_type == VAR_OBJECT)
{
// Check whether the object variable is modifiable
if (!lhs_class_member_modifiable(lhs, var_start, cctx))
return FAIL;
}
// Now we can properly check the type. The variable is indexed, thus // Now we can properly check the type. The variable is indexed, thus
// we need the member type. For a class or object we don't know the // we need the member type. For a class or object we don't know the
// type yet, it depends on what member is used. // type yet, it depends on what member is used.
@@ -2198,8 +2228,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
class_T *cl = lhs->lhs_type->tt_class; class_T *cl = lhs->lhs_type->tt_class;
type_T *type = class_member_type(cl, TRUE, dot + 1, type_T *type = class_member_type(cl, TRUE, dot + 1,
lhs->lhs_end, &lhs->lhs_member_idx, lhs->lhs_end, &lhs->lhs_member_idx);
NULL);
if (lhs->lhs_member_idx < 0) if (lhs->lhs_member_idx < 0)
return FAIL; return FAIL;

View File

@@ -439,12 +439,6 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
if (m != NULL) if (m != NULL)
{ {
// Note: type->tt_type = VAR_CLASS // Note: type->tt_type = VAR_CLASS
if ((cl->class_flags & CLASS_INTERFACE) != 0)
{
semsg(_(e_interface_static_direct_access_str),
cl->class_name, m->ocm_name);
return FAIL;
}
// A private class variable can be accessed only in the class where // A private class variable can be accessed only in the class where
// it is defined. // it is defined.
if (*name == '_' && cctx->ctx_ufunc->uf_class != cl) if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
@@ -1152,9 +1146,6 @@ compile_call(
// the class where the function is defined. // the class where the function is defined.
if (cctx->ctx_ufunc->uf_defclass == cl) 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, res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
0, type, argcount); 0, type, argcount);
} }

View File

@@ -1899,9 +1899,15 @@ generate_CALL(
// drop the argument types // drop the argument types
cctx->ctx_type_stack.ga_len -= argcount; cctx->ctx_type_stack.ga_len -= argcount;
// For an object or class method call, drop the object/class type // For an object or class method call, drop the object/class type.
if (ufunc->uf_class != NULL) if (ufunc->uf_class != NULL)
{
// When a class method is called without the class name prefix, then
// the type will not be in the stack.
type_T *stype = get_type_on_stack(cctx, 0);
if (stype->tt_type == VAR_CLASS || stype->tt_type == VAR_OBJECT)
cctx->ctx_type_stack.ga_len--; cctx->ctx_type_stack.ga_len--;
}
// add return type // add return type
return push_type_stack(cctx, ufunc->uf_ret_type); return push_type_stack(cctx, ufunc->uf_ret_type);